Talos Linux on OpenStack: step by step

22 August, 2024

Daniel Bodky
Daniel Bodky
Senior Platform Advocate

Daniel kam nach Abschluss seines Studiums im Oktober 2021 zu NETWAYS und beriet zwei Jahre lang Kunden zu den Themen Icinga2 und Kubernetes, bevor es ihn weiter zu Managed Services zog. Seitdem redet und schreibt er viel über cloud-native Technologien und ihre spannenden Anwendungsfälle und gibt sein Bestes, um Neues und Interessantes rund um Kubernetes zu vermitteln. Nebenher schreibt er in seiner Freizeit kleinere Tools für verschiedenste Einsatzgebiete, nimmt öfters mal ein Buch in die Hand oder widmet sich seinem viel zu großen Berg Lego. In der wärmeren Jahreszeit findet man ihn außerdem oft auf dem Fahrrad oder beim Wandern.

by | Aug 22, 2024

Choosing the right platform for running Kubernetes workloads is an important strategic decision. Managed Kubernetes services offer a quick and easy way to deploy and manage Kubernetes clusters, but they often come with limitations in terms of flexibility and control.
For those looking for more customization options or greater integration with their existing OpenStack infrastructure, installing Talos Linux on OpenStack is an interesting alternative.

In this blog post, we will show you how to install Talos Linux on OpenStack using Terraform and thus create the basis for a secure, lightweight Kubernetes cluster.

Build your cluster today!
The NETWAYS Cloud and NETWAYS Managed Kubernetes leave all options open to you.

Why Talos Linux?

alos is an extremely lightweight Linux distribution that was explicitly designed for the operation of Kubernetes. Consequently, it differs fundamentally from other distributions such as Ubuntu, Fedora, or RHEL.
Some of the most important differences between Talos and other standard distributions are

  • the minimalist design: Talos Linux only needs 12 binaries! There is no shell, no interpreters for programming languages such as Python etc., and not even a package manager.
  • the communication model: communication, installation and configuration of Talos takes place exclusively via API – no more SSH vulnerabilities!
  • the file system: Talos Linux relies on an unchangeable root file system, on which only individual files can be changed as overlayfs (e.g. /etc/host).
  • the focus on Kubernetes: Talos Linux was designed specifically for Kubernetes. Creation, troubleshooting and upgrades of Kubernetes clusters can be carried out directly via Talos API in a secure manner.

Now that we’ve given you a brief overview of its benefits, let’s tackle the installation of Talos Linux on OpenStack!

Our installation plan

As mentioned at the beginning, we will use Terraform for the installation of Talos Linux on OpenStack.

Infrastructure as Code(IaC) offers a number of advantages over manual installation, including reproducibility, speed and security. Typical errors of manual operation (e.g. typing errors) can be ruled out from the outset.

Info: Terraform changed its licensing model in 2023. In direct response, the cloud-native community created a fork of the project called OpenTofu.

You can use both tools for this tutorial – simply replace terraform with tofu if necessary.

We will divide the installation process into three steps:

  1. Initialization of the Terraform project: Before we can work with Terraform on OpenStack, we need to configure it accordingly.
  2. Preparation of the OpenStack project: We need a network, firewall rules, and a Talos Linux image from which we can boot.
  3. Setting up the Talos Linux VMs: For this article we will create a cluster consisting of one controlplane node and two worker nodes.
  4. Creating a Kubernetes cluster: After the successful installation of Talos Linux on OpenStack, we will look at how quickly and easily we can set up a Kubernetes cluster on it.

So let’s get straight to it!

Initialization of the Terraform project

For the installation of Terraform or OpenTofu please follow the links. After successful installation of the CLI, we create a new directory talos-openstack in which we will work. In this directory we create a new file main.tf with the following content:

terraform {
    required_providers {
        openstack = {
            source  = "terraform-provider-openstack/openstack"
            version = "~> 2.1.0"
        }
    }
}

provider "openstack" {
    auth_url    = "https://cloud.netways.de:5000/v3/"
    tenant_name = ""
    user_name   = ""
    password    = ""
    region      = "HetznerNBG4"
}

With this information we tell Terraform that we need the official OpenStack provider for Terraform and how it should connect to our OpenStack project.
The above configuration is intended for NETWAYS Cloud, depending on the OpenStack used, the information must be adapted accordingly.

Using the following command, we can now initialize our Terraform project and download the OpenStack provider:

terraform init

Afterwards we can start with the installation of Talos Linux on OpenStack!

Configuration of the OpenStack project

To set up a Kubernetes cluster on Talos Linux nodes, we need a few things in our OpenStack project, all of which we can provision with Terraform:

  • a network + subnet: To better secure our cluster and the underlying VMs, it makes sense to use a dedicated network.
  • a network router: To be able to manage our cluster and the Talos Linux VMs via the Internet, we need a router to connect our network to a so-called public network.
  • a security group + rules: To define firewall rules, we need a so-called security group and corresponding rules.
  • a Talos Linux boot image: To install our VMs, we need an image from which the VMs can initially boot Talos Linux.

Definition of network resources

To better organize our resources managed by Terraform, we will create a new file network.tf, in which we will define all network-related resources.
We will start with the network, subnet and network router:

resource "openstack_networking_network_v2" "talos" {
    name           = "talos-network"
    admin_state_up = true
}

resource "openstack_networking_subnet_v2" "talos-subnet-1" {
    name       = "talos-subnet-1"
    network_id = openstack_networking_network_v2.talos.id
    cidr       = "192.168.1.0/24"
}

resource "openstack_networking_router_v2" "talos-router" {
    name                = "talos-router"
    admin_state_up      = true
    external_network_id = data.openstack_networking_network_v2.public-network.id
}

resource "openstack_networking_router_interface_v2" "talos-router-interface" {
    router_id = openstack_networking_router_v2.talos-router.id
    subnet_id = openstack_networking_subnet_v2.talos-subnet-1.id
}

We need one resource definition each for the network and subnet, whereby we assign a CIDR to the subnet and reference the defined network.
The router consists of two components: the router itself (the link between the router and the public network) and its interface (the link between the router and our subnet).

The attentive observer will have noticed that we reference the public network in the router definition via data.openstack_networking_network_v2.public-network.id – we must also define this so-called data variable.
To do this, we create another file data.tf, which initially contains a single entry:

data "openstack_networking_network_v2" "public-network" {
    name = "public-network"
}

As with the configuration of Terraform itself, the definition here is also tailored to the NETWAYS Cloud – depending on the OpenStack environment, it is quite possible that any existing public networks may have a different name.

Definition of the firewall

Next, we can create the required security group and associated security rules . Before that, however, we should take a look at which network connections Talos Linux uses for communication within the cluster and via the CLI.

The official documentation helps us here:

We therefore need security rules for ports 50000/50001 within the subnet and for port 50000 from remote (e.g. from our local Talos CLI).
We will also create a rule for port 6443 in order to be able to communicate with the Kubernetes API .

To this end, we are expanding network.tf to include the following resource definitions:

resource "openstack_networking_secgroup_v2" "talos-controlplane" {
    name        = "talos"
    description = "A Security Group for Talos Linux"
}

resource "openstack_networking_secgroup_v2" "talos-workers" {
    name        = "talos-workers"
    description = "A Security Group for Talos Linux workers"
}

resource "openstack_networking_secgroup_rule_v2" "k8s-api" {
    direction = "ingress"
    ethertype = "IPv4"
    protocol  = "tcp"
    port_range_min = 6443
    port_range_max = 6443
    remote_ip_prefix = "0.0.0.0/0"
    security_group_id = openstack_networking_secgroup_v2.talos-controlplane.id
}

resource "openstack_networking_secgroup_rule_v2" "talos-apid-controlplane" {
    direction = "ingress"
    ethertype = "IPv4"
    protocol  = "tcp"
    port_range_min = 50000
    port_range_max = 50000
    remote_ip_prefix = "0.0.0.0/0"
    security_group_id = openstack_networking_secgroup_v2.talos-controlplane.id
}

resource "openstack_networking_secgroup_rule_v2" "talos-trustd-controlplane" {
    direction = "ingress"
    ethertype = "IPv4"
    protocol = "tcp"
    port_range_min = 50001
    port_range_max = 50001
    remote_ip_prefix = openstack_networking_subnet_v2.talos-subnet-1.cidr
    security_group_id = openstack_networking_secgroup_v2.talos-controlplane.id
}

resource "openstack_networking_secgroup_rule_v2" "talos-apid-worker" {
    direction = "ingress"
    ethertype = "IPv4"
    protocol  = "tcp"
    port_range_min = 50000
    port_range_max = 50000
    remote_ip_prefix = openstack_networking_subnet_v2.talos-subnet-1.cidr
    security_group_id = openstack_networking_secgroup_v2.talos-workers.id
}

In this way, we create two security groups and a handful of rules to map the connections to be released.

Definition of the Talos Linux boot image

The last ToDo of this first section is to define the Talos Linux boot image. We use the Talos Image Factory to create it and upload it easily.

As Talos Linux is immutable, required extensions cannot simply be installed later. The Talos Image Factory therefore makes it possible to create and download various preconfigured boot images.

For our OpenStack image, we can click through the dialog on the website as follows:

  1. Hardware Type: Cloud Server
  2. Choose Talos Linux Version: v1.7.5
  3. Cloud: OpenStack
  4. Machine Architecture: amd64
  5. System extensions: none
  6. Customization: none

We now land on a download page where we copy the link under First Boot > Disk Image .

We then insert it into a new file compute.tf as follows:

resource "openstack_images_image_v2" "talos-175" {
    name             = "Talos v1.7.5"
    image_source_url = "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.7.5/openstack-amd64.raw.xz"
    container_format = "bare"
    disk_format      = "raw"
    decompress       = "true"
}

When Terraform is executed, the referenced image is first downloaded to our local computer, decompressed and then configured as a boot image in OpenStack.

We can now configure the Talos Linux VMs.

Setting up the Talos Linux VMs

The creation of VMs also requires the definition of several resources in Terraform:

  • Ports: For connecting the VMs to the already defined subnet and for linking the security group(s)
  • Floating IP: For availability of the control plane node via the Internet
  • Compute Instances: the actual VMs, including resource and image definition.

Creation of network ports and floating IP

Before we finally define our Talos Linux VMs, the only thing missing is the network ports with which we connect the VMs to the defined subnet and the respective security groups . To do this, we add the following resources to compute.tf:

resource "openstack_networking_port_v2" "talos-controlplane" {
    depends_on         = [openstack_networking_subnet_v2.talos-subnet-1]
    name               = "talos-controlplane"
    network_id         = openstack_networking_network_v2.talos.id
    admin_state_up     = true
    security_group_ids = [openstack_networking_secgroup_v2.talos-controlplane.id]
}

resource "openstack_networking_port_v2" "talos-workers" {
    depends_on         = [openstack_networking_subnet_v2.talos-subnet-1]
    count              = 2
    name               = "talos-worker-${count.index}"
    network_id         = openstack_networking_network_v2.talos.id
    admin_state_up     = true
    security_group_ids = [openstack_networking_secgroup_v2.talos-workers.id]
}

We now have two port definitions, one for the controlplane node to be created and one for the worker nodes, which will create two ports in OpenStack, one for each worker node, thanks to the count = 2 property.

We also define the floating IP in compute.tf and reference the port we have just defined for the control plane node and one of the public networks in our OpenStack environment:

resource "openstack_networking_floatingip_v2" "talos-controlplane" {
    pool    = "public-network"
    port_id = openstack_networking_port_v2.talos-controlplane.id
}

As the specific floating IP is only known after the resources have been provisioned by Terraform, we have it output after successful creation. To do this, we create the file outputs.tf with the following content:

output "control-plane-ip" {
    value = openstack_networking_floatingip_v2.talos-controlplane.address
}

Half-time: First provisioning with Terraform

At this point, we will now have Terraform provision the previously defined resources for the first time.
The reason for this is that we need to know the floating IP of our control plane node for the next steps.

We therefore execute the following command, confirm the X outstanding changes with yes, and wait for the successful completion of the provisioning:

terraform apply

Our current configuration results in 15 resources to be provisioned in OpenStack.

After successful provisioning, we also see our floating IP here for the first time – although it is not yet connected to any workload.

Thanks to the output defined by us, we receive the provisioned floating IP immediately after creation.

Creation of the Talos Linux VMs

After we have successfully provisioned (among other things) a floating IP for our project, we can proceed with the installation of Talos Linux on OpenStack.

Creation of the Talos configurations

If you have not already done so, now is the perfect time to install the Talos CLI. Please follow the link for instructions.

Once installed, we generate the required configuration files locally with the CLI.
First we generate the required secrets (CA keys, tokens, etc.).
Then we create the configuration for the control plane, worker and the local CLI: We pass a cluster name as well as the Kubernetes endpoint (https://:6443) and the previously created secrets – for this we need the previously created floating IP:

talosctl gen secrets
talosctl gen config talos-on-openstack https://:6443 --with-secrets secrets.yaml

This gives us four files:

  • secrets.conf with the secrets required for the configuration of Talos Linux and Kubernetes
  • talosconfig with the information required for our local CLI
  • controlplane.yaml with the configuration for our control plane node(s)
  • worker.yaml with the configuration for our worker nodes

Creation of the compute instances

To create the compute instances, we need a flavor in addition to the already defined boot image , which determines the number of CPUs, memory and hard disk space of the VMs, among other things.

For this purpose, we extend data.tf to obtain information about an existing flavor in our Terraform project. Due to the resource-saving design of Talos Linux, the flavor s1.small is sufficient for our purposes:

data "openstack_compute_flavor_v2" "s1-small" {
    name = "s1.small"
}

Depending on the OpenStack environment, adjustments must be made here again to query an existing flavor.

With these resources defined, all that remains is to create the actual VMs! We also define these in compute.tf and reference our newly generated configuration files as user_data – this way, Talos takes them directly into account when creating the VMs:

resource "openstack_compute_instance_v2" "talos-controlplane" {
    name      = "talos-controlplane"
    image_id  = openstack_images_image_v2.talos-175.id
    flavor_id = data.openstack_compute_flavor_v2.s1-small.id
    user_data = file("controlplane.yaml")

    network {
        port = openstack_networking_port_v2.talos-controlplane.id
    }
}

resource "openstack_compute_instance_v2" "talos-workers" {
    count     = 2
    name      = "talos-worker-${count.index}"
    image_id  = openstack_images_image_v2.talos-175.id
    flavor_id = data.openstack_compute_flavor_v2.s1-small.id
    user_data = file("worker.yaml")
    network {
        port = openstack_networking_port_v2.talos-workers[count.index].id
    }
}

As with the port definition, we also have two resources here, one for the control plane node and one for the worker nodes. We also reference the defined boot image, the flavor and the respective port.

The subnet-internal IP of the worker VMs may also be relevant for us, e.g. to make changes to the worker nodes via Talos CLI and our accessible control plane node.

We therefore also add a corresponding entry for the worker nodes in outputs.tf:

output "worker-ip" {
    value = openstack_compute_instance_v2.talos-workers[*].access_ip_v4
}

Provisioning of Talos Linux VMs with Terraform

We are ready – all required resources are defined in *.tf files and we can move on to the (almost) final step: The actual provisioning of Talos Linux on OpenStack!

To do this, we use the following command again:

terraform apply

Terraform will compare the state of our OpenStack project with the locally defined, desired state and then show us a summary of the outstanding changes (our three compute instances).

Terraform knows the live status of the provisioned infrastructure and will only create the resources that are still missing.

If we are satisfied with these changes, we confirm again with yes and wait until Terraform has carried out the provisioning. At the end, in addition to the already known floating IP, we should enter the two internal IPs of our worker nodes received as output:

Our three VMs were also created and the IP addresses issued by Terraform.

The installation of Talos Linux on OpenStack was successful! So let’s move on to the last step of our installation plan: Setting up a Kubernetes cluster.

Bootstrapping from Kubernetes to Talos Linux

The creation of a Kubernetes cluster on Talos is done with the following command against our controlplane node. We can save the resulting kubeconfig directly afterwards:

talosctl --talosconfig talosconfig bootstrap \
  --endpoints  \
  --nodes 

talosctl --talosconfig talosconfig kubeconfig \
  --endpoints  \
  --nodes

Now we wait a moment until the cluster has been successfully created:

talosctl --talosconfig talosconfig health \
  --endpoints  \
  --nodes

Once the cluster has been started, we can display its status. We just need to make sure that we are using the correct Kubernetes context :

kubectl get nodes
NAME                 STATUS   ROLES           AGE     VERSION
talos-controlplane   Ready    control-plane   2m16s   v1.30.3
talos-worker-0       Ready                    2m17s   v1.30.3
talos-worker-1       Ready                    2m17s   v1.30.3

Talos Linux on OpenStack + Terraform = ❤️

With the successful installation of Talos Linux on OpenStack and the setup of your Kubernetes cluster, you now have a powerful and flexible environment that is specifically optimized for running container workloads.

The combination of OpenStack, Talos and Terraform gives you full control over your environment, which you can build “from scratch”, replicate or customize at any time. This approach gives you the freedom to design your Kubernetes clusters exactly according to your requirements, should managed services ever be too inflexible.
Either way, you are now ideally equipped to run your cloud-native applications on a stable, secure and scalable platform.

To stay on the ball, we would like to take this opportunity to recommend that you subscribe to our newsletter. This way you will regularly receive new, up-to-date and interesting insights into cloud-native practices, tutorials and news.

Our portfolio

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

How did you like our article?