01Le dépôt, seule source de vérité
Tout l'état désiré de la plateforme vit dans un dépôt Git. Le poste d'administration, un bastion hors du cluster, en a une copie et détient la seule clé capable de déchiffrer les secrets.
k8s-proxmox/ ├── scripts/ # préparation Proxmox, bootstraps des clusters ├── tofu/ # OpenTofu : VM et configuration Talos │ ├── tofu.sh # lanceur : tunnel, passphrase, HA après apply │ ├── clusters.tf # les clusters demo et mgmt, le routeur │ └── modules/talos-cluster/ ├── ansible/ # routeur et serveur de sauvegarde ├── k8s/ # un dossier par composant : version + valeurs │ ├── cilium/ │ │ ├── version │ │ ├── values.yaml │ │ └── values-demo.yaml │ └── ... ├── secrets/secrets.sops.yaml └── renovate.json
Une convention simple rend le tout lisible par un humain et par Renovate : chaque composant
a son dossier sous k8s/, un fichier version d'une ligne, et ses
valeurs Helm, communes puis par cluster. Les scripts lisent la version dans ce fichier, Renovate
la met à jour, et personne n'a à chercher où elle est écrite.
Enfin, tous les scripts sont idempotents : les relancer réapplique l'état du dépôt sans rien casser. C'est aussi la façon de corriger une dérive, ce réglage « temporaire » fait à la main un soir de stress.
02Les secrets avec SOPS et age
Jetons d'API Proxmox et OVH, clé Ceph, mots de passe des consoles, clés S3 : tous les secrets sont dans un seul fichier YAML, chiffré par SOPS avec une clé age. Le fichier est versionné ; seules les valeurs sont chiffrées, les noms restent lisibles dans les diffs.
creation_rules:
- path_regex: secrets/.*\.sops\.yaml$
age: age1... # la clé publique age du bastion
Un secret généré pendant l'installation ne passe jamais par l'écran : il va directement dans SOPS. Et pour l'injecter dans Kubernetes, aucun fichier intermédiaire :
# générer un mot de passe et l'écrire dans SOPS, sans l'afficher sops set secrets/secrets.sops.yaml '["rancher"]["admin_password"]' \ "\"$(openssl rand -base64 24)\"" # le passer à Kubernetes, sans fichier temporaire SECRETS=secrets/secrets.sops.yaml kubectl -n cattle-system create secret generic bootstrap-secret \ --from-literal=bootstrapPassword="$(sops -d --extract '["rancher"]["admin_password"]' $SECRETS)" \ --dry-run=client -o yaml | kubectl apply -f -
Sans la clé privée age, plus aucun secret n'est lisible : ni pour vous, ni pour reconstruire la plateforme. Gardez-en une copie hors du bastion, dans un coffre de mots de passe. Un secret en clair dans Git, c'est la cape coincée dans la porte ; une clé age perdue, c'est la porte murée.
03Préparer Proxmox une fois
Quatre scripts préparent le cluster Proxmox, une seule fois : la zone SDN et ses VNets, le modèle de VM Talos, le modèle Debian du routeur, et le pool Ceph vu au chapitre précédent.
pvesh create /cluster/sdn/zones --zone k8s --type vxlan \ --peers 172.16.0.11,172.16.0.12,172.16.0.13,172.16.0.14 --mtu 8950 pvesh create /cluster/sdn/vnets --vnet k8s5000 --zone k8s --tag 5000 pvesh create /cluster/sdn/vnets --vnet k8s5001 --zone k8s --tag 5001 pvesh set /cluster/sdn # applique la configuration sur tous les noeuds
Le modèle Talos est une VM 9100 créée depuis l'image nocloud de l'Image Factory,
avec l'agent invité activé. OpenTofu la clonera pour chaque noeud.
04Les VM avec OpenTofu
OpenTofu s'appuie sur deux fournisseurs : bpg/proxmox pour les VM et
siderolabs/talos pour générer les secrets du cluster, appliquer la configuration
et amorcer etcd. Un module talos-cluster fait tout le travail ; chaque cluster se
résume à sa description :
module "demo" {
source = "./modules/talos-cluster"
name = "demo"
vnet = "k8s5001"
vip = "10.20.1.10"
talos_version = "v1.14.4"
kubernetes_version = "1.36.5"
nodes = {
"demo-cp1" = { role = "controlplane", host = "pve1", ip = "10.20.1.11" }
"demo-cp2" = { role = "controlplane", host = "pve2", ip = "10.20.1.12" }
"demo-cp3" = { role = "controlplane", host = "pve3", ip = "10.20.1.13" }
"demo-w1" = { role = "worker", host = "pve4", ip = "10.20.1.21", memory = 8192 }
"demo-w2" = { role = "worker", host = "pve1", ip = "10.20.1.22", memory = 8192 }
}
}
Le module clone le modèle, pose le CPU en x86-64-v3, le disque sur Ceph et la
carte réseau sur le VNet, puis génère et applique la configuration Talos de chaque noeud. Les
versions de Talos et de Kubernetes sont écrites ici en clair : Renovate saura les trouver.
Deux précautions complètent le tableau :
- L'état OpenTofu contient des secrets (certificats du cluster, jetons). Il est chiffré par OpenTofu lui-même (AES-GCM, clé dérivée d'une passphrase) et la passphrase est dans SOPS.
- L'API Proxmox n'est pas exposée au bastion : le lanceur
tofu.shouvre un tunnel SSH vers le port 8006 d'un noeud, injecte la passphrase depuis SOPS, lance OpenTofu, puis referme le tunnel.
05La configuration Talos
Talos 1.14 découpe la configuration d'une machine en documents spécialisés. Le module passe à chaque noeud une série de correctifs courts, plutôt qu'un gros fichier :
apiVersion: v1alpha1
kind: UnattendedInstallConfig
installer:
image: factory.talos.dev/nocloud-installer/<schematic>:v1.14.4
provisioning:
diskSelector:
match: disk.dev_path == "/dev/sda"
---
apiVersion: v1alpha1
kind: ResolverConfig
nameservers:
- address: 9.9.9.9
- address: 149.112.112.112
---
# pas de Flannel ni de kube-proxy : Cilium s'en charge
$patch: delete
apiVersion: v1alpha1
kind: KubeFlannelCNIConfig
---
apiVersion: v1alpha1
kind: KubeProxyConfig
enabled: false
---
# adresse virtuelle de l'API, portée par les control planes
machine:
network:
interfaces:
- deviceSelector: { physical: true }
mtu: 8950
vip: { ip: 10.20.1.10 }
Les clés historiques machine.install, cluster.proxy, cluster.network ou cluster.allowSchedulingOnControlPlanes sont refusées dès que le document dédié existe. Les exemples trouvés en ligne datent souvent d'avant ce découpage : si Talos rejette une configuration qui a l'air juste, c'est presque toujours ça.
Un dernier document mérite d'être signalé : KubeTalosAPIAccessConfig, qui
autorise des pods à appeler l'API Talos avec un rôle précis. Il servira au chapitre 06, pour
la sauvegarde d'etcd.
06Haute disponibilité et anti-affinité
Trois control planes sur le même hôte Proxmox, c'est une haute disponibilité de façade. Les VM sont donc confiées à la haute disponibilité de Proxmox, avec des règles d'anti-affinité : les control planes sur trois hôtes distincts, les workers sur deux.
for vm in 5001011 5001012 5001013 5001021 5001022; do ha-manager add vm:$vm --state started done ha-manager rules add resource-affinity k8s-demo-cp --affinity negative \ --resources vm:5001011,vm:5001012,vm:5001013 ha-manager rules add resource-affinity k8s-demo-workers --affinity negative \ --resources vm:5001021,vm:5001022
Le script est relancé automatiquement par tofu.sh après chaque
apply réussi : une VM ajoutée est protégée sans qu'on y pense.
07Lancer et vérifier
./tofu/tofu.sh plan
./tofu/tofu.sh apply
# récupérer les accès depuis les sorties OpenTofu
tofu -chdir=tofu output -raw demo_kubeconfig > ~/.kube/config
tofu -chdir=tofu output -raw demo_talosconfig > ~/.talos/config
kubectl get nodes
Les VM et Talos montent en trois minutes environ. Les noeuds restent alors en
NotReady : c'est normal, il n'y a pas encore de réseau des pods. Pas de panique,
le chapitre suivant s'en occupe.
Une plateforme Kubernetes, sans y laisser vos nuits
Nous concevons, montons et exploitons des plateformes Kubernetes sur Proxmox VE pour des équipes qui veulent se consacrer à leur code : mises à jour nocturnes, sauvegardes testées, déploiement en GitOps. Reprise d'un cluster existant, plateforme clé en main ou astreinte : nous intervenons au forfait comme au long cours.