Talos cluster template

The talos cluster template will create a cluster using Talos Control Plane provider.

A container running a haproxy server is used for the cluster load balancer endpoint. The load balancer endpoint will be the IP address of the haproxy container.

WARNING: The load balancer container is a single point of failure for the control plane of the workload cluster, therefore should only be used for development or evaluation purposes. For production grade clusters, you should instead use a different option for the load balancer, e.g. OVN

WARNING: This cluster template is not currently tested in CI, therefore could be broken. Please raise a GitHub issue if that is the case.

Table Of Contents

Requirements

1. CAPN version

Talos cluster templates require CAPN version v0.8.5 or newer.

2. Install Talos control plane and bootstrap providers on the cluster

clusterctl init -c talos -b talos

Tested Versions:

3. Import Talos nocloud server image into Incus

Start from https://factory.talos.dev/

  • Select Cloud Server
  • Select Talos Linux Version (v1.12.2)
  • Select nocloud variant
  • Select Machine architecture (amd64)
  • Select Secure Boot (true or false)
  • Select optional System Extensions (none required)
  • Select Bootloader (auto)
  • Select BIOS Only boot (NOTE: UEFI boot seems to not work correctly)

Example configuration with SecureBoot disabled

# 1. download nocloud-amd64.raw.xz, extract and convert to "rootfs.qcow2"
curl -L "https://factory.talos.dev/image/9ed5fecdacb36b5c5427b87d409f1065cfb2df69b0f71c58b868d9d466d8dab3/v1.12.2/nocloud-amd64.raw.xz" -o nocloud-amd64.raw.xz
unxz nocloud-amd64.raw.xz
qemu-img convert -f raw -O qcow2 nocloud-amd64.raw rootfs.qcow2

WARNING: Avoid SecureBoot enabled images if unsure of how they work.

Example configuration with SecureBoot enabled

# 1. download nocloud-amd64-secureboot.raw.xz, extract and convert to "rootfs.qcow2"
curl -L "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/v1.12.2/nocloud-amd64-secureboot.raw.xz" -o nocloud-amd64-secureboot.raw.xz
unxz nocloud-amd64-secureboot.raw.xz
qemu-img convert -f raw -O qcow2 nocloud-amd64-secureboot.raw rootfs.qcow2
# 2. generate image metadata tarball "metadata.tar.gz"
SERIAL="$(date '+%s')"
VERSION=v1.12.2
echo "
architecture: amd64
creation_date: $SERIAL
expiry_date: 0
properties:
  architecture: amd64
  description: talos $VERSION amd64 ($SERIAL)
  name: talos-$VERSION
  os: talos
  release: $VERSION
  serial: $SERIAL
  variant: default
templates: {}
" | tee metadata.yaml
tar cvzf metadata.tar.gz metadata.yaml

Finally, import the image into Incus:

# c. import image into Incus
incus image import metadata.tar.gz rootfs.qcow2 --alias talos-v1.12.2

Make a note of:

  • The image alias talos-v1.12.2, as we will later refer to it when generating the cluster manifest.
  • Whether you used the SecureBoot image variant (configuration below needs to match).

Configuration

# Cluster version and size
export KUBERNETES_VERSION=v1.34.0
export CONTROL_PLANE_MACHINE_COUNT=3
export WORKER_MACHINE_COUNT=2

# Name of secret with server credentials
export LXC_SECRET_NAME=lxc-secret

# Talos version (tested v1.12)
export TALOS_VERSION=v1.12

# Talos image (tested talos/nocloud/v1.12.2/amd64)
export TALOS_IMAGE_NAME=talos-v1.12.2           # alias of imported Talos nocloud image
export TALOS_IMAGE_SECURE_BOOT=false            # 'true' or 'false' based on nocloud image

# Load balancer configuration
export LXC_LOAD_BALANCER_TYPE=lxc               # must be 'lxc' or 'oci'
export LOAD_BALANCER_MACHINE_PROFILES=[default] # profiles for the haproxy container
export LOAD_BALANCER_MACHINE_FLAVOR=c1-m1       # instance type for the haproxy container

# Control plane machine configuration
export CONTROL_PLANE_MACHINE_FLAVOR=c2-m4       # flavor for control plane nodes
export CONTROL_PLANE_MACHINE_PROFILES=[default] # profiles for control plane nodes

# Worker machine configuration
export WORKER_MACHINE_FLAVOR=c2-m4              # flavor for worker nodes
export WORKER_MACHINE_PROFILES=[default]        # profiles for worker nodes

Generate cluster

clusterctl generate cluster example-cluster -i incus --flavor talos

Cluster Template

---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
  name: ${CLUSTER_NAME}
spec:
  clusterNetwork:
    pods:
      cidrBlocks: ${POD_CIDR:=[10.244.0.0/16]}
    services:
      cidrBlocks: ${SERVICE_CIDR:=[10.96.0.0/12]}
    serviceDomain: cluster.local
  controlPlaneRef:
    apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
    kind: TalosControlPlane
    name: ${CLUSTER_NAME}-control-plane
  infrastructureRef:
    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
    kind: LXCCluster
    name: ${CLUSTER_NAME}
---
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
metadata:
  name: ${CLUSTER_NAME}-control-plane
spec:
  replicas: ${CONTROL_PLANE_MACHINE_COUNT}
  version: ${KUBERNETES_VERSION}
  infrastructureTemplate:
    apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
    kind: LXCMachineTemplate
    name: ${CLUSTER_NAME}-control-plane
  controlPlaneConfig:
    controlplane:
      generateType: controlplane
      talosVersion: ${TALOS_VERSION}
      hostname:
        source: InfrastructureName
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: ${CLUSTER_NAME}-md-0
spec:
  clusterName: ${CLUSTER_NAME}
  replicas: ${WORKER_MACHINE_COUNT}
  selector:
    matchLabels:
  template:
    spec:
      version: ${KUBERNETES_VERSION}
      clusterName: ${CLUSTER_NAME}
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
          kind: TalosConfigTemplate
          name: ${CLUSTER_NAME}-md-0
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
        kind: LXCMachineTemplate
        name: ${CLUSTER_NAME}-md-0
---
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
kind: TalosConfigTemplate
metadata:
  name: ${CLUSTER_NAME}-md-0
spec:
  template:
    spec:
      generateType: worker
      talosVersion: ${TALOS_VERSION}
      hostname:
        source: InfrastructureName
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: LXCCluster
metadata:
  name: ${CLUSTER_NAME}
spec:
  secretRef:
    name: ${LXC_SECRET_NAME}
  cloudProviderNodePatch: true
  loadBalancer:
    ${LXC_LOAD_BALANCER_TYPE:=lxc}:
      instanceSpec:
        flavor: ${LOAD_BALANCER_MACHINE_FLAVOR:=""}
        profiles: ${LOAD_BALANCER_MACHINE_PROFILES:=[default]}
      disableHealthzCheck: true
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: LXCMachineTemplate
metadata:
  name: ${CLUSTER_NAME}-control-plane
spec:
  template:
    spec:
      instanceType: virtual-machine
      config:
        security.secureboot: "${TALOS_IMAGE_SECURE_BOOT:=false}"
      devices:
      - cloud-init,type=disk,source=cloud-init:config
      image:
        name: ${TALOS_IMAGE_NAME}
      flavor: ${CONTROL_PLANE_MACHINE_FLAVOR}
      profiles: ${CONTROL_PLANE_MACHINE_PROFILES:=[default]}
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2
kind: LXCMachineTemplate
metadata:
  name: ${CLUSTER_NAME}-md-0
spec:
  template:
    spec:
      instanceType: virtual-machine
      config:
        security.secureboot: "${TALOS_IMAGE_SECURE_BOOT:=false}"
      devices:
      - cloud-init,type=disk,source=cloud-init:config
      image:
        name: ${TALOS_IMAGE_NAME}
      flavor: ${WORKER_MACHINE_FLAVOR}
      profiles: ${WORKER_MACHINE_PROFILES:=[default]}