Cluster API + Talos + Proxmox = ❤️
Si vous êtes des habitués de ce blog, vous savez déjà que je suis un énorme fan de Talos, une distribution minimaliste et sécurisée qui a pour seul but de faire tourner les composants de Kubernetes. Si j’en crois la date de publication de mon article sur ce sujet ça fait bientôt un an que j’en bouffe tous les jours (même si en réalité, j’ai commencé bien avant).
J’aime toujours autant ce projet, et je suis toujours aussi convaincu de son potentiel. Talos me semble être de-facto une des meilleures options pour déployer un Kubernetes en garantissant une sécurité maximale sans non-plus sacrifier la simplicité et la flexibilité.
Bref, pas besoin de vous refaire un topo sur les avantages que je lui trouve.
J’ai eu l’occasion de développer des extensions pour Talos, d’utiliser le SDK, de faire mumuse avec Omni (système pour gérer du Talos à grande échelle), de faire du Talos dans Talos dans Talos. J’ai également installé des clusters avec du baremetal OVH, des Raspberry Pi, des ZimaBoard, des VMs sur Proxmox, Openstack, AWS…
Aujourd’hui, je m’attaque à une autre facette de Talos : le provisionnement de clusters Kubernetes sur des VMs Proxmox via la Cluster API.
Je n’ai pas les compétences pour faire un article complet sur la Cluster API, je n’ai pas non-plus testé plusieurs providers ni plusieurs clouds. Dans cet article, je vais donc plutôt vous présenter mon cheminement pour déployer un cluster Talos sur Proxmox via la Cluster API, en essayant de détailler les étapes, les problèmes rencontrés et les solutions trouvées.
Mais déjà : qu’est-ce que la Cluster API ?
Cluster API
La Cluster API est un projet open-source qui vise à automatiser le déploiement, la configuration et la gestion de clusters Kubernetes.
Started by the Kubernetes Special Interest Group (SIG) Cluster Lifecycle, the Cluster API project uses Kubernetes-style APIs and patterns to automate cluster lifecycle management for platform operators. The supporting infrastructure, like virtual machines, networks, load balancers, and VPCs, as well as the Kubernetes cluster configuration are all defined in the same way that application developers operate deploying and managing their workloads.
Si on résume, la Cluster API (qu’on va abréger en CAPI dans cet article) permet d’utiliser des ressources Kubernetes pour décrire et gérer des clusters Kubernetes… depuis un cluster Kubernetes ! On profite alors de toutes les fonctionnalités de Kubernetes : boucle de réconciliation, contrôle d’accès, gestion des secrets, etc.
Ainsi, nous allons avoir un cluster de management qui va se charger de créer des clusters de workload.
Les composants de la CAPI sont les suivants :
- Déjà, le Cluster-API Controller, qui est le composant principal de la CAPI ;
- Le Bootstrap Provider, responsable de la génération des certificats et des configurations nécessaires pour créer le cluster. C’est aussi lui qui va piloter les workers pour qu’ils rejoignent le cluster ;
- Le Infrastructure Provider, qui va gérer les ressources nécessaires pour le cluster. C’est lui qui va créer les VMs, les réseaux, les disques, etc. Bref : piloter le cloud ou l’hyperviseur ;
- Le IPAM Provider qui va attribuer les adresses IP aux machines du cluster (en fonction des besoins et des contraintes de l’Infrastructure Provider) ;
- Et enfin le Control Plane Provider, qui s’assure que les control plane sont bien configurés et fonctionnels (ceux-ci peuvent être des VMs, ou managés par un service cloud).
Je vous vois venir :
Mais sur quelles plateformes est-ce que je peux utiliser la CAPI ?
La CAPI est extensible et il existe des providers (un peu comme pour Pulumi, ou Terraform) pour de nombreux environnements : AWS, Azure, GCP, OpenStack, Hetzner, vcluster, etc.
Pour commencer à jouer avec la CAPI, on doit d’abord installer le client en ligne de commande clusterctl
:
Dans l’idée, on va piocher les composants nécessaires pour notre environnement. Je vais donc faire ma liste de courses pour mon projet Proxmox + Talos :
- Bootstrap Provider : Celui de Talos (pré-configuré dans CAPI) ;
- Infrastructure Provider : J’utilise le projet de IONOS (1&1) qui intègre Proxmox (également pré-configuré dans CAPI) ;
- IPAM Provider : Je vais utiliser le projet in-cluster pour gérer nous-même les IP (encore une fois, pré-configuré dans CAPI) ;
- Last but not least, le Control Plane Provider sera celui de Talos (à votre avis, pré-configuré dans CAPI ou pas ?).
Je lève également une petite alerte sur le fait que notre clusterctl
va par défaut utiliser les dernières releases de chaque projet Github (et donc, potentiellement, des versions instables), pas glop tout ça. On peut ainsi spécifier des versions précises dans le fichier de configuration présent ~/.cluster-api/clusterctl.yaml
.
providers:
- name: "talos"
url: "https://github.com/siderolabs/cluster-api-bootstrap-provider-talos/releases/download/v0.6.7/bootstrap-components.yaml"
type: "BootstrapProvider"
- name: "talos"
url: "https://github.com/siderolabs/cluster-api-control-plane-provider-talos/releases/download/v0.5.8/control-plane-components.yaml"
type: "ControlPlaneProvider"
- name: "proxmox"
url: "https://github.com/ionos-cloud/cluster-api-provider-proxmox/releases/download/v0.6.2/infrastructure-components.yaml"
type: "InfrastructureProvider"
Ok ! On a nos composants, il nous manque quoi ?
Cluster de Management
Comme dit en introduction, on va avoir besoin d’un premier cluster pour gérer les autres. Je vais alors créer un cluster mono-noeud rapidos (et je vais le faire sur mon cluster Proxmox actuel). J’aurais pu utiliser un cluster éphémère sous KIND ou talosctl cluster create
mais pour les besoins de l’article, je vais quand même déployer un cluster basé sur des machines virtuelles.
Si vous voulez suivre un guide pour déployer un Talos, je vous invite à suivre mon article sur le sujet (il est un peu vieux, mais les bases sont là).
Je démarre alors une ISO Talos sur mon Proxmox.
On génère la configuration pour le cluster de management :
talosctl gen secrets
talosctl gen config capi https://192.168.1.6:6443
Je vais également modifier le champ cluster.allowSchedulingOnControlPlanes
pour le mettre à true
dans le fichier controlplane.yaml
. L’idée est d’autoriser les pods à se scheduler sur le control plane (et heureusement qu’on a un seul noeud).
Une fois le fichier modifié, on peut terminer la configuration du cluster de management :
$ talosctl bootstrap -n 192.168.1.6 -e 192.168.1.6 --talosconfig ./talosconfig
$ talosctl kubeconfig -n 192.168.1.6 -e 192.168.1.6 --talosconfig ./talosconfig --merge=false
$ kubectl --kubeconfig kubeconfig get nodes
NAME STATUS ROLES AGE VERSION
talos-gag-kjo Ready control-plane 32s v1.31.2
On a notre cluster de management, on peut passer à la suite.
Configurer Proxmox
Pour déployer des VMs, nous allons devoir fournir les accès d’une certaine manière à la CAPI (sinon on n’ira pas bien loin). Je dédie donc un utilisateur sur mon Proxmox pour gérer les machines depuis notre cluster.
# Dans Proxmox
$ pveum user add capmox@pve
$ pveum aclmod / -user capmox@pve -role PVEVMAdmin
$ pveum user token add capmox@pve capi -privsep
┌──────────────┬──────────────────────────────────────┐
│ key │ value │
╞══════════════╪══════════════════════════════════════╡
│ full-tokenid │ capmox@pve!capi │
├──────────────┼──────────────────────────────────────┤
│ info │ {"privsep":"0"} │
├──────────────┼──────────────────────────────────────┤
│ value │ 918a2c6d-8f30-47i7-8d46-e72c2a882ec8 │
└──────────────┴──────────────────────────────────────┘
Avec ça, je peux modifier mon fichier de configuration de la CAPI pour ajouter les informations de connexion à Proxmox :
# ~/.cluster-api/clusterctl.yaml
PROXMOX_URL: "https://192.168.1.182:8006"
PROXMOX_TOKEN: 'capmox@pve!capi'
PROXMOX_SECRET: "918a2c6d-8f30-47i7-8d46-e72c2a882ec8"
Information
Si vous n’aimez pas les fichiers de configuration, vous pouvez aussi utiliser des variables d’environnement (e.g. export PROXMOX_URL="https://192.168.1.182:8006" PROXMOX_TOKEN: 'capmox@pve!capi'
etc).
On peut maintenant installer CAPI ainsi que nos différents providers :
$ clusterctl init --infrastructure proxmox --ipam in-cluster --control-plane talos --bootstrap talos
$ kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
cabpt-system cabpt-controller-manager-6dcd86fd55-6q4nh 1/1 Running 0 6m1s
cacppt-system cacppt-controller-manager-7757bc6849-grm6b 1/1 Running 0 6m
capi-ipam-in-cluster-system capi-ipam-in-cluster-controller-manager-8696b5d999-kmkxm 1/1 Running 0 5m59s
capi-system capi-controller-manager-59c7f9c475-fx2jb 1/1 Running 0 6m1s
capmox-system capmox-controller-manager-674bdf77bd-8tpzw 1/1 Running 0 5m59s
cert-manager cert-manager-74b56b6655-rt6cc 1/1 Running 0 19h
cert-manager cert-manager-cainjector-55d94dc4cc-qk7h4 1/1 Running 0 19h
cert-manager cert-manager-webhook-564f647c66-dkfrm 1/1 Running 0 19h
kube-system coredns-b588ffbd5-rsbhl 1/1 Running 0 20h
kube-system coredns-b588ffbd5-x2j7g 1/1 Running 0 20h
kube-system kube-apiserver-talos-gag-kjo 1/1 Running 0 20h
kube-system kube-controller-manager-talos-gag-kjo 1/1 Running 2 (20h ago) 20h
kube-system kube-flannel-b5wpl 1/1 Running 1 (20h ago) 20h
kube-system kube-proxy-jmlkw 1/1 Running 1 (20h ago) 20h
kube-system kube-scheduler-talos-gag-kjo 1/1 Running 3 (20h ago) 20h
Oui, un dodo et une journée de travail se sont écoulés entre la création du cluster de management et l’installation de CAPI.
Ok, on a la même base maintenant, on va pouvoir continuer en pairing.
Objectif : Un ControlPlane !
Commençons simple, je veux juste un control-plane pour démarrer. Une première étape serait de détailler les ressources que l’on veut pour notre cluster. Alors, de quoi a-t-on besoin pour déployer un control-plane Talos sur Proxmox ?
De la définition d’un cluster !
C’est un bon début, on va d’abord commencer par créer un cluster avant d’y ajouter des machines. Pour cela, je m’appuie de la documentation de CAPI pour comprendre qu’il me faut une ressource de type Cluster
. Celle-ci possède également une dépendance sur une autre ressource ProxmoxCluster
Dans cet objet ProxmoxCluster
, on va définir les paramètres de notre cluster Kubernetes, c.a.d. configurer les adresses IP, la gateway, les DNS, que les noeuds utiliseront etc. mais surtout un endpoint de l’API-Server d’un control-plane.
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxCluster
metadata:
name: proxmox-cluster
namespace: default
spec:
allowedNodes:
- homelab-proxmox-02
controlPlaneEndpoint:
host: 192.168.1.220
port: 6443
dnsServers:
- 8.8.8.8
- 8.8.4.4
ipv4Config:
addresses:
- 192.168.1.210-192.168.1.219
gateway: 192.168.1.254
prefix: 24
On peut ensuite créer notre Cluster
. Ici, on voit bien que l’on fait référence à notre ProxmoxCluster
(provider Proxmox) coté infrastructureRef
et à notre TalosControlPlane
(provider Talos) coté controlPlaneRef
(il n’existe pas encore, mais à la vitesse où on va, il ne va pas tarder).
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: coffee-cluster
namespace: default
spec:
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
name: talos-cp # Existe pas encore
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxCluster
name: proxmox-cluster
So far, so good. On a nos ressources bien présentes dans le cluster.
$ kubectl get cluster,proxmoxcluster
NAME CLUSTERCLASS PHASE AGE VERSION
cluster.cluster.x-k8s.io/coffee-cluster Provisioning 102s
NAME CLUSTER READY ENDPOINT
proxmoxcluster.infrastructure.cluster.x-k8s.io/proxmox-cluster {"host":"192.168.1.220","port":6443}
Trop facile 😎, plus qu’à créer notre ControlPlane…
On précise bien que le ControlPlane est une machine gérée par Proxmox (once again, on mélange la configuration de l’infrastructure et des machines). On peut également ajouter des paramètres supplémentaires pour influer sur la configuration Talos que le provider va générer (ici, on ne précise que le type de la machine, mais on pourrait aller plus loin).
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
metadata:
name: talos-cp
spec:
version: v1.32.0
replicas: 1
infrastructureTemplate:
kind: ProxmoxMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
name: control-plane-template
namespace: default
controlPlaneConfig:
controlplane:
generateType: controlplane
On applique, on relance… Il ne reste plus que le ProxmoxMachineTemplate
à créer pour enfin voir notre ControlPlane démarrer.
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxMachineTemplate
metadata:
name: control-plane-template
namespace: default
spec:
template:
spec:
disks:
bootVolume:
disk: scsi0
sizeGb: 20
format: qcow2
full: true
memoryMiB: 2048
network:
default:
bridge: vmbr0
model: virtio
numCores: 2
numSockets: 1
sourceNode: homelab-proxmox-02
templateID: ??? # A remplir
Comme pour les providers Terraform quand on ne fait pas de Cloud-Init, on va devoir créer une machine ’template’ que l’on dupliquera pour chaque machine du cluster. Comme nous utilisons Talos, il n’est pas nécessaire de booter cette machine pour l’installer, il suffit juste que la machine soit créée, l’image (ISO) de Talos comme disque bootable et surtout que les qemu-guest-agent soient activés coté Proxmox.
Il n’est pas non-plus nécessaire d’être très attentif aux ressources que l’on alloue à cette machine. CAPI les modifiera en fonction de ce qu’on a défini dans nos ressources comme montré plus haut.
Pour récupérer l’ISO de Talos, on peut la télécharger depuis Factory. Dans notre cas, comme nous sommes sur des VMs classiques, on peut sélectionner “Bare-metal Machine” qui correspond aux machines standards (virtuelles ou physiques).
Activer les Qemu Guest Agent sur Proxmox :
J’obtiens alors la machine possédant l’ID 124 ! Je peux alors remplir le champ .spec.template.spec.templateID
de mon ProxmoxMachineTemplate
.
Maintenant, notre cluster possède ce qu’il faut pour déployer une première machine, elle peut démarrer à tout moment 😁 !
Et là, c’est long… très long…
Y’a un truc qui coince, checkons les logs du provider Proxmox.
I0131 16:44:24.941681 1 proxmoxmachine_controller.go:187] "Bootstrap data secret reference is not yet available" controller="proxmoxmachine" controllerGrou
I0131 16:44:24.990591 1 find.go:63] "vmid doesn't exist yet" controller="proxmoxmachine" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="
E0131 16:44:25.060475 1 proxmoxmachine_controller.go:209] "error reconciling VM" err="cannot reserve 2147483648B of memory on node homelab-proxmox-02: 0B a
E0131 16:44:25.119065 1 controller.go:324] "Reconciler error" err="failed to reconcile VM: cannot reserve 2147483648B of memory on node homelab-proxmox-02:
J’aurais pu attendre longtemps 😅 ! À savoir que 2147483648B = 2GiB, et que j’ai largement la quantité de mémoire disponible sur mon nœud Proxmox.
Beh, direction les issues github alors… ⌛
En creusant un peu, je suis tombé sur ce message :
By default our scheduler only allows to allocate as much memory to guests as the host has. This might not be a desirable behaviour in all cases. For example, one might to explicitly want to overprovision their host’s memory, or to reserve bit of the host’s memory for itself.
Parce que je n’ai plus rien à perdre, je modifie la configuration de mon ProxmoxCluster
pour ajouter un schedulerHint
qui va permettre de over-provisionner la mémoire.
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxCluster
metadata:
name: proxmox-cluster
namespace: default
spec:
+ schedulerHints:
+ memoryAdjustment: 0
allowedNodes:
- homelab-proxmox-02
controlPlaneEndpoint:
host: 192.168.1.220
port: 6443
dnsServers:
- 8.8.8.8
- 8.8.4.4
ipv4Config:
addresses:
- 192.168.1.210-192.168.1.219
gateway: 192.168.1.254
prefix: 24
Y’a aucune raison que ça marche, mais je tente quand même.
Bonjour, toi !
C’est une avancée incroyable !! 🎉 Mais il y a comme un truc qui cloche 🤔, une incohérence entre les labels que CAPI a ajoutés à la machine et ce que me dit l’écran de la VM.
Ce n’est pas la bonne adresse IP, et il ne se passe plus grand-chose coté CAPI.
E0131 17:04:39.039618 1 controller.go:324] "Reconciler error" err="failed to reconcile VM: error waiting for agent: error waiting for agent: the operat │
│ ion has timed out" controller="proxmoxmachine" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="ProxmoxMachine" ProxmoxMachine="default/cont │
│ rol-plane-template-4jtgp" namespace="default" name="control-plane-template-4jtgp" reconcileID="294544f4-4de0-4a91-b686-47a0d51a1f2a"
Qu’est-ce que c’est que ce binz ?
Ma théorie est que CAPI n’arrive pas à configurer l’adresse IP. Je vois deux manières qu’il aurait pu utiliser pour le faire :
- Soit, obtenir l’adresse IP et lui envoyer sa configuration en paramétrant l’adresse IP dans la configuration Talos ;
- Soit, lui paramétrer son adresse IP par Cloud-Init (et en profiter pour lui donner sa configuration).
Bref, quelque chose déconne et ça semble lié au fait que l’adresse IP ne soit pas correctement configurée. Procédons par élimination : je suis plutôt sûr que mon Proxmox n’est pas en cause, ma configuration semble correspondre à ce que CAPI attend, il ne reste qu’un potentiel problème : Talos.
Le setup Proxmox-Talos serait particulier ? J’ai pourtant déployé de nombreux clusters Talos sous Proxmox sans rencontrer de problème. La réponse est en fait assez simple : CAPI demande à Proxmox de paramétrer l’adresse IP de la machine à travers Cloud-Init, mais Talos ne le supporte pas lorsqu’on utilise l’image ISO “Bare-metal Machine” (qui est celle que j’ai utilisée).
Il fallait utiliser l’image “nocloud” (celle utilisée pour les clouds/hyperviseurs classiques) :
Qui aurait pu se douter que l’image ‘cloud’ était la seule compatible avec cloud-init ?
Je modifie la template de ma machine pour utiliser l’image “nocloud” et je relance le déploiement.
Ça démarre, on voit que la machine récupère sa configuration IP, qu’elle a bien rejoint le cluster (puisqu’on peut voir le nom du cluster sur la console), mais elle crash et redémarre en boucle.
On peut apercevoir le message d’erreur suivant :
29.285667 [talos] controller runtime finished
29.287109 [talos] error running phase 2 in install sequence: task 1/1: fail task "upgrade" failed: exit code 1
29.2884421 [talos] no system disk found, nothing to
On pensait que c’était gagné, mais il semblerait que non. Mais ça tombe bien, j’ai une petite idée de ce qui ne va pas : Talos ne sait absolument pas quel disque utiliser. Pour confirmer ça, je peux directement aller lire la configuration de la machine, non pas via l’API de Talos (la machine n’est pas assez longtemps démarrée pour ça), mais directement depuis Kubernetes où la configuration Talos est stockée.
Le champ “disk” est vide, ce qui explique pourquoi Talos ne sait pas où installer le système. Pour pallier à ça, on peut modifier cette configuration via l’objet TalosControlPlane
.
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
metadata:
name: talos-cp
spec:
version: v1.32.0
replicas: 1
infrastructureTemplate:
kind: ProxmoxMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
name: control-plane-template
namespace: default
controlPlaneConfig:
controlplane:
generateType: controlplane
+ strategicPatches:
+ - |
+ - op: replace
+ path: /machine/install
+ value:
+ disk: /dev/sda
On applique, on croise les doigts, et on attend.
Incroyable, ça a marché ! 😄 On a notre ControlPlane qui tourne sur Proxmox. On retrouve des logs traditionnels de Talos nous demandant de bootstrap le cluster.
Dans un contexte normal, on aurait dû lancer la commande talosctl bootstrap
. Dans un déploiement CAPI, cette étape est censée être automatisée. En listant le status de notre objet ‘ProxmoxMachine’, on voit qu’il est toujours en “pending”.
# kubectl describe proxmoxmachine control-plane-template-n62jk
Name: control-plane-template-n62jk
Namespace: default
API Version: infrastructure.cluster.x-k8s.io/v1alpha1
Kind: ProxmoxMachine
# ...
Status:
Addresses:
Address: control-plane-template-n62jk
Type: Hostname
Address: 192.168.1.210
Type: InternalIP
Bootstrap Data Provided: true
Ip Addresses:
net0:
ipv4: 192.168.1.210
Proxmox Node: homelab-proxmox-02
Vm Status: pending
Events: <none>
Oh shit, here we go again.
Bon, c’est quoi le problème maintenant ? Regardons les logs du pod du provider Proxmox :
E0131 19:27:13.724453 1 controller.go:324] "Reconciler error" err="failed to reconcile VM: error waiting for agent: error waiting for agent: the operation has timed out" controller="proxmoxmachine" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="ProxmoxMachine" ProxmoxMachine="default/control-plane-template-n62jk" namespace="default" name="control-plane-template-n62jk" reconcileID="d6d0983
3-8d38-4518-bd49-b94522f560bd"
L’erreur error waiting for agent
ne peut pas venir de n’importe où : ça doit forcément être lié aux Qemu-guest-agent (CAPI doit faire des requêtes à l’hyperviseur pour vérifier si la machine est conforme).
En effet, les QGAs ne semblent pas être fonctionnels. Pour corriger ça, on peut encore passer par un patch dans la configuration Talos pour demander l’installation de l’extension nécessaire.
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
metadata:
name: talos-cp
spec:
version: v1.32.0
replicas: 1
infrastructureTemplate:
kind: ProxmoxMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
name: capi-quickstart-control-plane
namespace: default
controlPlaneConfig:
controlplane:
generateType: controlplane
strategicPatches:
- |
- op: replace
path: /machine/install
value:
disk: /dev/sda
+ extensions:
+ - image: ghcr.io/siderolabs/qemu-guest-agent:9.2.0
Machine redéployée et… on est toujours en “pending” :
# ...
Status:
Addresses:
Address: control-plane-template-hcthr
Type: Hostname
Address: 192.168.1.210
Type: InternalIP
Bootstrap Data Provided: true
Conditions:
Last Transition Time: 2025-01-31T19:47:42Z
Reason: PoweringOn
Severity: Info
Status: False
Type: Ready
Last Transition Time: 2025-01-31T19:47:42Z
Reason: PoweringOn
Severity: Info
Status: False
Type: VMProvisioned
Ip Addresses:
net0:
ipv4: 192.168.1.210
Proxmox Node: homelab-proxmox-02
Vm Status: pending
Events: <none>
Retournons voir les logs du provider pour comprendre cette nouvelle erreur.
E0131 19:51:41.225099 1 controller.go:324] "Reconciler error" err="failed to reconcile VM: unable to get cloud-init status: no pid returned from agent exec command" controller="proxmoxmachine" controllerGroup="infrastructure.cluster.x-k8s.io" controllerKind="ProxmoxMachine" ProxmoxMachine="default/control-plane-template-hcthr" namespace="default" name="control-plane-template-hcthr" reconcileID="099e3e4a-64ca-4b3f-8fb5-be280e3de35e"
no pid returned from agent exec command
signifie que CAPI essaye de lancer une commande bash sur la machine via les QGAs.
Essayer de lancer un terminal dans Talos ? You fool !
Pour une fois, la solution n’est pas dans la configuration de Talos, mais plutôt dans celle du provider Proxmox. On va le modifier comme ceci pour demander à ne pas vérifier le status de la machine via Cloud-Init.
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxMachineTemplate
metadata:
name: control-plane-template
namespace: default
spec:
template:
spec:
disks:
bootVolume:
disk: scsi0
sizeGb: 20
format: qcow2
full: true
memoryMiB: 2048
network:
default:
bridge: vmbr0
model: virtio
numCores: 2
numSockets: 1
sourceNode: homelab-proxmox-02
templateID: 124
+ checks:
+ skipCloudInitStatus: true
Je relance… ON EST ENFIN EN “READY” ! 🎉
Status:
# ...
Ip Addresses:
net0:
ipv4: 192.168.1.210
Proxmox Node: homelab-proxmox-02
Ready: true
Vm Status: ready
Events: <none>
On peut maintenant essayer de déployer d’autres control-planes en modifiant le nombre de replicas dans la configuration du TalosControlPlane
.
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
metadata:
name: talos-cp
spec:
version: v1.32.0
- replicas: 1
+ replicas: 3
infrastructureTemplate:
kind: ProxmoxMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
name: control-plane-template
namespace: default
controlPlaneConfig:
controlplane:
generateType: controlplane
strategicPatches:
- |
- op: replace
path: /machine/install
value:
disk: /dev/sda
extensions:
- image: ghcr.io/siderolabs/qemu-guest-agent:9.2.0
En quelques secondes, on obtient deux nouvelles machines qui tournent sur Proxmox.
Il reste quand même un petit ‘hic’ : on a défini que les machines devaient rejoindre le cluster dont l’API-Server est exposée sur l’adresse 192.168.1.220
. Mais aucune machine ne correspond à cette IP, donc aucune machine n’arrive à rejoindre le cluster. De plus, si on attribue manuellement cette adresse à une machine : elle deviendra un SPOF pour notre cluster (spoiler: c’est pas bien).
Normalement, dans un contexte Cloud, c’est à ce moment qu’on ajoute un LoadBalancer pour exposer le port 6443 de nos control-planes à travers une adresse IP qui n’est pas attachée à une machine. En environnement on-prem, on peut utiliser un service comme KeepAlived ou du HAProxy.
Mais c’est sans compter sur Talos (❤️) qui propose une solution pour établir une VIP pour joindre l’API-Server (justement ce qu’on cherche à faire).
apiVersion: controlplane.cluster.x-k8s.io/v1alpha3
kind: TalosControlPlane
metadata:
name: talos-cp
spec:
version: v1.32.0
replicas: 3
infrastructureTemplate:
kind: ProxmoxMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
name: control-plane-template
namespace: default
controlPlaneConfig:
controlplane:
generateType: controlplane
strategicPatches:
- |
- op: replace
path: /machine/install
value:
disk: /dev/sda
extensions:
- image: ghcr.io/siderolabs/qemu-guest-agent:9.2.0
+ - op: add
+ path: /machine/install/extraKernelArgs
+ value:
+ - net.ifnames=0
+ - op: add
+ path: /machine/network/interfaces
+ value:
+ - interface: eth0
+ dhcp: false
+ vip:
+ ip: 192.168.1.220
Pour détailler un peu plus, on ajoute l’option net.ifnames=0
(1er patch) au kernel pour que les interfaces réseau soient nommées eth0
, eth1
, et deviennent prédictibles (sinon, on a un nommage dynamique). Une fois qu’on a le nom de l’interface réseau, on peut y ajouter une adresse IP virtuelle (2ème patch) qui sera utilisée pour joindre l’API-Server.
Au démarrage de la première machine, celle-ci va récupérer l’adresse IP 192.168.1.220
(en plus de son actuelle adresse).
Grâce à ça, les machines rejoignent automatiquement le cluster sans qu’on ait à s’en occuper (et l’étape de bootstrap est automatiquement réalisée).
Ajouter des workers
On peut désormais passer à la suite : déployer des workers ! Comme c’est assez straightforward, je peux directement vous donner les manifests.
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
name: machinedeploy-workers
namespace: default
spec:
clusterName: coffee-cluster
replicas: 3
selector:
matchLabels: null
template:
spec:
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
kind: TalosConfigTemplate
name: talosconfig-workers
clusterName: coffee-cluster
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxMachineTemplate
name: worker-template
version: v1.32.0
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: ProxmoxMachineTemplate
metadata:
name: worker-template
namespace: default
spec:
template:
spec:
disks:
bootVolume:
disk: scsi0
sizeGb: 20
format: qcow2
full: true
memoryMiB: 2048
network:
default:
bridge: vmbr0
model: virtio
numCores: 2
numSockets: 1
sourceNode: homelab-proxmox-02
templateID: 124
checks:
skipCloudInitStatus: true
---
apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3
kind: TalosConfigTemplate
metadata:
name: talosconfig-workers
spec:
template:
spec:
generateType: worker
talosVersion: v1.9
configPatches:
- op: replace
path: /machine/install
value:
disk: /dev/sda
En tenant compte de ce que l’on a appris durant le déploiement des control-planes, c’est un jeu d’enfant.
Voilà, on a un cluster Kubernetes complet déployé sur Proxmox avec Talos et la Cluster API !
Récupérer les accès
Si on déploie un cluster, c’est quand même pour pouvoir l’utiliser, non ? Alors voyons comment récupérer les accès à notre cluster.
Que ça soit pour le KubeConfig ou pour le TalosConfig, ça se passe de la même manière : nous avons des secrets qui ont été générés par CAPI et que l’on peut récupérer. Le secret contenant le KubeConfig est sous la forme <cluster-name>-kubeconfig
et le secret contenant le TalosConfig est sous la forme <cluster-name>-talosconfig
.
Commençons par le KubeConfig :
$ kubectl get secret coffee-cluster-kubeconfig -o yaml | yq .data.value | base64 -d > kubeconfig
$ kubectl --kubeconfig kubeconfig get nodes
NAME STATUS ROLES AGE VERSION
control-plane-template-d4kfb Ready control-plane 10m v1.32.0
control-plane-template-hhrgq Ready control-plane 10m v1.32.0
control-plane-template-s4gwg Ready control-plane 10m v1.32.0
machinedeploy-workers-h87sj-dp799 Ready <none> 56s v1.32.0
machinedeploy-workers-h87sj-kjg64 Ready <none> 58s v1.32.0
machinedeploy-workers-h87sj-z7cs2 Ready <none> 57s v1.32.0
Puis, le TalosConfig :
$ kubectl get secret coffee-cluster-talosconfig -o yaml | yq .data.talosconfig | base64 -d > talosconfig
$ talosctl --talosconfig talosconfig -n 192.168.1.220 version
Client:
Tag: v1.8.3
SHA: 6494aced
Built:
Go version: go1.22.9
OS/Arch: darwin/arm64
Server:
NODE: 192.168.1.220
Tag: v1.9.3
SHA: d40df438
Built:
Go version: go1.23.5
OS/Arch: linux/amd64
Enabled: RBAC
Prêt à l’emploi !
Conclusion
Il s’agissait là de mon premier contact avec la Cluster API, et malgré les quelques difficultés rencontrées, j’ai trouvé ça très intéressant de réussir à faire fonctionner ensemble ces composants qui n’ont à priori pas été conçus initialement pour ça.
Pour rendre tout ça plus clair, j’ai résumé les liens entre les différentes ressources grâce à ce schéma :
Si vous voulez retrouver les manifestes utilisés dans cet article, vous pouvez les retrouver ici.
Comme pour tout projet utilisant des providers tiers, sa complexité est directement liée à la qualité de la documentation. J’ai quelques fois dû piocher des informations dans le code, et surtout dans les descriptions des ressources via kubectl explain
pour comprendre comment les ressources étaient liées entre elles, ainsi que le contenu des champs.
J’ai encore d’autres choses à tester avec la CAPI, notamment l’intégration d’un programme gérant l’auto-scaling (ou même karpenter), l’installation d’applications depuis le cluster de management, ou encore l’usage des ClusterClass.
Je suis sûr que nous en reparlerons bientôt !
Bon kawa ☕ !