Déployer Kubernetes avec Kubespray : guide Rocky Linux 9 et Cilium
EdwardMoon
Ce guide construit un cluster Kubernetes reproductible sous Rocky Linux 9 avec Kubespray et Ansible, puis valide trois nœuds de contrôle, le quorum etcd, une VIP pour l'API et le réseau Cilium. L'objectif est de vérifier la résistance aux pannes et aux mises à niveau, au-delà de la simple réussite des commandes d'installation.
Kubernetes 1.31.6, utilisé dans l'ancien article, n'est plus pris en charge depuis novembre 2025. Cette révision retient les versions fournies par Kubespray 2.31.0 en juillet 2026 : Kubernetes 1.35.4 et Cilium 1.19.3. Avant le déploiement, vérifiez les sommes de contrôle du tag Kubespray choisi et la matrice de compatibilité Kubernetes de Cilium.

Versions et périmètre de prise en charge
| Composant | Référence de ce guide | Motif et points d'attention |
|---|---|---|
| Rocky Linux | Derniers correctifs 9.x | Aligner les versions mineures, les noyaux et la synchronisation horaire des nœuds |
| Kubespray | Tag de version v2.31.0 | Fixer le tag et les dépendances pour la reproductibilité |
| Kubernetes | v1.35.4 | Version par défaut de Kubespray 2.31.0 ; version mineure prise en charge |
| Cilium | v1.19.3 | Version fournie par Kubespray 2.31.0 |
| containerd | Valeur par défaut Kubespray | Utiliser la combinaison testée par la version 2.31.0 |
| etcd | Trois membres | Conserve une majorité après la panne d'un nœud |
Topologie de déploiement
| Hôte | IP d'exemple | Rôle |
|---|---|---|
| ansible01 | 10.20.0.5 | Exécute Kubespray et conserve l'inventaire et les artefacts |
| api.k8s.example.com | 10.20.0.10 | VIP HAProxy/répartiteur externe :6443 |
| cp01~cp03 | 10.20.0.11~13 | kube_control_plane + etcd |
| wk01~wk03 | 10.20.0.21~23 | kube_node |
| backup01 | 10.20.0.30 | Snapshots etcd chiffrés et sauvegardes de l'inventaire |
Fournissez la VIP de l'API avec au moins deux proxys et VRRP, ou un répartiteur existant, plutôt qu'une instance HAProxy unique. Transférez le trafic et les contrôles de santé du port 6443 vers kube-apiserver sur chaque nœud de contrôle. Finalisez d'abord le plan d'adressage : les CIDR des nœuds, Pods et Services ne doivent pas chevaucher les réseaux d'entreprise, VPN ou de stockage.
Vérifier la résolution de noms et les ports
getent hosts api.k8s.example.com cp01 cp02 cp03 wk01 wk02 wk03
for host in cp01 cp02 cp03 wk01 wk02 wk03; do
printf '%-6s ' "$host"
timeout 3 bash -c "</dev/tcp/${host}/22" && echo SSH_OK || echo SSH_FAIL
done
timeout 3 bash -c '</dev/tcp/api.k8s.example.com/6443' && echo API_VIP_OK || echo API_VIP_FAIL
Vérifications préalables sur les nœuds Rocky Linux 9
Kubespray gère containerd, kubelet, les modules du noyau et sysctl : n'exécutez pas d'autres scripts d'installation en amont. Auditez le système, le CPU, la mémoire, les disques, les cgroups, la synchronisation horaire et les éventuels runtimes de conteneurs déjà installés sur chaque nœud.
cat /etc/rocky-release
uname -r
timedatectl status
free -h
lsblk -f
findmnt -no FSTYPE,OPTIONS / /var
stat -fc %T /sys/fs/cgroup
swapon --show
rpm -qa | grep -E 'kube(let|adm|ctl)|containerd|docker|cri-o' || true
Kubernetes 1.35 prend cgroup v2 comme référence. Conservez la configuration systemd/cgroup v2 de Rocky Linux 9 au lieu d'utiliser d'anciens contournements pour cgroup v1. Ne désactivez pas systématiquement SELinux et firewalld ; vérifiez la configuration prise en charge dans les notes de version Kubespray et les ACL réseau de votre organisation.
Utiliser un compte d'administration dédié et enregistrer les clés hôtes SSH
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/kubespray_ed25519
for host in cp01 cp02 cp03 wk01 wk02 wk03; do
ssh-copy-id -i ~/.ssh/kubespray_ed25519.pub "ansible@${host}"
ssh-keyscan -H "$host" >> ~/.ssh/known_hosts.new
done
sort -u ~/.ssh/known_hosts.new >> ~/.ssh/known_hosts
rm -f ~/.ssh/known_hosts.new
chmod 0600 ~/.ssh/known_hosts
Comparez les résultats de ssh-keyscan aux empreintes d'une console fiable ou du système de gestion des actifs avant de les enregistrer. Désactiver StrictHostKeyChecking empêche la détection d'une interception. Accordez au compte ansible uniquement les droits sudo nécessaires et interdisez la connexion SSH directe de root.
Fixer l'environnement d'exécution Kubespray 2.31.0
Fixez Kubespray sur un tag de version plutôt que sur main. Un environnement virtuel Python et requirements.txt séparent les dépendances Ansible du Python système et rendent le contrôleur reproductible.
sudo dnf install -y git python3.12 python3.12-pip
git clone --branch v2.31.0 --depth 1 https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
git verify-tag v2.31.0 || git show --show-signature --no-patch v2.31.0
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --requirement requirements.txt
python --version
ansible --version
git describe --tags --always
La vérification des signatures Git n'a de valeur que si la clé du mainteneur de confiance a été vérifiée par un canal indépendant. Pour un environnement isolé, vérifiez en ligne les sommes de contrôle et la provenance des archives, des wheels Python et des images de conteneurs avant de les importer dans les dépôts approuvés.
Créer l'inventaire Kubespray
inventory/prod/inventory.yml
cp -a inventory/sample inventory/prod
cat > inventory/prod/inventory.yml <<'YAML'
all:
hosts:
cp01: {ansible_host: 10.20.0.11, ip: 10.20.0.11, access_ip: 10.20.0.11}
cp02: {ansible_host: 10.20.0.12, ip: 10.20.0.12, access_ip: 10.20.0.12}
cp03: {ansible_host: 10.20.0.13, ip: 10.20.0.13, access_ip: 10.20.0.13}
wk01: {ansible_host: 10.20.0.21, ip: 10.20.0.21, access_ip: 10.20.0.21}
wk02: {ansible_host: 10.20.0.22, ip: 10.20.0.22, access_ip: 10.20.0.22}
wk03: {ansible_host: 10.20.0.23, ip: 10.20.0.23, access_ip: 10.20.0.23}
children:
kube_control_plane:
hosts: {cp01: {}, cp02: {}, cp03: {}}
kube_node:
hosts: {wk01: {}, wk02: {}, wk03: {}}
etcd:
hosts: {cp01: {}, cp02: {}, cp03: {}}
k8s_cluster:
children:
kube_control_plane: {}
kube_node: {}
calico_rr:
hosts: {}
YAML
Conservez un nombre impair de membres etcd. Définissez séparément les groupes de contrôle et de workers. Utilisez ansible_host pour l'adresse SSH et ip/access_ip pour les communications entre nœuds. Avec du NAT ou plusieurs cartes réseau, validez séparément les adresses annoncées et le routage.
Ouvrez les trois fichiers suivants dans un éditeur pour ajouter les clés concernées ou remplacer leurs valeurs. Chaque exemple est du YAML à placer dans un fichier, pas une commande shell.
Paramètres essentiels de inventory/prod/group_vars/all/all.yml
ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/kubespray_ed25519
loadbalancer_apiserver:
address: 10.20.0.10
port: 6443
kubeconfig_localhost: true
kubectl_localhost: true
Paramètres Kubernetes et sécurité
Dans Kubespray v2.31.0, kube_version et cilium_version prennent des versions numériques sans préfixe v. Kubespray ajoute ce préfixe aux URL et tags de conteneurs ; n'indiquez donc pas v1.35.4. Remplacez les clés correspondantes du YAML d'exemple au lieu d'en ajouter des doublons.
kube_version: 1.35.4
container_manager: containerd
kube_network_plugin: cilium
kube_service_addresses: 10.233.0.0/18
kube_pods_subnet: 10.233.64.0/18
cluster_name: cluster.local
kube_api_anonymous_auth: false
remove_anonymous_access: true
kubernetes_audit: true
supplementary_addresses_in_ssl_keys:
- 10.20.0.10
- api.k8s.example.com
Les CIDR des Pods et Services ne doivent pas chevaucher les réseaux des routeurs, VPN ou centres de données. Modifier un CIDR en service peut équivaloir à une migration vers un nouveau cluster. Incluez la VIP et le nom DNS de l'API dans les SAN du certificat kube-apiserver.
Configurer le réseau overlay Cilium et Hubble
cilium_version: 1.19.3
cilium_tunnel_mode: vxlan
cilium_identity_allocation_mode: crd
cilium_ipam_mode: kubernetes
cilium_cni_exclusive: true
cilium_enable_hubble: true
cilium_hubble_install: true
cilium_hubble_tls_generate: true
cilium_enable_hubble_ui: false
Le remplacement de kube-proxy modifie le chemin réseau des Services ; ce n'est pas un simple réglage de performance. Ce guide de base conserve kube-proxy. Pour le remplacer, concevez séparément cilium_kube_proxy_replacement, le point d'accès global à l'API, DSR/SNAT et le pare-feu des hôtes, puis effectuez des tests de charge et de panne.
Valider avant le déploiement
Vérifier les conflits de CIDR et la structure de l'inventaire
ip route
ip -4 address show
grep -R --line-number -E 'kube_(service_addresses|pods_subnet)|loadbalancer_apiserver|kube_version|cilium_version' inventory/prod/group_vars
ansible-inventory -i inventory/prod/inventory.yml --graph
ansible-inventory -i inventory/prod/inventory.yml --list > inventory/prod/inventory-expanded.json
Vérifier SSH, sudo et Python
ansible -i inventory/prod/inventory.yml all -m ping
ansible -i inventory/prod/inventory.yml all --become -m command -a 'id'
ansible -i inventory/prod/inventory.yml all --become -m shell -a 'python3 --version; stat -fc %T /sys/fs/cgroup; swapon --show'
La réussite du module ping ne suffit pas. Vérifiez que become fonctionne sans interaction et que l'interpréteur Python et cgroup v2 sont cohérents sur tous les nœuds. Ne versionnez jamais les mots de passe Ansible Vault ou les clés privées SSH.
Examiner la syntaxe du playbook et la liste des tâches
ansible-playbook -i inventory/prod/inventory.yml cluster.yml --syntax-check
git status --short
git diff -- inventory/prod/group_vars inventory/prod/inventory.yml
tar --exclude='credentials' --exclude='artifacts' -czf "inventory-prod-$(date +%F).tgz" inventory/prod
Exécuter le déploiement Kubespray
Ne commencez pas par appliquer des tags arbitraires à tous les nœuds. Exécutez le cluster.yml officiel avec un inventaire validé. En cas d'échec, examinez la première tâche en erreur et l'état du nœud avant de relancer. Kubespray vise l'idempotence, mais les répartiteurs et réseaux externes possèdent leur propre état.
mkdir -p logs
ansible-playbook -i inventory/prod/inventory.yml cluster.yml --become 2>&1 | tee "logs/cluster-$(date +%F-%H%M%S).log"
test ${PIPESTATUS[0]} -eq 0
Avec tee dans un pipeline, le code de retour final peut être celui de tee ; vérifiez donc PIPESTATUS[0] pour connaître le résultat d'ansible-playbook. Les journaux peuvent contenir des noms d'hôtes, des IP et des sorties de tâches : définissez leurs accès et leur rétention, et masquez les informations sensibles avant tout partage externe.
Valider le déploiement
Vérifier kubeconfig et le point d'accès au plan de contrôle
find inventory/prod/artifacts -maxdepth 2 -type f -ls
install -d -m 0700 "$HOME/.kube"
install -m 0600 inventory/prod/artifacts/admin.conf "$HOME/.kube/config"
kubectl config view --minify
kubectl cluster-info
kubectl get --raw='/readyz?verbose'
Les noms des artefacts varient selon le tag Kubespray et la configuration : examinez d'abord la sortie de find. Un kubeconfig peut contenir des identifiants cluster-admin ; ne le placez pas dans un répertoire personnel partagé ou un artefact CI librement accessible. Les opérateurs doivent utiliser OIDC et un RBAC au moindre privilège.
Vérifier les nœuds, etcd et les Pods système
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl get --raw='/livez?verbose'
ansible -i inventory/prod/inventory.yml etcd --become -m command -a 'systemctl status etcd --no-pager'
kubectl -n kube-system get endpoints kube-dns
kubectl get events -A --sort-by='.lastTimestamp' | tail -n 50
Vérifier Cilium et Hubble
kubectl -n kube-system rollout status daemonset/cilium --timeout=10m
kubectl -n kube-system get pods -l k8s-app=cilium -o wide
cilium status --wait --wait-duration 10m
cilium connectivity test
kubectl -n kube-system get secret | grep -i hubble
kubectl -n kube-system logs deployment/hubble-relay --tail=100
cilium connectivity test crée plusieurs namespaces et Pods. Exécutez-le dans un périmètre de test autorisé par la politique de production, puis nettoyez les ressources. Vérifiez le DaemonSet Cilium sur chaque nœud, l'opérateur, CoreDNS et le routage des Services avant de déployer des applications.
Valider les applications et NetworkPolicy
Tests élémentaires de Deployment, Service et DNS
kubectl create namespace smoke-test
kubectl -n smoke-test create deployment web --image=registry.k8s.io/e2e-test-images/agnhost:2.53 -- /agnhost netexec --http-port=8080
kubectl -n smoke-test expose deployment web --port=80 --target-port=8080
kubectl -n smoke-test rollout status deployment/web --timeout=5m
kubectl -n smoke-test run client --rm -it --restart=Never --image=docker.io/library/busybox:1.36.1 --command -- /bin/sh -c 'nslookup web; wget -qO- http://web/'
Refuser par défaut, puis autoriser explicitement
cat <<'YAML' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-default-deny
namespace: smoke-test
spec:
podSelector: {matchLabels: {app: web}}
policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-allow-client
namespace: smoke-test
spec:
podSelector: {matchLabels: {app: web}}
policyTypes: [Ingress]
ingress:
- from:
- podSelector: {matchLabels: {run: client}}
ports:
- {protocol: TCP, port: 8080}
YAML
kubectl -n smoke-test get networkpolicy
Vérifiez la réussite avant la politique, l'échec après default-deny, puis la réussite après ajout d'une autorisation explicite. Si vous limitez aussi les sorties DNS, contrôlez les véritables sélecteurs kube-dns et le port 53 en UDP et TCP. Supprimez ensuite les ressources avec kubectl delete namespace smoke-test.
Préparer les snapshots etcd et la récupération
Automatisez les snapshots etcd dès le déploiement. Créez un snapshot depuis un membre, copiez-le vers un stockage chiffré hors cluster et documentez la récupération avec l'inventaire, les certificats et le tag Kubespray.
# Exécuter sur un nœud etcd utilisant le mode de déploiement host par défaut.
sudo systemctl cat etcd
sudo grep -E '^ETCD_(LISTEN_CLIENT_URLS|TRUSTED_CA_FILE|CERT_FILE|KEY_FILE)=' /etc/etcd.env
sudo bash -euo pipefail <<'BASH'
backup_dir=/var/backups/etcd
install -d -m 0700 "$backup_dir"
snapshot="$backup_dir/snapshot-$(date +%F-%H%M%S).db"
node_name=$(hostname -s)
ca=/etc/ssl/etcd/ssl/ca.pem
cert="/etc/ssl/etcd/ssl/admin-${node_name}.pem"
key="/etc/ssl/etcd/ssl/admin-${node_name}-key.pem"
for file in "$ca" "$cert" "$key"; do test -r "$file"; done
etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert="$ca" --cert="$cert" --key="$key" snapshot save "$snapshot"
etcdutl snapshot status "$snapshot" --write-out=table
BASH
Avec la valeur par défaut etcd_deployment_type: host, etcd s'exécute comme service systemd. Vérifiez les points d'accès et les chemins de certificats réels dans /etc/etcd.env et systemctl cat etcd, puis adaptez les valeurs ci-dessus. Si les noms d'inventaire diffèrent des noms d'hôtes, utilisez le véritable nom du certificat administrateur. Testez régulièrement la restauration des snapshots, le démarrage de l'API et la cohérence des objets dans un environnement isolé.
Certificats, audit et contrôles d'exploitation
sudo kubeadm certs check-expiration
kubectl auth can-i --list
kubectl auth can-i create clusterrolebindings --as=system:anonymous
kubectl get --raw='/metrics' | grep -E 'apiserver_request_total|apiserver_request_duration_seconds' | head
kubectl get nodes -o json | jq -r '.items[] | [.metadata.name,.status.nodeInfo.kubeletVersion,.status.nodeInfo.containerRuntimeVersion] | @tsv'
- Vérifier le comportement de la VIP et du DNS de l'API, puis la réussite des requêtes kubectl lorsqu'un nœud de contrôle est arrêté.
- Déclencher des alertes sur les membres et le leader etcd, la taille de la base et la réussite des snapshots.
- Surveiller les systèmes de fichiers, les inodes, la pression mémoire et PID et l'espace des images sur les nœuds.
- Envoyer les journaux d'audit kube-apiserver vers un stockage central protégé contre l'altération et doté d'une politique de rétention.
- Retirer les kubeconfigs cluster-admin des comptes quotidiens et utiliser OIDC, RBAC et des sessions courtes.
- Surveiller les paquets rejetés par Cilium, les décisions de politique, les erreurs DNS et l'expiration des certificats Hubble.
Mettre Kubernetes à niveau avec Kubespray
Avancez d'une seule version mineure Kubernetes à la fois et respectez sa politique d'écart de versions. Vérifiez d'abord que le correctif Kubernetes et la version Cilium cibles figurent dans les sommes de contrôle et versions prises en charge par le tag Kubespray visé. Contrôlez ensuite les snapshots etcd, les sauvegardes applicatives, les PodDisruptionBudgets et la capacité disponible.
git fetch --tags --prune
git tag --sort=-version:refname | head
# Copier l'inventaire dans un nouveau clone avec venv, puis examiner les différences.
git diff v2.31.0..'<TARGET_KUBESPRAY_TAG>' -- inventory/sample roles/kubespray_defaults docs
ansible-playbook -i inventory/prod/inventory.yml upgrade-cluster.yml --become --syntax-check
ansible-playbook -i inventory/prod/inventory.yml upgrade-cluster.yml --become 2>&1 | tee "logs/upgrade-$(date +%F-%H%M%S).log"
test ${PIPESTATUS[0]} -eq 0
Lisez les notes urgentes de mise à niveau avant upgrade-cluster.yml. Pour Kubespray 2.31, examinez notamment les changements cgroup v1, l'arrêt du projet ingress-nginx, l'archivage de Kubernetes Dashboard, les versions etcd prérequises et les variables supprimées. Ne redémarrez pas tous les nœuds de contrôle et workers simultanément.
Erreurs fréquentes et solutions plus sûres
| Erreur | Impact | Solution |
|---|---|---|
| Déploiement depuis main | Dépendances et valeurs par défaut susceptibles de changer à tout moment | Fixer le tag, les dépendances et les sommes de contrôle |
| Deux membres etcd | Perte de majorité si un nœud tombe | Utiliser trois ou cinq membres |
| Point d'accès API unique | control plane SPOF | Utiliser des répartiteurs redondants avec contrôles de santé de la VIP |
| Chevauchement des CIDR | Conflits de routage entre Pods, Services et réseaux d'entreprise | Valider l'IPAM et les routes avant le déploiement |
| Désactivation du contrôle des clés hôtes SSH | Impossible de détecter une interception | Vérifier les empreintes, puis les enregistrer dans known_hosts |
| Partage d'un kubeconfig cluster-admin | Exposition des pleins pouvoirs sur le cluster | Utiliser OIDC, RBAC et des sessions courtes |
| Snapshots sans tests de restauration | Récupération non démontrée | Effectuer régulièrement des exercices de restauration isolés |
Points à vérifier pour le déploiement Kubespray
- Fixer le tag Kubespray et les dépendances Python/Ansible.
- Vérifier la prise en charge de la combinaison Kubernetes, Cilium et containerd.
- Préparer trois nœuds de contrôle/etcd et des répartiteurs API redondants.
- Éviter les chevauchements entre CIDR des Pods, Services, nœuds, VPN et stockages.
- Vérifier les clés hôtes SSH, les droits sudo et le stockage des secrets Vault et clés privées.
- Tester la santé des nœuds et de l'API, CoreDNS, la connectivité Cilium et NetworkPolicy.
- Conserver les snapshots etcd hors cluster et réaliser un exercice de récupération isolé.
- Surveiller l'expiration des certificats, l'audit, la pression sur les ressources et les rejets Cilium.
- Avant toute mise à niveau, examiner les notes urgentes, les écarts de versions, les PDB et la capacité disponible.
Ressources associées
- Version officielle Kubespray v2.31.0
- Versions Kubernetes et durées de support
- Politique d'écart de versions Kubernetes
- Compatibilité Kubernetes de Cilium
- Playbooks Ansible, Vault et déploiements progressifs
- Diagnostiquer l'espace disque et les inodes épuisés sous Linux
- Conteneurs Linux et cgroups v2
Conclusion
La réussite de cluster.yml n'est qu'une étape du déploiement Kubespray. Validez ensemble les versions, le quorum à trois nœuds, le point d'accès API, le réseau et la récupération. Fixez le tag et l'inventaire, restreignez l'accès anonyme et testez réellement Cilium, NetworkPolicy et la restauration etcd. Ces critères transforment un laboratoire Rocky Linux 9 en une plateforme Kubernetes exploitable.