Talos - Un OS immuable pour Kubernetes
Introduction
Depuis quelque temps, je m’intéresse à Talos, un système d’exploitation pour Kubernetes. J’ai installé mon premier cluster Talos en novembre 2023, et ma “production” (composée de 3 Raspberry Pi) tourne maintenant sous cet OS.
C’est une solution qui m’a séduit par sa simplicité, sa sécurité et sa facilité d’administration.
Aujourd’hui, je me décide enfin à écrire cette page pour présenter Talos, et partager mon expérience avec cet OS.
Qu’est-ce que Talos ?
Comme dit précédemment, Talos est un OS spécialement conçu pour Kubernetes. C’est un système immuable et minimaliste.
Qu’est-ce qu’un OS immuable ?
Après avoir bricolé quelques définitions, je suis tombé sur celle-ci qui me semble être la plus claire :
Une distribution immuable garantit que le cœur du système d’exploitation reste inchangé. Le système de fichiers racine d’une distribution immuable reste en lecture seule, ce qui permet de rester le même sur plusieurs instances. Bien sûr, vous pouvez changer les choses si vous le souhaitez. Mais la capacité reste désactivée par défaut.
Source: Linux-Console.net
En tenant compte de cela, Talos permet d’installer un nœud Kubernetes en quelques minutes sans avoir à se soucier de la configuration de l’OS. De plus, Talos ne se contente pas de s’administrer avec un simple SSH, il propose une API pour gérer les nœuds avec le même principe que kubectl
avec Kubernetes.
Normalement à ce moment, certains grimacent en se disant “encore un truc qui sera impossible à débugger”. Mais SideroLabs (la société derrière Talos) a pensé à tout et l’absence d’un bash ne vous bloquera pas dans votre maintenance quotidienne. L’utilitaire talosctl
comporte de nombreux outils comme la génération d’un rapport pcap pour débugger un problème réseau, la consultation des logs, des moyens de vérifier l’état du cluster, etc.
Bref, Talos est une solution mature avec une communauté active et utilisée par de nombreuses entreprises.
Les extensions
Du fait de sa nature minimaliste, Talos ne contient pas de paquets jugés “inutiles” par le plus grand nombre. Il est néanmoins possible d’installer des extensions pour lui ajouter des fonctionnalités. Par exemple, il est possible d’installer qemu-guest-agent
pour avoir des informations sur la machine virtuelle, ou bien iscsi-tools
pour monter des disques iSCSI.
Ces extensions sont disponibles sur un dépôt géré par SideroLabs : siderolabs/extensions
Si vous n’êtes pas encore convaincu, j’espère que la suite de cet article vous donnera envie de tester Talos.
Installer talosctl
Le point d’entrée pour utiliser Talos est talosctl
. C’est l’utilitaire en CLI qui permet de générer la configuration du cluster, de l’appliquer sur les machines, de gérer les nœuds, de consulter les logs, etc. Il est possible de l’installer sur Linux, MacOS et Windows.
Sur Linux, on peut l’installer en exécutant la commande suivante :
curl -sL https://talos.dev/install | sh
Générer la configuration
Avant de faire quoi que ce soit, il faut générer les clés de chiffrement de notre cluster via la commande talosctl gen secrets
. On obtient alors un fichier secrets.yaml
qui contient les clés de chiffrement pour s’authentifier sur les nœuds du cluster.
Dès que les clés sont produites, on peut générer la configuration du cluster avec la commande talosctl gen config
en précisant le nom du cluster ainsi qu’un endpoint d’un des nœuds controlplane (qui permettra aux nœuds de rejoindre le cluster).
talosctl gen config homelab-talos-dev https://192.168.128.110:6443 --with-secrets ./secrets.yaml --install-disk /dev/sda
On obtient les fichiers controlplane.yaml
, worker.yaml
et talosconfig
. Ceux-ci contiennent la configuration de notre cluster, et le fichier talosconfig
, les clés pour s’authentifier sur les différents nœuds du cluster à l’API de Talos.
Au début, je modifiais directement les fichiers pour ajouter des paramètres spécifiques à mon infrastructure, mais avec du recul, je me rends compte que ce n’est pas la bonne approche. Il est préférable de stocker le delta de configuration dans un fichier patch.yaml
et de l’appliquer avec la commande talosctl gen config --config-patch @patch.yaml
.
# pach.yaml
machine:
install:
extraKernelArgs:
- net.ifnames=0
cluster:
proxy:
disabled: true
network:
cni:
name: none # On installera Cilium manuellement
apiServer:
certSANs:
- 192.168.1.1
- 127.0.0.1
Comme ça, je n’ai qu’à modifier le fichier patch.yaml
puis à regénérer les fichiers de configuration à chaque fois que je veux appliquer des changements.
Par contre, je ne peux pas avoir une configuration différente pour les controlplanes et les workers, il faudra alors générer un fichier de configuration par type de machine (on parlera d’une solution pour ça plus tard).
Installer le cluster
Ma configuration est prête (après le talosctl gen config --config-patch @patch.yaml
), il ne reste qu’à l’envoyer sur les machines qui vont composer le cluster.
Pour installer ces serveurs, j’ai téléchargé l’image de Talos sur le dépôt Github de Talos et l’ai installée sur mes hyperviseurs Proxmox.
L’ISO de Talos est très légère, elle fait moins de 100Mo et aucune installation n’est nécessaire (elle se fera automatiquement lors de la réception de la configuration).
Nom | Adresse IP | Rôle |
---|---|---|
controlplane-01 | 192.168.1.85 | Control Plane |
controlplane-02 | 192.168.1.79 | Control Plane |
controlplane-03 | 192.168.1.82 | Control Plane |
worker-01 | 192.168.1.83 | Worker |
worker-02 | 192.168.1.86 | Worker |
Avant d’appliquer la configuration, je vais vérifier que les disques soient bien détectés par Talos (et qu’ils utilisent bien le bon disque pour l’installation).
$ talosctl disks --insecure -n 192.168.1.85 -e 192.168.1.85
DEV MODEL SERIAL TYPE UUID WWID MODALIAS NAME SIZE BUS_PATH SUBSYSTEM READ_ONLY SYSTEM_DISK
/dev/sda QEMU HARDDISK - HDD - - scsi:t-0x00 - 34 GB /pci0000:00/0000:00:05.0/0000:01:01.0/virtio2/host2/target2:0:0/2:0:0:0/ /sys/class/block
J’ai bel et bien utilisé /dev/sda
pour l’installation de Talos, je peux donc appliquer la configuration sur les machines.
Information
Et si mes noeuds n’utilisent pas le même disque pour l’installation ?
Pour cela, il faudra dupliquer le fichier de configuration et modifier le champ installDisk
pour chaque machine.
# Appliquer la configuration sur les nœuds controlplane
talosctl apply-config --insecure -n 192.168.1.85 -e 192.168.1.85 --file controlplane.yaml
talosctl apply-config --insecure -n 192.168.1.79 -e 192.168.1.79 --file controlplane.yaml
talosctl apply-config --insecure -n 192.168.1.82 -e 192.168.1.82 --file controlplane.yaml
# Appliquer la configuration sur les nœuds worker
talosctl apply-config --insecure -n 192.168.1.83 -e 192.168.1.83 --file worker.yaml
talosctl apply-config --insecure -n 192.168.1.86 -e 192.168.1.86 --file worker.yaml
Les machines s’installent, on peut suivre l’avancement de l’installation directement sur l’hyperviseur (ou avec talosctl logs
/ talosctl dmesg
).
Si tout se passe bien, une erreur devrait s’afficher :
trucmuche - Service "etcd" to be "up"
Cette erreur n’en est pas réellement une, elle signifie que la base de donnée etcd
n’est pas encore initialisée. On va s’en occuper tout de suite avec la commande talosctl bootstrap
en désignant un des controlplane.
talosctl bootstrap -e 192.168.1.85 --talosconfig ./talosconfig --nodes 192.168.1.85
Maintenant, si on regarde les nœuds du cluster, on devrait voir que les control-planes ne sont pas en état Ready
.
La raison : Nous n’avons encore aucun CNI ! Récupérons notre kubeconfig
et résolvons ce problème.
$ talosctl kubeconfig -e 192.168.1.85 --talosconfig ./talosconfig --nodes 192.168.1.85
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
talos-8tc-18b NotReady control-plane 6m5s v1.29.1
talos-fiy-ula NotReady <none> 2m48s v1.29.1
talos-x5n-ji0 NotReady <none> 2m43s v1.29.1
talos-xtb-22h NotReady control-plane 6m7s v1.29.1
talos-ypm-jy8 NotReady control-plane 6m3s v1.29.1
$ kubectl describe node talos-8tc-18b
# [ ... ]
Conditions:
Type Status LastHeartbeatTime LastTransitionTime Reason Message
---- ------ ----------------- ------------------ ------ -------
MemoryPressure False Thu, 22 Feb 2024 07:38:31 +0100 Thu, 22 Feb 2024 07:33:13 +0100 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Thu, 22 Feb 2024 07:38:31 +0100 Thu, 22 Feb 2024 07:33:13 +0100 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Thu, 22 Feb 2024 07:38:31 +0100 Thu, 22 Feb 2024 07:33:13 +0100 KubeletHasSufficientPID kubelet has sufficient PID available
Ready False Thu, 22 Feb 2024 07:38:31 +0100 Thu, 22 Feb 2024 07:33:13 +0100 KubeletNotReady container runtime network not ready: NetworkRe
ady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized
# [ ... ]
Il manque bel et bien un CNI. Je vais alors installer Cilium (qui va également remplacer kube-proxy dans mon cas).
La documentation de Talos propose directement la commande pour installer Cilium en remplaçant kube-proxy. On va directement l’appliquer sur le cluster.
$ helm repo add cilium https://helm.cilium.io/
$ helm repo update
$ helm install \
cilium \
cilium/cilium \
--version 1.15.0 \
--namespace kube-system \
--set=ipam.mode=kubernetes \
--set=bpf.masquerade=true \
--set=kubeProxyReplacement=true \
--set=securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set=securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set=cgroup.autoMount.enabled=false \
--set=cgroup.hostRoot=/sys/fs/cgroup \
--set=k8sServiceHost=localhost \
--set=k8sServicePort=7445
NAME: cilium
LAST DEPLOYED: Fri Feb 23 09:43:35 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble.
Your release version is 1.15.0.
For any further help, visit https://docs.cilium.io/en/v1.15/gettinghelp
Nos nœuds sont prêts à accueillir des pods !
Définir les nœuds / endpoints
Depuis le début de cet article, nous précisons à chaque commande talosctl:
- Une adresse IP d’un control-plane (endpoint) talos.
- L’adresse IP de la machine sur laquelle nous exécutons la commande.
- Le fichier de configuration
talosconfig
que nous avons généré (contenant le nécessaire pour s’authentifier sur le cluster).
Cela devient vite fastidieux, surtout si on a beaucoup de machines à gérer. Il est alors possible de définir ces informations dans un dossier ~/.talos
comme le ferait kubectl
avec ~/.kube
.
talosctl --talosconfig=./talosconfig config endpoint 192.168.1.85 192.168.1.82 192.168.1.79 # Control plane
talosctl --talosconfig=./talosconfig config node 192.168.1.85 192.168.1.82 192.168.1.79 192.168.1.83 192.168.1.86 # controle plane + nodes
talosctl config merge ./talosconfig
Avec ces commandes, j’édite le fichier ./talosconfig
pour ajouter les adresses IP des endpoints, et des machines du cluster, puis je fusionne ce fichier avec le fichier ~/.talos/config
.
Information
Tout comme kubectl
le permet : il est possible d’avoir plusieurs contextes pour gérer plusieurs clusters. Il suffit de rajouter un argument --context
à la commande talosctl
ou d’éditer le fichier ~/.talos/config
pour rajouter un nouveau contexte.
Je n’ai plus à préciser l’adresse IP de l’endpoint ou de la machine sur laquelle j’exécute la commande, puis talosctl
va aller chercher ces informations dans le fichier ~/.talos/config
.
$ talosctl disks
NODE DEV MODEL SERIAL TYPE UUID WWID MODALIAS NAME SIZE BUS_PATH SUBSYSTEM READ_ONLY SYSTEM_DISK
192.168.1.79 /dev/sda QEMU HARDDISK - HDD - - scsi:t-0x00 - 34 GB /pci0000:00/0000:00:05.0/0000:01:01.0/virtio3/host2/target2:0:0/2:0:0:0/ /sys/class/block *
192.168.1.85 /dev/sda QEMU HARDDISK - HDD - - scsi:t-0x00 - 34 GB /pci0000:00/0000:00:05.0/0000:01:01.0/virtio3/host2/target2:0:0/2:0:0:0/ /sys/class/block *
192.168.1.82 /dev/sda QEMU HARDDISK - HDD - - scsi:t-0x00 - 34 GB /pci0000:00/0000:00:05.0/0000:01:01.0/virtio3/host2/target2:0:0/2:0:0:0/ /sys/class/block
# [ ... ]
Mettre à jour le cluster
Bien évidemment, Talos supporte le fait de mettre à jour le kubelet
d’un nœud sans forcément devoir réinstaller le système complet.
Pour cela, l’utilitaire talosctl
dispose d’un argument upgrade-k8s
. Il suffit alors de préciser la version de kubelet que l’on souhaite installer et le nœud désigné sera mis à jour.
Avertissement
À savoir qu’il n’est pas possible de migrer de plusieurs versions majeures en un seul coup (ex, de 1.27 à 1.29). Il faudra alors mettre à jour chaque version majeure une par une (d’abord de 1.27 à 1.28, puis de 1.28 à 1.29).
Talos mettra donc à jour chaque nœud un par un, et le redémarrera (si nécessaire) pour appliquer les changements.
$ talosctl upgrade-k8s --to 1.29.1 -n 192.168.1.85 # Même s'il n'y a qu'un nœud précisé, ils seront tous mis à jour
Modifier la configuration d’un nœud
Il est possible à tout moment de modifier la configuration d’une des machines du cluster. C’est indispensable pour pouvoir mettre à jour les adresses IPs, ou rajouter des paramètres à une installation déjà existante.
Par exemple, si je veux changer le nom d’hôte de talos-8tc-18b
en talos-controlplane-01
, je peux le faire de plusieurs manières :
- En éditant en interactif la configuration de la machine :
talosctl -n <IP1>,<IP2>,... edit machineconfig
- En créant un
patch.yaml
et l’envoyant sur les nœuds :
- op: add
path: /machine/network/hostname
value: talos-controlplane-01
talosctl -n <IP1>,<IP2> patch machineconfig -p @patch.yaml
- Ou bien en appliquant ce même patch en JSON directement dans la commande :
talosctl -n <IP> patch machineconfig -p '[{"op": "add", "path": "/machine/network/hostname", "value": "talos-controlplane-01"}]'
En fonction de la modification apportée, Talos va automatiquement redémarrer la machine pour appliquer la configuration (ou non, si ce n’est pas nécessaire).
Un fichier par machine avec talhelper
La configuration de talosctl
est très pratique et agréable à utiliser. Seul le fichier patch.yaml
est à sauvegarder sur un Git et les fichiers secret.yaml
et talosconfig
à garder en sécurité dans un vault ou équivalent (les autres fichiers controlplane.yaml
et worker.yaml
sont re-générables, la sauvegarde de ces fichiers est alors moins pertinente).
Mais je ne reste pas moins frustré par une limitation de talosctl
:
- En effet, il ne permet pas de générer un fichier de configuration par machine du cluster.
Ainsi, si je veux créer un cluster avec des machines drastiquement différentes, je vais devoir créer un fichier de configuration par machine (ce qui est fastidieux).
Pour palier à ces problèmes budimanjojo a développé talhelper.
talhelper
est un programme en go qui répond à cette problématique : Il génère un fichier par machine du cluster à partir d’un unique fichier de configuration et va vous aider à générer les commandes pour appliquer la configuration sur les machines.
Utiliser talhelper
Comme dit précédemment, talhelper
s’appuie sur un unique fichier de configuration : talconfig.yaml
. Celui-ci va contenir les informations de chaque machine du cluster, à minima :
- Le nom d’hôte ;
- L’adresse IP ;
- Le disque sur lequel installer talos.
De nombreux paramètres optionnels permettent de personnaliser la configuration de chaque machine. Par exemple, vous pouvez configurer des VIP, des routes, des disques additionnels, des extensions Talos (kata-containers, qemu-guest-agent, iscsi-tools), des patches pour la configuration de Talos, etc.
Pour créer ce fichier, j’utilise la template proposée sur le site de talhelper.
Remarque
À savoir que de nombreux paramètres ne sont pas valorisés dans la template. La documentation de talhelper
est très correcte et même si un paramètre n’est pas pris en compte, il est possible de rajouter un patch qui sera appliqué lors de la génération de la configuration.
Par exemple, je souhaite désactiver l’usage du discovery dans mon talconfig, problème : talhelper
ne possède aucun paramètre pour désactiver ça. Je peux alors créer un ‘patch’ qui sera appliqué lorsque talhelper
génèrera la configuration.
Pour cela, il me suffit de mettre ces lignes dans mon talconfig.yaml
:
patches:
- |-
- op: add
path: /cluster/discovery/enabled
value: false
Mon fichier talconfig.yaml
ressemble à ceci :
---
clusterName: homelab-talos-dev
talosVersion: v1.6.5
kubernetesVersion: v1.27.2
endpoint: https://192.168.1.85:6443
allowSchedulingOnMasters: true
cniConfig:
name: none
patches:
- |-
- op: add
path: /cluster/discovery/enabled
value: true
- op: replace
path: /machine/network/kubespan
value:
enabled: true
nodes:
- hostname: controlplane-01
ipAddress: 192.168.1.85
controlPlane: true
arch: amd64
installDisk: /dev/sda
nameservers:
- 1.1.1.1
- 8.8.8.8
networkInterfaces:
- interface: eth0
addresses:
- 192.168.1.85/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.1.1
vip:
ip: 192.168.1.80
- hostname: controlplane-02
ipAddress: 192.168.1.79
controlPlane: true
arch: amd64
installDisk: /dev/sda
nameservers:
- 1.1.1.1
- 8.8.8.8
networkInterfaces:
- interface: eth0
addresses:
- 192.168.1.79/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.1.1
vip:
ip: 192.168.1.80
- hostname: controlplane-03
ipAddress: 192.168.1.82
controlPlane: true
arch: amd64
installDisk: /dev/sda
nameservers:
- 1.1.1.1
- 8.8.8.8
networkInterfaces:
- interface: eth0
addresses:
- 192.168.1.82/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.1.1
vip:
ip: 192.168.1.80
- hostname: worker-01
ipAddress: 192.168.1.83
controlPlane: false
arch: amd64
installDisk: /dev/sda
- hostname: worker-02
ipAddress: 192.168.1.86
controlPlane: false
arch: amd64
installDisk: /dev/sda
controlPlane:
patches:
- |-
- op: add
path: /cluster/proxy/disabled
value: true
schematic:
customization:
extraKernelArgs:
- net.ifnames=0
systemExtensions:
officialExtensions:
# - siderolabs/kata-containers # Disponible à la sortie de Talos 1.7
- siderolabs/qemu-guest-agent
- siderolabs/iscsi-tools
worker:
schematic:
customization:
extraKernelArgs:
- net.ifnames=0
systemExtensions:
officialExtensions:
# - siderolabs/kata-containers # Disponible à la sortie de Talos 1.7
- siderolabs/qemu-guest-agent
- siderolabs/iscsi-tools
On commence par générer le fichier talsecret.yaml
. Celui-ci est strictement équivalent au fichier secret.yaml
généré par talosctl
(il est donc possible d’utiliser celui que nous avons généré précédemment).
talhelper gensecret > talsecret.yaml # ou talosctl gen secrets
Ensuite, on génère les fichiers de configuration pour chaque machine du cluster.
$ talhelper genconfig
There are issues with your talhelper config file:
field: "talosVersion"
* WARNING: "v1.6.5" might not be compatible with this Talhelper version you're using
generated config for controlplane-01 in ./clusterconfig/homelab-talos-dev-controlplane-01.yaml
generated config for controlplane-02 in ./clusterconfig/homelab-talos-dev-controlplane-02.yaml
generated config for controlplane-03 in ./clusterconfig/homelab-talos-dev-controlplane-03.yaml
generated config for worker-01 in ./clusterconfig/homelab-talos-dev-worker-01.yaml
generated config for worker-01 in ./clusterconfig/homelab-talos-dev-worker-01.yaml
generated client config in ./clusterconfig/talosconfig
generated .gitignore file in ./clusterconfig/.gitignore
Information
talhelper
va de lui-même générer un fichier .gitignore
qui permet de prevenir un push des fichiers contenant les clés vers un Git.
Dans mon cas, il contient les lignes suivantes:
homelab-talos-dev-controlplane-01.yaml
homelab-talos-dev-controlplane-02.yaml
homelab-talos-dev-controlplane-03.yaml
homelab-talos-dev-worker-01.yaml
talosconfig
La dernière étape est d’apply les fichiers pour lancer l’installation du cluster. talhelper
permet aussi de générer les commandes bash appliquant chaque fichier sur la bonne adresse IP.
Cela évite d’appliquer un fichier de configuration sur une mauvaise machine.
$ talhelper gencommand apply --extra-flags --insecure
There are issues with your talhelper config file:
field: "talosVersion"
* WARNING: "v1.6.5" might not be compatible with this Talhelper version you're using
talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.85 --file=./clusterconfig/homelab-talos-dev-controlplane-01.yaml --insecure;
talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.79 --file=./clusterconfig/homelab-talos-dev-controlplane-02.yaml --insecure;
talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.82 --file=./clusterconfig/homelab-talos-dev-controlplane-03.yaml --insecure;
talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.83 --file=./clusterconfig/homelab-talos-dev-worker-01.yaml --insecure;
talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.86 --file=./clusterconfig/homelab-talos-dev-worker-01.yaml --insecure;
Information
le flag --insecure
permet d’apply sans préciser de clé de chiffrement, c’est obligatoire lorsque le nœud n’est pas encore installé. Par défaut, talhelper
ne l’ajoute pas dans les commandes générées, c’est pourquoi j’utilise l’argument --extra-flags
pour rajouter ce flag.
Je peux également lui demander de me fournir les commandes de bootstrap ou de récupération du kubeconfig
(moins intéressant mais c’est pratique).
$ talhelper gencommand bootstrap
talosctl bootstrap --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.85;
$ talhelper gencommand kubeconfig
talosctl kubeconfig --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.85;
Mais Talos ne s’arrête pas là ! Il propose également des fonctionnalités avancées pour faire des clusters multi-régions. Voyons ça de plus près.
Cluster Across-Region : KubeSpan
En plus de Talos, SideroLabs souhaite répondre au besoin des entreprises de faire des clusters hybrides et/ou multi-regions sans devoir exposer l’API d’un control-plane. Une solution courante est d’établir un VPN entre les différentes régions, mais cela peut être coûteux et complexe à mettre en place.
On pourrait par exemple faire un cluster où les control-planes seraient dans une infrastructure on-premise, et les workers sur un cloud provider (pour les workloads qui nécessitent de la puissance de calcul).
Cette fonctionnalité se nomme KubeSpan et est spécifique à Talos.
KubeSpan est basé sur Wireguard et utilise un service hébergé par Talos: Discovery.
Pour utiliser cette fonctionnalité, il faut bien sûr qu’elle soit activée dans la configuration de Talos (à /machine/network/kubespan/enabled
).
Si vous avez un cluster déjà en place, vous pouvez envoyer ce fichier patch.yaml
sur les machines pour activer le service discovery et KubeSpan.
machine:
network:
kubespan:
enabled: true
cluster:
discovery:
enabled: true
Dans la pratique, Discovery va authentifier les machines du même cluster via le cluster id/secret. Ces valeurs sont générées automatiquement par talosctl
(même si le kubspan/discovery est désactivé).
cluster:
id: vPBXurnpNDVRVkF_DrM4SvA7YNV6xcwt7mnkSKOQQoI=
secret: +LoNuCGKUDWW0Efo8tph/XkbFI7ffS+w2slsFell+bY=
Ces tokens sont à garder en sécurité, ils permettent à un nœud d’initier une connexion avec le service discovery vers un autre nœud (même si ça ne suffit pas pour s’enregistrer dans le cluster, il convient de garder ces tokens en sécurité).
Derrière le service discovery, il y a un service de Wireguard qui permet aux nœuds de communiquer entre eux sans avoir à faire du NAT pour exposer l’API de Kubernetes.
Je vais donc activer cette fonctionnalité sur mon cluster. Mon objectif est d’avoir les control-planes chez moi, et les workers sur Vultr.
Astuce
Pour réinitialiser le cluster, talhelper
propose une commande talhelper gencommand reset
qui permet de supprimer les nœuds de ce dernier :
$ talhelper gencommand reset --extra-flags --graceful=false
There are issues with your talhelper config file:
field: "talosVersion"
* WARNING: "v1.6.5" might not be compatible with this Talhelper version you're using
talosctl reset --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.85 --graceful=false;
talosctl reset --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.79 --graceful=false;
talosctl reset --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.82 --graceful=false;
# [ ... ]
Créer des nœuds sur Vultr
Vultr est un fournisseur de cloud qui propose des instances à des prix très compétitifs. Talos le présente comme un fournisseur de cloud compatible, donc je vais essayer de créer des nœuds worker sur Vultr.
Je me suis beaucoup appuyé sur la documentation de Vultr de Talos pour cette partie.
La première étape est d’envoyer une ISO de Talos sur Vultr. Pour cela, j’utilise la cli proposée par Vultr vultr-cli.
vultr-cli iso create --url https://github.com/siderolabs/talos/releases/download/v1.6.4/metal-amd64.iso
ID FILE NAME SIZE STATUS MD5SUM SHA512SUM DATE CREATED
61f5fdbe-b773-4aff-b3a5-9c1ae6eee70a metal-amd64.iso 85168128 complete 65f0eadf019086c4b11f57977d5521b4 cd8f887b91a154132a55dc752a8718fd5ef37177963a5c10c1be295382264b4305757442eea0fa06bff98208d897512f056d3e9701f24678ffe35ad30336fd7a 2024-02-23T11:44:27+00:00
On garde l’ID de l’ISO qui servira à créer notre instance Talos (dans mon cas, 61f5fdbe-b773-4aff-b3a5-9c1ae6eee70a
).
for i in 1 2; do
vultr-cli instance create \
--plan vc2-2c-4gb \
--region cdg \
--iso "61f5fdbe-b773-4aff-b3a5-9c1ae6eee70a" \
--host talos-k8s-${i} \
--label "Talos Kubernetes" \
--tags talos,kubernetes \
--ipv6
done
Après 1-2 minutes, les instances sont prêtes. On peut alors récupérer les adresses IP des nœuds et les ajouter à notre configuration.
$ vultr-cli instance list
ID IP LABEL OS STATUS REGION CPU RAM DISK BANDWIDTH TAGS
ac9150e3-5b07-4d0c-b6a7-4ab1eedde7c3 45.32.146.110 Talos Kubernetes Custom Installed active cdg 2 4096 80 4 [kubernetes, talos]
21fec645-f391-48cb-87a8-8ea11c223afc 217.69.4.72 Talos Kubernetes Custom Installed active cdg 2 4096 80 4 [kubernetes, talos]
On va noter ces adresses IP et créer notre fichier talconfig.yaml
avec les informations de nos nœuds.
---
clusterName: cross-region-cluster
talosVersion: v1.6.5
kubernetesVersion: v1.29.1
endpoint: https://192.168.1.85:6443
allowSchedulingOnMasters: true
cniConfig:
name: none
patches:
- |-
- op: add
path: /cluster/discovery/enabled
value: true
- op: replace
path: /machine/network/kubespan
value:
enabled: true
nodes:
- hostname: controlplane-01
ipAddress: 192.168.1.85
controlPlane: true
arch: amd64
installDisk: /dev/sda
networkInterfaces:
- interface: eth0
addresses:
- 192.168.1.85/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.1.1
vip:
ip: 192.168.1.80
- hostname: controlplane-02
ipAddress: 192.168.1.79
controlPlane: true
arch: amd64
installDisk: /dev/sda
networkInterfaces:
- interface: eth0
addresses:
- 192.168.1.79/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.1.1
vip:
ip: 192.168.1.80
- hostname: controlplane-03
ipAddress: 192.168.1.82
controlPlane: true
arch: amd64
installDisk: /dev/sda
networkInterfaces:
- interface: eth0
addresses:
- 192.168.1.82/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.1.1
vip:
ip: 192.168.1.80
- hostname: vultr-worker-01
ipAddress: 45.32.146.110
controlPlane: false
arch: amd64
installDisk: /dev/vda
- hostname: vultr-worker-02
ipAddress: 217.69.4.72
controlPlane: false
arch: amd64
installDisk: /dev/vda
controlPlane:
patches:
- |-
- op: add
path: /cluster/proxy/disabled
value: true
schematic:
customization:
extraKernelArgs:
- net.ifnames=0
systemExtensions:
officialExtensions:
- siderolabs/qemu-guest-agent
- siderolabs/iscsi-tools
worker:
schematic:
customization:
extraKernelArgs:
- net.ifnames=0
systemExtensions:
officialExtensions:
- siderolabs/iscsi-tools
⚠️ Attention, sur Vultr, le disque d’installation est /dev/vda
et non /dev/sda
.
$ talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.85 --file=./clusterconfig/cross-region-cluster-controlplane-01.yaml --insecure;
$ talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.79 --file=./clusterconfig/cross-region-cluster-controlplane-02.yaml --insecure;
$ talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.82 --file=./clusterconfig/cross-region-cluster-controlplane-03.yaml --insecure;
$ talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=45.32.146.110 --file=./clusterconfig/cross-region-cluster-vultr-worker-01.yaml --insecure;
$ talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=217.69.4.72 --file=./clusterconfig/cross-region-cluster-vultr-worker-02.yaml --insecure;
$ talosctl bootstrap --talosconfig=./clusterconfig/talosconfig --nodes=192.168.1.85;
J’installe ensuite Cilium sur mon cluster :
helm install \
cilium \
cilium/cilium \
--version 1.15.0 \
--namespace kube-system \
--set=ipam.mode=kubernetes \
--set=kubeProxyReplacement=true \
--set=securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set=securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set=cgroup.autoMount.enabled=false \
--set=cgroup.hostRoot=/sys/fs/cgroup \
--set=k8sServiceHost=localhost \
--set=k8sServicePort=7445
Mon cluster possède maintenant cinq nœuds, trois control-planes sur mon réseau local et deux workers sur Vultr.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controlplane-01 Ready control-plane 6m43s v1.29.1
controlplane-02 Ready control-plane 6m40s v1.29.1
controlplane-03 Ready control-plane 6m42s v1.29.1
vultr-worker-01 Ready <none> 6m40s v1.29.1
vultr-worker-02 Ready <none> 6m45s v1.29.1
Ajouter un nœud au cluster multi région
Je vais ajouter un sixième nœud à mon cluster, mais cette fois-ci sur mon infrastructure virtualisée chez OVH (dans un datacenter à Roubaix).
J’y ai déjà une machine virtuelle prête et en attente d’une configuration Talos. Dans son réseau privé, elle dispose de l’adresse IP 192.168.128.112
.
Je l’ajoute alors à mon fichier talconfig.yaml
:
- hostname: ovh-worker-03
ipAddress: 192.168.128.112
controlPlane: false
arch: amd64
installDisk: /dev/sda
Je me connecte au bastion (sur lequel j’ai déjà configuré talosctl
), et j’applique la configuration sur la machine.
$ talosctl apply-config --talosconfig=./clusterconfig/talosconfig --nodes=192.168.128.112 --file=./clusterconfig/cross-region-cluster-ovh-worker-03.yaml --insecure;
En l’état, ça ne fonctionne pas. La raison est que pour que le worker rejoigne le cluster, il doit pouvoir communiquer avec les controlplanes. Cette communication est faite avec le port 51820
(en UDP), le worker ou le control-plane doivent pouvoir se connecter au port de l’autre pour que la connexion soit établie.
$ talosctl get kubespanpeerstatus -n 192.168.1.85
192.168.1.85 kubespan KubeSpanPeerStatus DT8v2yiopnU6aPp3nxJurX/HfFTWy4dj1haWXINXjhc= 60 vultr-worker-01 45.32.146.110:51820 up 1723500 3403308
192.168.1.85 kubespan KubeSpanPeerStatus F2nTY5mP5aVBKRz5V1Bl5Ba93+G6EWY12SKT5PSnpFI= 62 vultr-worker-02 217.69.4.72:51820 up 449052 641764
192.168.1.85 kubespan KubeSpanPeerStatus UelYhx04oUfdS5X+yNf+1dOsvTzfBZc+WrH/GY4HKWU= 23 ovh-worker-03 5.39.75.213:51820 unknown 0 17168
192.168.1.85 kubespan KubeSpanPeerStatus Vbvp05aKjlLhXb6KD2zo5C6aneRQEpsLEZ/AQLjXlmU= 62 controlplane-03 192.168.1.82:51820 up 18745312 37628360
192.168.1.85 kubespan KubeSpanPeerStatus o9BLRyCBgMyJoNbEqdaD16PY2sPSLkXUaj4Vy+qBl3U= 63 controlplane-02 192.168.1.79:51820 up 17769228 27982564
Actuellement, les nœuds tentent des requêtes vers le port 51820
du nœud ovh-worker-03
. Le client KubeSpan va essayer toutes les interfaces (IPv4 et IPv6) pour se connecter au nœud mais… impossible puisque je n’ai pas ouvert le port depuis mon routeur PFSense.
Une ouverture de port plus tard…
$ talosctl get kubespanpeerstatus -n 192.168.1.85
NODE NAMESPACE TYPE ID VERSION LABEL ENDPOINT STATE RX TX
192.168.1.85 kubespan KubeSpanPeerStatus DT8v2yiopnU6aPp3nxJurX/HfFTWy4dj1haWXINXjhc= 75 vultr-worker-01 45.32.146.110:51820 up 2169468 4172648
192.168.1.85 kubespan KubeSpanPeerStatus F2nTY5mP5aVBKRz5V1Bl5Ba93+G6EWY12SKT5PSnpFI= 77 vultr-worker-02 217.69.4.72:51820 up 547696 748732
192.168.1.85 kubespan KubeSpanPeerStatus UelYhx04oUfdS5X+yNf+1dOsvTzfBZc+WrH/GY4HKWU= 38 ovh-worker-03 5.39.75.213:51820 up 314900 997748
192.168.1.85 kubespan KubeSpanPeerStatus Vbvp05aKjlLhXb6KD2zo5C6aneRQEpsLEZ/AQLjXlmU= 77 controlplane-03 192.168.1.82:51820 up 23345384 45761364
192.168.1.85 kubespan KubeSpanPeerStatus o9BLRyCBgMyJoNbEqdaD16PY2sPSLkXUaj4Vy+qBl3U= 78 controlplane-02 192.168.1.79:51820 up 22072656 33896416
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controlplane-01 Ready control-plane 33m v1.29.1
controlplane-02 Ready control-plane 33m v1.29.1
controlplane-03 Ready control-plane 33m v1.29.1
ovh-worker-03 Ready <none> 5m6s v1.29.1
vultr-worker-01 Ready <none> 33m v1.29.1
vultr-worker-02 Ready <none> 34m v1.29.1
Ainsi, ovh-worker-03
a bel et bien rejoint le cluster, et est prêt à accueillir des applications !
Mon cluster de “production” étant constitué de 3 Raspberry Pi, je peux à présent ajouter des nœuds provenant de différents fournisseurs de cloud, et ce, sans avoir à me soucier de la configuration réseau.
Benchmark réseau entre les nœuds
Forcément, avec un cluster multi-région, on peut se poser la question des performances réseau. J’ai donc utilisé le projet k8s-Bench-Suite proposant un script pour benchmarker les performances réseau entre les nœuds.
À savoir que les performances réseaux sont très dépendantes de la qualité de la connexion entre les nœuds.
Mon débit internet est de ~300Mbps (en symétrique) avec l’offre Sosh Fibre.
Mon débit internet :
Speedtest by Ookla
Server: LASOTEL - Lyon (id: 42393)
ISP: Orange
Idle Latency: 2.01 ms (jitter: 0.08ms, low: 1.93ms, high: 2.08ms)
Download: 283.37 Mbps (data used: 208.0 MB)
9.38 ms (jitter: 0.81ms, low: 5.76ms, high: 15.31ms)
Upload: 297.00 Mbps (data used: 356.2 MB)
37.01 ms (jitter: 1.11ms, low: 13.53ms, high: 41.06ms)
Packet Loss: 0.0%
Débit des serveurs Vultr :
Speedtest by Ookla
Server: Nextmap - LeKloud - Paris (id: 33869)
ISP: Vultr
Idle Latency: 1.39 ms (jitter: 0.73ms, low: 0.94ms, high: 3.18ms)
Download: 1257.66 Mbps (data used: 1.5 GB)
6.56 ms (jitter: 19.39ms, low: 1.08ms, high: 452.37ms)
Upload: 3550.69 Mbps (data used: 3.2 GB)
3.58 ms (jitter: 4.17ms, low: 1.16ms, high: 74.61ms)
Packet Loss: 0.0%
( On voit immédiatement où sera le goulot d’étranglement dans les performances réseau.)
Lançons le benchmark entre deux nœuds dans le même réseau (controlplane-01
et controlplane-02
) :
=========================================================
Benchmark Results
=========================================================
Name : knb-1012758
Date : 2024-02-24 07:49:12 UTC
Generator : knb
Version : 1.5.0
Server : controlplane-02
Client : controlplane-01
UDP Socket size : auto
=========================================================
Discovered CPU : QEMU Virtual CPU version 2.5+
Discovered Kernel : 6.1.78-talos
Discovered k8s version :
Discovered MTU : 1500
Idle :
bandwidth = 0 Mbit/s
client cpu = total 8.23% (user 3.65%, nice 0.00%, system 3.19%, iowait 0.56%, steal 0.83%)
server cpu = total 7.11% (user 3.14%, nice 0.00%, system 2.77%, iowait 0.60%, steal 0.60%)
client ram = 1231 MB
server ram = 1179 MB
Pod to pod :
TCP :
bandwidth = 287 Mbit/s
client cpu = total 70.99% (user 4.76%, nice 0.00%, system 45.39%, iowait 0.40%, steal 20.44%)
server cpu = total 73.55% (user 4.42%, nice 0.00%, system 51.63%, iowait 0.22%, steal 17.28%)
client ram = 1239 MB
server ram = 1182 MB
UDP :
bandwidth = 194 Mbit/s
client cpu = total 72.85% (user 8.15%, nice 0.00%, system 52.78%, iowait 0.18%, steal 11.74%)
server cpu = total 65.34% (user 6.35%, nice 0.00%, system 42.11%, iowait 0.17%, steal 16.71%)
client ram = 1241 MB
server ram = 1180 MB
Pod to Service :
TCP :
bandwidth = 288 Mbit/s
client cpu = total 72.91% (user 5.87%, nice 0.00%, system 45.40%, iowait 0.20%, steal 21.44%)
server cpu = total 75.97% (user 4.40%, nice 0.00%, system 52.85%, iowait 0.19%, steal 18.53%)
client ram = 1247 MB
server ram = 1182 MB
UDP :
bandwidth = 194 Mbit/s
client cpu = total 72.61% (user 6.79%, nice 0.00%, system 52.33%, iowait 0.41%, steal 13.08%)
server cpu = total 63.99% (user 6.40%, nice 0.00%, system 40.88%, iowait 0.35%, steal 16.36%)
client ram = 1247 MB
server ram = 1180 MB
=========================================================
Un débit de 288 Mbit/s en TCP en pod-to-service (ce qui réprésente la majorité des communications dans un cluster Kubernetes) est très bon.
Voyons maintenant les performances entre deux nœuds dans des réseaux différents (controlplane-01
et vultr-worker-01
) :
=========================================================
Benchmark Results
=========================================================
Name : knb-1020457
Date : 2024-02-24 08:13:47 UTC
Generator : knb
Version : 1.5.0
Server : vultr-worker-04
Client : controlplane-01
UDP Socket size : auto
=========================================================
Discovered CPU : AMD EPYC-Rome Processor
Discovered Kernel : 6.1.78-talos
Discovered k8s version :
Discovered MTU : 1500
Idle :
bandwidth = 0 Mbit/s
client cpu = total 8.09% (user 3.55%, nice 0.00%, system 2.95%, iowait 0.75%, steal 0.84%)
server cpu = total 2.52% (user 1.42%, nice 0.00%, system 0.92%, iowait 0.00%, steal 0.18%)
client ram = 1232 MB
server ram = 442 MB
Pod to pod :
TCP :
bandwidth = 22.0 Mbit/s
client cpu = total 14.33% (user 3.72%, nice 0.00%, system 8.78%, iowait 0.56%, steal 1.27%)
server cpu = total 17.78% (user 1.69%, nice 0.00%, system 14.95%, iowait 0.00%, steal 1.14%)
client ram = 1237 MB
server ram = 443 MB
UDP :
bandwidth = 31.4 Mbit/s
client cpu = total 68.58% (user 6.66%, nice 0.00%, system 58.55%, iowait 0.37%, steal 3.00%)
server cpu = total 19.31% (user 1.89%, nice 0.00%, system 16.27%, iowait 0.00%, steal 1.15%)
client ram = 1238 MB
server ram = 445 MB
Pod to Service :
TCP :
bandwidth = 15.0 Mbit/s
client cpu = total 13.69% (user 3.63%, nice 0.00%, system 8.24%, iowait 0.70%, steal 1.12%)
server cpu = total 14.28% (user 1.42%, nice 0.00%, system 11.85%, iowait 0.00%, steal 1.01%)
client ram = 1241 MB
server ram = 444 MB
UDP :
bandwidth = 29.5 Mbit/s
client cpu = total 70.13% (user 6.76%, nice 0.00%, system 60.08%, iowait 0.42%, steal 2.87%)
server cpu = total 18.93% (user 2.07%, nice 0.00%, system 15.43%, iowait 0.00%, steal 1.43%)
client ram = 1238 MB
server ram = 446 MB
On voit tout de suite que les performances entre deux nœuds dans des datacenters différents sont bien plus faibles. Dès qu’on passe par le réseau internet, les performances sont divisées par 10.
J’imagine qu’avec un cluster complètement en cloud, les performances seraient bien meilleures.
Avertissement
Bien sûr, ces benchmarks sont à prendre avec des pincettes. Il faudrait créer un réel scénario de test pour évaluer les performances réseau en testant des applications réelles, la fiabilité des connexions, s’il n’y a pas de pertes de paquets, etc.
Ce n’est qu’un test de performance réseau brut, il ne faut donc pas en tirer de conclusions hâtives.
Conclusion
Talos est un vrai coup de cœur pour moi. Il est simple à prendre en main, et propose des fonctionnalités avancés. La documentation est très complète et la communauté semble plutôt active.
Les quelques défauts de l’utilitaire talosctl
sont largement compensés par talhelper
. J’ai hâte de voir les futures évolutions de Talos (j’ai d’ailleurs entendu dire que les katacontainers étaient en cours d’intégration à Talos 1.7).
Les possibilités de Talos sont encore nombreuses, et je suis loin d’avoir tout exploré (on pourrait par exemple parler de Omni, le SaaS de SideroLabs pour déployer des noeuds en PXE, ou encore de l’intégration de Talos avec certains clouds providers…).
En attendant de voir tout ça, je vais continuer à utiliser Talos pour mes projets personnels en espérant que la communauté grandisse et que le projet continue d’évoluer.