Fullmoon System

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.

Architecture Kubernetes avec Kubespray : Ansible, VIP de l'API, trois nœuds de contrôle, trois workers, Cilium et sauvegardes etcd
Un contrôleur Ansible déploie trois nœuds de contrôle/etcd et trois workers derrière une VIP d'API, avec validation de Cilium et des sauvegardes hors cluster

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
La dernière version de Kubernetes n'est pas nécessairement une combinaison validée avec Kubespray et Cilium. En juillet 2026, Kubernetes 1.36.2 était la dernière version stable, tandis que Kubespray 2.31.0 utilisait 1.35.4 par défaut. Consultez la matrice de tests de bout en bout garantie dans la documentation stable de Cilium et validez séparément toute combinaison hors de cette liste avant la production.

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

  1. Fixer le tag Kubespray et les dépendances Python/Ansible.
  2. Vérifier la prise en charge de la combinaison Kubernetes, Cilium et containerd.
  3. Préparer trois nœuds de contrôle/etcd et des répartiteurs API redondants.
  4. Éviter les chevauchements entre CIDR des Pods, Services, nœuds, VPN et stockages.
  5. Vérifier les clés hôtes SSH, les droits sudo et le stockage des secrets Vault et clés privées.
  6. Tester la santé des nœuds et de l'API, CoreDNS, la connectivité Cilium et NetworkPolicy.
  7. Conserver les snapshots etcd hors cluster et réaliser un exercice de récupération isolé.
  8. Surveiller l'expiration des certificats, l'audit, la pression sur les ressources et les rejets Cilium.
  9. Avant toute mise à niveau, examiner les notes urgentes, les écarts de versions, les PDB et la capacité disponible.

Ressources associées

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.