Aller au contenu
English
English
Conteneurs & Orchestration medium

Installer un cluster Kubernetes avec kubeadm

70 min de lecture

logo kubernetes

kubeadm est l'outil officiel pour bootstrapper un cluster Kubernetes conforme aux standards upstream. Ce guide vous accompagne dans l'installation d'un cluster multi-nœuds sur des machines virtuelles KVM, de la préparation des VMs jusqu'au déploiement d'applications. Vous apprendrez à configurer containerd, initialiser le control plane, joindre des workers et maintenir votre cluster.

Autant lever le malentendu tout de suite. Ce guide construit un cluster robuste pour le lab, la validation technique et la préparation CKA, pas un cluster de production stricte. Il y manque, dans le désordre, un miroir de registre privé avec sa politique d'images, une sauvegarde etcd externalisée et chiffrée, de la supervision, une gestion centralisée des journaux, et un durcissement aligné sur un référentiel. Chacun de ces points fait l'objet d'une leçon ailleurs dans ce parcours : ce que vous montez ici en est le socle, pas l'aboutissement.

Le tableau ci-dessous situe kubeadm parmi les outils qui montent des clusters. Lisez-le en gardant une idée en tête : kubeadm n'est pas en concurrence avec les distributions, il est ce sur quoi plusieurs d'entre elles reposent. Le choisir directement, c'est accepter d'assembler soi-même ce qu'elles livrent pré-assemblé, en échange d'un Kubernetes strictement conforme à l'upstream et d'une compréhension de chaque pièce.

Critèrekubeadmk0sk3sKubesprayRKE2
TypeBootstrap toolDistributionDistributionPlaybooks AnsibleDistribution
OrientationStandard upstreamEdge, prod, CIEdge, IoT, devProd, multi-cloudEnterprise, sécurité
InstallationCommandesk0sctl (YAML)Script uniqueAnsibleScript
CNI défautAucunkube-routerFlannelConfigurableCanal
HA nativeManuel✅ Multi-controller✅ Multi-server✅ Ansible✅ Intégrée
Profil CISManuelManuelManuelSelon config✅ Intégré
Cas d'usageBare metal, formationEdge, CI/CD, prodEdge, IoT, devProd automatiséeConformité, prod

Ce guide vous permettra de maîtriser l'installation et la maintenance d'un cluster Kubernetes avec kubeadm. À la fin, vous saurez :

  • Provisionner des VMs KVM pour héberger le cluster, en comprenant pourquoi chaque configuration est nécessaire
  • Installer containerd et les composants Kubernetes, en configurant correctement le cgroup driver pour éviter les problèmes de stabilité
  • Initialiser un cluster avec kubeadm, en comprenant le rôle de chaque option et composant
  • Joindre des nœuds workers au cluster et diagnostiquer les échecs de jointure
  • Configurer la haute disponibilité avec plusieurs control planes et un load balancer
  • Mettre à jour le cluster vers une nouvelle version en respectant les règles de version skew
  • Sauvegarder et restaurer etcd, la base de données critique du cluster
  • Dépanner les problèmes courants avec une méthodologie structurée

Avant de créer vos VMs, comprenez pourquoi Kubernetes impose des minimums stricts. Le control plane héberge l'API server, etcd, le scheduler et le controller-manager : ces composants consomment de la mémoire même au repos. Avec moins de 2 Go de RAM, etcd refusera de démarrer ou crashera sous charge. Les workers exécutent vos conteneurs applicatifs : 2 vCPU permettent de faire tourner plusieurs pods sans contention excessive.

RôleCPURAMDisqueQuantitéPourquoi ces specs
Control plane2 vCPU2 Go20 Go1 (ou 3 pour HA)etcd + API server + scheduler + controller-manager
Worker2 vCPU2 Go20 Go2+kubelet + pods applicatifs

Pour un environnement de production, doublez ces valeurs minimum. En environnement de test ou pour la préparation CKA, ces minimums suffisent.

Vous aurez besoin d'un hyperviseur Linux avec KVM pour créer les machines virtuelles. Si vous n'avez pas de serveur dédié, votre poste de travail suffit, à condition d'avoir assez de mémoire : 16 Go pour faire tourner trois VM simultanément.

  • KVM/QEMU et libvirt installés (le cœur de la virtualisation)
  • virt-manager ou virsh pour gérer les VMs (interface graphique ou CLI)
  • Une image cloud Ubuntu 24.04 ou Rocky Linux 9 (pré-configurée pour cloud-init)

La communication entre composants Kubernetes passe par des ports bien définis. Avec un pare-feu entre les VM, iptables, firewalld ou un équipement réseau, vous devez les ouvrir explicitement. Sur un réseau local sans pare-feu, cette étape ne sert à rien.

PortComposantQui se connectePourquoi
6443kube-apiserverkubectl, kubelet, autres control planesPoint d'entrée de toute communication avec le cluster
2379-2380etcdAPI server, autres etcd (HA)Base de données du cluster, réplication HA
10250kubeletAPI server, metrics-serverGestion des pods, récupération des logs et métriques
10257kube-controller-managerPrometheus (optionnel)Métriques du controller
10259kube-schedulerPrometheus (optionnel)Métriques du scheduler
179Calico BGPAutres nœuds CalicoRoutage BGP entre nœuds (si vous utilisez Calico)

kubeadm ne provisionne pas de machines : il faut donc les fabriquer avant lui. Cette section monte trois VM sous KVM, un choix qui n'a rien d'obligatoire, n'importe quel hyperviseur ou serveur physique conviendrait. Ce qui compte est le résultat : trois systèmes joignables entre eux, avec un utilisateur disposant de sudo et une adresse stable. Si vous disposez déjà de machines, passez directement à la section suivante.

Le schéma ci-dessous représente l'architecture cible. Un nœud control plane, cp1, héberge les composants de gestion, et deux workers, worker1 et worker2, exécutent vos applications. Tous sont sur le réseau virtuel par défaut de libvirt, virbr0 en 192.168.122.0/24.

Architecture du lab kubeadm avec KVM

Cette architecture suffit pour apprendre Kubernetes et préparer la CKA. En production, on ajoute deux control planes de plus pour la haute disponibilité, traitée dans une section dédiée.

Les images cloud sont des disques système préconfigurés pour s'initialiser via cloud-init. Elles démarrent en quelques secondes et se configurent seules au premier amorçage. On emploie ici le backing file de QCOW2 : l'image de base reste intacte et chaque VM ne stocke que ses différences, ce qui économise du disque et accélère la création.

Ubuntu 24.04 LTS est recommandée pour sa stabilité et son support jusqu'en 2029. Son image cloud embarque déjà cloud-init et les modules noyau nécessaires.

Fenêtre de terminal
# Télécharger l'image cloud Ubuntu (environ 700 Mo)
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img \
-O /var/lib/libvirt/images/ubuntu-24.04-cloud.img
# Créer un snapshot pour chaque VM (20 Go max, mais démarre petit)
# Le backing file (-b) permet de partager l'image de base entre toutes les VMs
qemu-img create -f qcow2 -b /var/lib/libvirt/images/ubuntu-24.04-cloud.img \
-F qcow2 /var/lib/libvirt/images/cp1.qcow2 20G
qemu-img create -f qcow2 -b /var/lib/libvirt/images/ubuntu-24.04-cloud.img \
-F qcow2 /var/lib/libvirt/images/worker1.qcow2 20G
qemu-img create -f qcow2 -b /var/lib/libvirt/images/ubuntu-24.04-cloud.img \
-F qcow2 /var/lib/libvirt/images/worker2.qcow2 20G

Cloud-init est l'outil standard d'initialisation des instances cloud. Il pose le nom d'hôte, crée les utilisateurs, injecte les clés SSH et exécute des commandes au premier amorçage. Écrivez un fichier par VM, en adaptant l'adresse IP et le nom d'hôte.

Le fichier ci-dessous configure le control plane (cp1). Les points importants :

  • Utilisateur kube : compte dédié avec sudo sans mot de passe pour simplifier les manipulations
  • Clé SSH : remplacez par votre clé publique pour vous connecter sans mot de passe
  • IP statique : garantit que les nœuds gardent la même IP après reboot
  • Swap désactivé : kubelet refuse de démarrer si le swap est actif
cloud-init-cp1.yaml
#cloud-config
hostname: cp1
fqdn: cp1.kube.local
manage_etc_hosts: true
users:
- name: kube
sudo: ALL=(ALL) NOPASSWD:ALL
groups: sudo
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... votre-clé-publique
# Réseau statique - adaptez à votre réseau libvirt
network:
version: 2
ethernets:
enp1s0:
addresses:
- 192.168.122.10/24
gateway4: 192.168.122.1
nameservers:
addresses:
- 192.168.122.1
# Commandes exécutées au premier boot
runcmd:
- swapoff -a
- sed -i '/swap/d' /etc/fstab

Créez des fichiers similaires pour worker1 (IP .20) et worker2 (IP .21).

Les images et la configuration prêtes, il reste à créer les machines virtuelles. La commande cloud-localds fabrique un disque ISO portant la configuration cloud-init, que la VM lit à son premier amorçage.

Fenêtre de terminal
# Générer l'ISO cloud-init pour le control plane
cloud-localds /var/lib/libvirt/images/cp1-cidata.iso cloud-init-cp1.yaml
# Créer la VM control plane
# --import : utilise un disque existant au lieu de lancer une installation
# --noautoconsole : ne pas attacher de console (la VM démarre en arrière-plan)
virt-install \
--name cp1 \
--ram 2048 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/cp1.qcow2,format=qcow2 \
--disk path=/var/lib/libvirt/images/cp1-cidata.iso,device=cdrom \
--os-variant ubuntu24.04 \
--network network=default \
--graphics none \
--console pty,target_type=serial \
--import \
--noautoconsole

Répétez l'opération pour worker1 et worker2 en adaptant les noms et les fichiers cloud-init.

Vérification : après une minute de boot, testez la connexion SSH. Si la connexion échoue, vérifiez que votre clé publique est correcte dans le fichier cloud-init.

Fenêtre de terminal
ssh kube@192.168.122.10
# Doit se connecter sans demander de mot de passe

Cette section prépare le système pour exécuter Kubernetes. Chaque étape a une raison précise, et sauter une étape causera des problèmes difficiles à diagnostiquer. Exécutez ces commandes sur tous les nœuds (control plane et workers).

  1. Configurer les modules kernel pour le réseau des conteneurs

    Kubernetes relie les conteneurs par des ponts réseau virtuels. Le module overlay autorise le stockage en couches des images, et br_netfilter fait passer le trafic des ponts par iptables. Sans ce second module, les règles des Services ne s'appliquent pas.

    Fenêtre de terminal
    # Charger les modules au démarrage
    cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
    overlay
    br_netfilter
    EOF
    # Charger les modules immédiatement (sans reboot)
    sudo modprobe overlay
    sudo modprobe br_netfilter

    Vérification : les modules doivent apparaître dans la liste des modules chargés.

    Fenêtre de terminal
    lsmod | grep -E 'overlay|br_netfilter'
    # br_netfilter 32768 0
    # overlay 151552 0
  2. Configurer sysctl pour le forwarding réseau

    Ces paramètres noyau laissent le trafic traverser les interfaces virtuelles. Sans eux, les Pods ne communiquent ni entre eux ni avec l'extérieur. Le paramètre ip_forward active le routage IP, indispensable puisque chaque nœud se comporte en routeur pour ses Pods.

    Fenêtre de terminal
    cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
    net.bridge.bridge-nf-call-iptables = 1
    net.bridge.bridge-nf-call-ip6tables = 1
    net.ipv4.ip_forward = 1
    EOF
    # Appliquer sans reboot
    sudo sysctl --system

    Vérification : les valeurs doivent être à 1.

    Fenêtre de terminal
    sysctl net.ipv4.ip_forward
    # net.ipv4.ip_forward = 1
  3. Désactiver le swap (obligatoire)

    Le kubelet refuse de démarrer tant que le swap est actif, et la raison mérite d'être connue : le scheduler décide du placement d'après la mémoire réellement disponible. Un système qui swape rend ces calculs faux. Si cloud-init a déjà désactivé le swap, la commande est sans effet.

    Fenêtre de terminal
    # Désactiver immédiatement
    sudo swapoff -a
    # Supprimer l'entrée swap du fstab pour que ça persiste au reboot
    sudo sed -i '/swap/d' /etc/fstab

    Vérification : la commande free ne doit montrer aucun swap.

    Fenêtre de terminal
    free -h | grep Swap
    # Swap: 0B 0B 0B
  4. Installer containerd comme runtime de conteneurs

    Kubernetes a besoin d'un runtime de conteneurs conforme à l'interface CRI (Container Runtime Interface). Depuis Kubernetes 1.24, le kubelet ne parle plus directement à Docker Engine : c'est dockershim qui a été retiré, Docker Engine restant utilisable via l'adaptateur cri-dockerd. containerd est devenu le standard de fait : léger, stable, et utilisé par tous les clouds publics. Nous l'installons depuis le dépôt Docker car il fournit des binaires à jour.

    Fenêtre de terminal
    # Installer les dépendances pour ajouter des repos HTTPS
    sudo apt-get update
    sudo apt-get install -y ca-certificates curl gnupg
    # Ajouter la clé GPG du repo Docker
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
    sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    # Ajouter le repo Docker
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
    https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list
    # Installer containerd
    sudo apt-get update
    sudo apt-get install -y containerd.io
  5. Configurer containerd avec le cgroup driver systemd

    C'est l'étape la plus critique et la plus souvent oubliée. Kubernetes et containerd doivent utiliser le même cgroup driver. Sur les distributions modernes avec systemd, ce driver doit être systemd (pas cgroupfs). Si vous oubliez cette configuration, le kubelet crashera avec des erreurs cryptiques.

    Fenêtre de terminal
    # Générer la configuration par défaut
    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml
    # CRITIQUE : activer le cgroup driver systemd
    sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
    # Redémarrer pour appliquer
    sudo systemctl restart containerd
    sudo systemctl enable containerd

    Vérification : containerd doit être actif et la configuration correcte.

    Fenêtre de terminal
    sudo systemctl status containerd
    # ● containerd.service - containerd container runtime
    # Active: active (running)
    grep SystemdCgroup /etc/containerd/config.toml
    # SystemdCgroup = true
  6. Installer kubeadm, kubelet et kubectl

    Ces trois binaires forment le cœur de l'installation :

    • kubeadm : outil de bootstrap qui initialise le cluster et génère les certificats
    • kubelet : agent qui tourne sur chaque nœud et gère les containers
    • kubectl : CLI pour interagir avec l'API Kubernetes
    Fenêtre de terminal
    # Ajouter la clé GPG du repo Kubernetes
    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | \
    sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    # Ajouter le repo Kubernetes
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
    https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /' | \
    sudo tee /etc/apt/sources.list.d/kubernetes.list
    # Installer les composants
    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl
    # Bloquer les mises à jour automatiques (important pour les upgrades contrôlés)
    sudo apt-mark hold kubelet kubeadm kubectl

    Vérification : les binaires doivent être installés à la bonne version.

    Fenêtre de terminal
    kubeadm version
    # kubeadm version: &version.Info{Major:"1", Minor:"36", EmulationMajor:"",
    # EmulationMinor:"", MinCompatibilityMajor:"", MinCompatibilityMinor:"",
    # GitVersion:"v1.36.0", GitCommit:"ecf6decece6a6de25a57aad9ba90b6ce580f6f78",
    # GitTreeState:"clean", BuildDate:"2026-04-22T13:54:03Z", GoVersion:"go1.26.2",
    # Compiler:"gc", Platform:"linux/amd64"}
    kubectl version --client
    # Client Version: v1.36.0
    # Kustomize Version: v5.8.1

    Deux champs de cette sortie déroutent la première fois. Depuis la 1.36, la structure de version porte EmulationMajor/EmulationMinor et MinCompatibilityMajor/MinCompatibilityMinor, liés à la compatibility version. Ils sont vides sur un binaire standard, et c'est normal : ils servent à un kube-apiserver qui doit émuler le comportement d'une version antérieure pendant une mise à jour progressive.

L'initialisation du cluster est le moment où kubeadm génère tous les composants du control plane : certificats TLS, fichiers de configuration, et pods statiques. Cette étape ne s'exécute que sur le premier nœud control plane.

Avant de lancer la commande, comprenez chaque option pour pouvoir adapter à votre environnement :

OptionSignificationPourquoi c'est important
--pod-network-cidrPlage d'adresses pour les podsLe CNI (Flannel, Calico) utilisera ce CIDR. 10.244.0.0/16 est le défaut pour Flannel
--apiserver-advertise-addressIP sur laquelle l'API server écouteLes workers et kubectl utiliseront cette IP
--control-plane-endpointEndpoint stable pour le control planeIndispensable pour HA : permet d'ajouter d'autres control planes plus tard

Méthode recommandée : fichier de configuration YAML

Section intitulée « Méthode recommandée : fichier de configuration YAML »

Plutôt que des options CLI longues, utilisez un fichier de configuration kubeadm. C'est reproductible, versionnable et compatible avec IaC (Ansible, Terraform).

Créez un fichier kubeadm-config.yaml :

kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: v1.36.0
controlPlaneEndpoint: "192.168.122.10:6443"
networking:
podSubnet: 10.244.0.0/16
serviceSubnet: 10.96.0.0/12
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd

Puis initialisez avec :

Fenêtre de terminal
sudo kubeadm init --config kubeadm-config.yaml

Quatre raisons rendent cette forme préférable à la ligne de commande. Le fichier est versionnable dans Git, donc relisible et comparable d'une version à l'autre. Il se génère depuis Ansible ou Terraform le jour où l'installation s'industrialise. Il rassemble toutes les options au même endroit, là où une commande longue devient illisible. Et il n'empêche rien : le passage en haute disponibilité se fait en ajoutant --upload-certs à la commande, pas au fichier.

Initialiser avec la ligne de commande (alternative)

Section intitulée « Initialiser avec la ligne de commande (alternative) »

Si vous préférez ne pas créer de fichier YAML pour un lab rapide, vous pouvez utiliser les options CLI. Sur le nœud cp1 uniquement, lancez l'initialisation :

Fenêtre de terminal
sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=192.168.122.10 \
--control-plane-endpoint=192.168.122.10:6443

Un conseil qui ne coûte rien sur le moment et évite une reconstruction plus tard : renseignez --control-plane-endpoint même si la haute disponibilité n'est pas au programme. Ce champ fixe l'adresse que tous les clients utiliseront, et c'est lui qui permet d'ajouter des control planes sans recréer le cluster. À défaut de load balancer, mettez l'adresse du premier control plane ; l'important est que le champ existe dès le départ.

Sortie attendue (les tokens seront différents) :

Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
...
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.122.10:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:...

Copiez et sauvegardez la commande kubeadm join affichée ! Vous en aurez besoin pour joindre les workers. Le token expire après 24 heures.

kubectl a besoin d'un kubeconfig pour savoir où joindre l'API server et comment s'y authentifier. Le fichier admin.conf produit par kubeadm porte un certificat client administrateur. On le copie dans le répertoire personnel pour que kubectl le trouve sans option.

Fenêtre de terminal
# Créer le répertoire de configuration kubectl
mkdir -p $HOME/.kube
# Copier le fichier de configuration admin
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# Donner les droits à l'utilisateur courant
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Vérification : kubectl doit maintenant pouvoir contacter l'API server.

Fenêtre de terminal
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# cp1 NotReady control-plane 30s v1.36.0

Le statut NotReady est normal à ce stade : le CNI (Container Network Interface) n'est pas encore installé. Sans CNI, les pods ne peuvent pas obtenir d'adresses IP et le nœud ne peut pas être considéré comme prêt.

Le CNI (Container Network Interface) fournit le réseau aux pods. Sans lui, les pods resteront en Pending et le nœud en NotReady.

Choisissez votre CNI selon votre cas d'usage :

CNIUsage conseilléNetworkPoliciesComplexitéQuand l'utiliser
FlannelLab, démo, apprentissage❌ Non⭐ FaibleVous débutez ou n'avez pas besoin de NetworkPolicies
CalicoProduction classique✅ Oui⭐⭐ MoyenneVous avez besoin d'isolation réseau entre namespaces
CiliumProduction avancée, observabilité✅ Oui + eBPF⭐⭐⭐ Plus élevéeVous voulez de l'observabilité réseau poussée

Flannel est le CNI le plus simple à poser, ce qui en fait le bon choix pour apprendre et pour la CKA. Il n'applique pas les NetworkPolicies, limite sans conséquence sur un lab mais rédhibitoire en production.

Fenêtre de terminal
# Installer Flannel
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

Cette commande crée un namespace dédié, un DaemonSet, des ConfigMaps et les RBAC nécessaires. Attendez environ 30 secondes que les pods CNI démarrent.

Vérification : le nœud doit passer en Ready et Flannel doit tourner.

Fenêtre de terminal
# Le nœud doit maintenant être Ready
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# cp1 Ready control-plane 2m v1.36.0
# Le pod Flannel doit être Running
kubectl get pods -n kube-flannel
# NAME READY STATUS RESTARTS AGE
# kube-flannel-ds-xxxxx 1/1 Running 0 30s
# Vérifier aussi les pods système
kubectl get pods -n kube-system
# Tous les pods doivent être Running ou Completed

Chaque worker doit s'enregistrer auprès du control plane avant de recevoir des Pods. La commande kubeadm join s'authentifie avec un token et une empreinte de certificat, les deux étant obligatoires.

Sur chaque worker (worker1 et worker2), exécutez la commande copiée lors de l'initialisation :

Fenêtre de terminal
sudo kubeadm join 192.168.122.10:6443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:...

Le token est valide 24 heures. Si vous l'avez perdu ou s'il a expiré, générez-en un nouveau sur le control plane :

Fenêtre de terminal
# Sur le control plane : regénérer la commande join complète
kubeadm token create --print-join-command

Vérification finale depuis le control plane : tous les nœuds doivent être Ready.

Fenêtre de terminal
kubectl get nodes -o wide
# NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE
# cp1 Ready control-plane 5m v1.36.0 192.168.122.10 Ubuntu 24.04
# worker1 Ready <none> 2m v1.36.0 192.168.122.20 Ubuntu 24.04
# worker2 Ready <none> 1m v1.36.0 192.168.122.21 Ubuntu 24.04

Félicitations ! Vous avez un cluster Kubernetes fonctionnel. Les sections suivantes couvrent la haute disponibilité, les mises à jour et le dépannage.

En production, un control plane unique est un point de défaillance unique. S'il tombe, l'API server devient injoignable et le cluster n'est plus pilotable. Les workers continuent de faire tourner les Pods existants, mais plus aucun déploiement, mise à l'échelle ou modification n'aboutit.

La haute disponibilité répond à ce risque avec trois control planes et un répartiteur de charge devant eux. La perte d'un nœud laisse les deux autres servir.

Kubernetes HA supporte deux architectures etcd. Comprenez les différences avant de choisir :

ArchitectureDescriptionAvantagesInconvénientsQuand l'utiliser
Stacked etcdetcd sur les mêmes nœuds que le control planeMoins de VMs, plus simpleCouplage control plane/etcd, risque de contention ressourcesLab, CKA, clusters < 50 nœuds
External etcdCluster etcd dédié (3 VMs séparées)Isolation ressources, scaling indépendantPlus de VMs, complexité certificatsProduction critique, > 50 nœuds

Ce guide utilise stacked etcd (la topologie par défaut de kubeadm).

Le load balancer (HAProxy) lui-même peut devenir un point de défaillance unique. En production stricte :

  • Utilisez 2 instances HAProxy avec Keepalived pour une VIP flottante
  • Ou un load balancer cloud (AWS ELB, GCP LB) qui est nativement HA

Le schéma ci-dessous montre une architecture HA courante. HAProxy répartit le trafic entre les API servers, chaque control plane héberge son propre etcd, ce qu'on appelle la topologie stacked, et les workers ne connaissent que le répartiteur.

Architecture Kubernetes haute disponibilité

HAProxy répartit en TCP le port 6443 de l'API server. Dédiez-lui une VM, ou réutilisez un répartiteur existant. Le contrôle de santé TCP retire automatiquement du service un control plane qui ne répond plus.

Fenêtre de terminal
sudo apt-get install -y haproxy

Configurez HAProxy pour répartir entre les trois control planes. Le mode tcp est obligatoire : l'API server parle TLS, et le répartiteur ne doit surtout pas terminer la connexion chiffrée.

/etc/haproxy/haproxy.cfg
global
daemon
maxconn 256
defaults
mode tcp
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend kubernetes-frontend
bind *:6443
default_backend kubernetes-backend
backend kubernetes-backend
balance roundrobin
option tcp-check
server cp1 192.168.122.10:6443 check
server cp2 192.168.122.11:6443 check
server cp3 192.168.122.12:6443 check

Redémarrez HAProxy et vérifiez qu'il écoute sur le port 6443 :

Fenêtre de terminal
sudo systemctl restart haproxy
sudo systemctl enable haproxy
# Vérifier que HAProxy écoute
ss -tlnp | grep 6443
# LISTEN 0 128 *:6443 *:* users:(("haproxy",...))

L'initialisation en HA se distingue par l'option --upload-certs, qui chiffre les certificats du cluster et les dépose dans un Secret. Les control planes suivants les récupèrent à la jointure, sans copie manuelle.

Fenêtre de terminal
sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--control-plane-endpoint="192.168.122.5:6443" \
--upload-certs

Points importants :

  • --control-plane-endpoint pointe vers HAProxy (pas vers cp1)
  • --upload-certs génère une clé de déchiffrement valide 2 heures

La sortie affiche deux commandes kubeadm join :

  • Une pour les workers (sans --control-plane)
  • Une pour les control planes (avec --control-plane --certificate-key)

Sur cp2 et cp3, exécutez la commande spéciale pour control planes :

Fenêtre de terminal
sudo kubeadm join 192.168.122.5:6443 \
--token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:... \
--control-plane \
--certificate-key ...

Vérification : les 3 control planes doivent apparaître avec le rôle control-plane.

Fenêtre de terminal
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# cp1 Ready control-plane 10m v1.36.0
# cp2 Ready control-plane 5m v1.36.0
# cp3 Ready control-plane 3m v1.36.0

La montée de version d'un cluster est une compétence attendue à la CKA. Kubernetes publie trois versions mineures par an, et l'exercice consiste à suivre ce rythme sans interruption de service.

Kubernetes impose des règles strictes de compatibilité entre versions. Le version skew définit l'écart de version autorisé entre composants :

ComposantSkew autorisé par rapport à kube-apiserver
kubelet-2 versions mineures (ex: API 1.36 → kubelet 1.34 OK)
kubectl±1 version mineure
kube-controller-manager, kube-schedulerMême version ou -1

Règle pratique : mettez à jour le control plane avant les workers, et ne sautez pas de version mineure.

Deux règles gouvernent l'ordre des opérations, et les enfreindre bloque la mise à jour plutôt que de la casser, ce qui est déjà ça. La première interdit de sauter une version mineure : de 1.34, on passe par 1.35 avant d'atteindre 1.36. La seconde fixe l'ordre à l'intérieur d'une même étape : kubeadm d'abord, puis kubeadm upgrade apply, et seulement ensuite kubelet et kubectl.

La mise à jour du control plane se fait en plusieurs étapes. Cette procédure minimise le temps d'indisponibilité de l'API server.

Quatre vérifications avant de lancer quoi que ce soit, dont aucune n'est facultative. Sauvegardez etcd, c'est le seul retour en arrière possible si l'opération tourne mal. Lisez les notes de version de la cible, à la recherche des changements de comportement. Vérifiez le skew, aucun saut de version mineure n'est permis. Et si vos nœuds sont derrière un miroir, récupérez les images à l'avance avec kubeadm config images pull.

  1. Vérifier les versions disponibles

    Avant toute chose, regardez quelles versions le dépôt propose réellement. C'est ce qui permet de planifier la montée et de vérifier que la version cible existe.

    Fenêtre de terminal
    # Lister les versions disponibles de kubeadm
    apt-cache madison kubeadm | head -5
    # kubeadm | 1.36.0-1.1 | https://pkgs.k8s.io/core:/stable:/v1.36/deb Packages
    # kubeadm | 1.35.4-1.1 | https://pkgs.k8s.io/core:/stable:/v1.35/deb Packages
    # kubeadm | 1.35.3-1.1 | https://pkgs.k8s.io/core:/stable:/v1.35/deb Packages
    # kubeadm | 1.35.2-1.1 | https://pkgs.k8s.io/core:/stable:/v1.35/deb Packages
    # kubeadm | 1.35.1-1.1 | https://pkgs.k8s.io/core:/stable:/v1.35/deb Packages

    Le dépôt pkgs.k8s.io est versionné par version mineure, ce qui surprend au premier passage. Pour voir les paquets 1.36.x, il faut d'abord déclarer le dépôt v1.36 : /etc/apt/sources.list.d/kubernetes.list doit pointer vers https://pkgs.k8s.io/core:/stable:/v1.36/deb/. Sans cette étape, apt-cache madison ne listera que la mineure courante, et vous conclurez à tort que la version cible n'existe pas.

  2. Mettre à jour kubeadm en premier

    kubeadm doit lui-même être à la version cible avant de pouvoir l'appliquer. On lève donc le blocage du paquet, on met à jour, puis on rebloque pour qu'une mise à jour système ne l'emporte pas.

    Fenêtre de terminal
    # Débloquer kubeadm
    sudo apt-mark unhold kubeadm
    # Mettre à jour le cache et installer la nouvelle version
    sudo apt-get update
    sudo apt-get install -y kubeadm=1.36.0-1.1
    # Rebloquer pour éviter les mises à jour automatiques
    sudo apt-mark hold kubeadm
  3. Vérifier le plan de mise à jour

    Cette commande affiche exactement ce qui sera mis à jour, vérifie les prérequis et signale les obstacles. Lisez sa sortie en entier avant d'aller plus loin.

    Fenêtre de terminal
    sudo kubeadm upgrade plan
    # [preflight] Running pre-flight checks.
    # [upgrade/config] Reading configuration from the "kubeadm-config" ConfigMap...
    # [upgrade] Running cluster health checks
    # [upgrade] Fetching available versions to upgrade to
    # [upgrade/versions] Cluster version: 1.35.4
    # [upgrade/versions] kubeadm version: v1.36.0
    # [upgrade/versions] Target version: v1.36.0
    # [upgrade/versions] Latest version in the v1.35 series: v1.35.4
    #
    # Components that must be upgraded manually after you have upgraded the control plane
    # with 'kubeadm upgrade apply':
    # COMPONENT NODE CURRENT TARGET
    # kubelet cp1 v1.35.4 v1.36.0
    # kubelet worker1 v1.35.4 v1.36.0
    # kubelet worker2 v1.35.4 v1.36.0
    #
    # Upgrade to the latest stable version:
    #
    # COMPONENT NODE CURRENT TARGET
    # kube-apiserver cp1 v1.35.4 v1.36.0
    # kube-controller-manager cp1 v1.35.4 v1.36.0
    # kube-scheduler cp1 v1.35.4 v1.36.0
    # kube-proxy 1.35.4 v1.36.0
    # CoreDNS v1.13.1 v1.14.2
    # etcd cp1 3.6.6-0 3.6.8-0
    #
    # You can now apply the upgrade by executing the following command:
    #
    # kubeadm upgrade apply v1.36.0

    Notez les sauts de version embarqués : CoreDNS v1.13.1 → v1.14.2, etcd 3.6.6-0 → 3.6.8-0. Pour la liste exhaustive des images packagées avec 1.36 :

    Fenêtre de terminal
    sudo kubeadm config images list --kubernetes-version v1.36.0
    # registry.k8s.io/kube-apiserver:v1.36.0
    # registry.k8s.io/kube-controller-manager:v1.36.0
    # registry.k8s.io/kube-scheduler:v1.36.0
    # registry.k8s.io/kube-proxy:v1.36.0
    # registry.k8s.io/coredns/coredns:v1.14.2
    # registry.k8s.io/pause:3.10.2
    # registry.k8s.io/etcd:3.6.8-0

    Si vos nœuds n'atteignent pas registry.k8s.io directement, ne découvrez pas le problème au milieu de la mise à jour : récupérez les images à l'avance sur chaque nœud avec kubeadm config images pull --kubernetes-version v1.36.0. Sur un cluster isolé derrière un miroir, cette étape fait la différence entre une fenêtre de quelques secondes et une indisponibilité qui s'étire.

  4. Appliquer la mise à jour

    Cette commande met à jour les composants du control plane : elle télécharge les images, réécrit les manifestes des pods statiques et redémarre les composants. Le cluster reste accessible, avec au pire quelques secondes d'indisponibilité de l'API.

    Fenêtre de terminal
    sudo kubeadm upgrade apply v1.36.0 --yes
    # [upgrade/staticpods] Backing up old manifest to /etc/kubernetes/tmp/...
    # [upgrade/staticpods] Waiting for the kubelet to restart the component
    # [apiclient] Found 1 Pods for label selector component=kube-apiserver
    # [upgrade/staticpods] Component "kube-apiserver" upgraded successfully!
    # ... (idem pour kube-controller-manager, kube-scheduler, etcd)
    # [upgrade/control-plane] The control plane instance for this node was successfully upgraded!
    # [kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system...
    # [upgrade/kubeconfig] The kubeconfig files for this node were successfully upgraded!
    # [upgrade/kubelet-config] The kubelet configuration for this node was successfully upgraded!
    # [addons] Applied essential addon: CoreDNS
    # [addons] Applied essential addon: kube-proxy
    #
    # [upgrade] SUCCESS! A control plane node of your cluster was upgraded to "v1.36.0".

    Ce que fait kubeadm upgrade apply mérite d'être connu, parce que cela dit où chercher si l'opération tourne mal. Pour chaque pod statique du control plane, kube-apiserver, kube-controller-manager, kube-scheduler et etcd, kubeadm procède en trois temps : il sauvegarde l'ancien manifeste dans /etc/kubernetes/tmp/kubeadm-backup-manifests-<horodatage>/, écrit le nouveau dans /etc/kubernetes/manifests/, puis attend que le kubelet redémarre le pod. L'indisponibilité par composant se compte en secondes, et ce répertoire de sauvegarde est votre filet en cas de retour arrière.

  5. Mettre à jour kubelet et kubectl

    kubelet et kubectl doivent être mis à jour séparément. Le kubelet gère les pods sur ce nœud, donc un redémarrage est nécessaire.

    Fenêtre de terminal
    # Débloquer les paquets
    sudo apt-mark unhold kubelet kubectl
    # Installer les nouvelles versions
    sudo apt-get install -y kubelet=1.36.0-1.1 kubectl=1.36.0-1.1
    # Rebloquer
    sudo apt-mark hold kubelet kubectl
    # Recharger la configuration systemd et redémarrer kubelet
    sudo systemctl daemon-reload
    sudo systemctl restart kubelet

    Vérification : le nœud control plane doit afficher la nouvelle version.

    Fenêtre de terminal
    kubectl get nodes
    # NAME STATUS ROLES AGE VERSION
    # cp1 Ready control-plane 8m v1.36.0 # ← nouvelle version
    # worker1 Ready <none> 7m v1.35.4 # ← workers à upgrader
    # worker2 Ready <none> 7m v1.35.4
  6. Vérifier les add-ons après upgrade

    Après l'upgrade, vérifiez que les composants critiques fonctionnent toujours :

    Fenêtre de terminal
    # Vérifier CoreDNS
    kubectl get pods -n kube-system -l k8s-app=kube-dns
    # Tous doivent être Running
    # Vérifier kube-proxy
    kubectl get pods -n kube-system -l k8s-app=kube-proxy
    # DaemonSet, un pod par nœud
    # Vérifier le CNI (Flannel ou Calico)
    kubectl get pods -n kube-flannel # ou -n calico-system
    # Tous doivent être Running
    # Vérifier qu'il n'y a pas d'erreurs dans les events
    kubectl get events -A --field-selector type=Warning --sort-by='.lastTimestamp' | head -10

Les workers se mettent à jour un par un, pour que les applications restent servies. La séquence drain, upgrade, uncordon garantit que les Pods sont déplacés avant toute intervention sur le nœud.

  1. Drainer le nœud pour évacuer les pods

    Le drain déplace proprement les Pods vers les autres nœuds. Les DaemonSets restent, puisqu'ils doivent tourner partout. Les Pods portant un volume emptyDir perdent leurs données, ce que l'option --delete-emptydir-data vous fait accepter explicitement.

    Fenêtre de terminal
    # Depuis le control plane
    kubectl drain worker1 --ignore-daemonsets --delete-emptydir-data
    # node/worker1 cordoned
    # Warning: ignoring DaemonSet-managed Pods: calico-system/calico-node-xxxxx,
    # calico-system/csi-node-driver-xxxxx, kube-system/kube-proxy-xxxxx
    # evicting pod kube-system/coredns-589f44dc88-xxxxx
    # evicting pod calico-system/calico-typha-55d4f6d9c7-xxxxx
    # pod/coredns-589f44dc88-xxxxx evicted
    # pod/calico-typha-55d4f6d9c7-xxxxx evicted
    # node/worker1 drained
    # Vérifier que le nœud est cordoned (SchedulingDisabled)
    kubectl get nodes
    # NAME STATUS ROLES AGE VERSION
    # cp1 Ready control-plane 10m v1.36.0
    # worker1 Ready,SchedulingDisabled <none> 9m v1.35.4
    # worker2 Ready <none> 9m v1.35.4
  2. Mettre à jour kubeadm et kubelet sur le worker

    Ces commandes se lancent sur le worker, et non sur le control plane. kubeadm upgrade node met à jour la configuration locale du kubelet, pas le plan de contrôle.

    Fenêtre de terminal
    # Sur le worker (SSH kube@192.168.122.20)
    # Ajouter le dépôt v1.36 (idem que sur le control plane)
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key \
    | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-1.36-keyring.gpg
    echo "deb [signed-by=/etc/apt/keyrings/kubernetes-1.36-keyring.gpg] \
    https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /" \
    | sudo tee /etc/apt/sources.list.d/kubernetes-1.36.list
    sudo apt-mark unhold kubeadm kubelet
    sudo apt-get update
    sudo apt-get install -y kubeadm=1.36.0-1.1 kubelet=1.36.0-1.1
    sudo apt-mark hold kubeadm kubelet
    # Mettre à jour la configuration kubelet locale
    sudo kubeadm upgrade node
    # [upgrade/preflight] Skipping prepull. Not a control plane node.
    # [upgrade/control-plane] Skipping phase. Not a control plane node.
    # [upgrade/kubeconfig] Skipping phase. Not a control plane node.
    # [upgrade] Backing up kubelet config file to /etc/kubernetes/tmp/kubeadm-kubelet-config-...
    # [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
    # [upgrade/kubelet-config] The kubelet configuration for this node was successfully upgraded!
    # [upgrade/addon] Skipping the addon/coredns phase. Not a control plane node.
    # [upgrade/addon] Skipping the addon/kube-proxy phase. Not a control plane node.
    # Redémarrer kubelet
    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
  3. Remettre le nœud en service

    Une fois la mise à jour terminée, autorisez à nouveau le scheduling sur ce nœud. Les pods en attente seront automatiquement schedulés dessus.

    Fenêtre de terminal
    # Depuis le control plane
    kubectl uncordon worker1
    # node/worker1 uncordoned
    # Vérifier que le nœud est Ready et à la nouvelle version
    kubectl get nodes
    # NAME STATUS ROLES AGE VERSION
    # cp1 Ready control-plane 10m v1.36.0
    # worker1 Ready <none> 10m v1.36.0
    # worker2 Ready <none> 10m v1.35.4 # ← prochain à upgrader

Répétez cette procédure pour chaque worker. En production, attendez que les pods soient stables sur un nœud avant de passer au suivant.

etcd est la base de données du cluster Kubernetes. Elle contient tout : Deployments, Services, Secrets, ConfigMaps, état des pods, etc. Si etcd est corrompu ou perdu sans sauvegarde, le cluster est irrécupérable. La sauvegarde régulière d'etcd est donc critique en production.

etcd tourne comme un pod statique sur chaque control plane. Ses données vivent dans /var/lib/etcd/, ses certificats dans /etc/kubernetes/pki/etcd/. Toute interaction avec lui en exige trois :

CertificatCheminRôle
CA/etc/kubernetes/pki/etcd/ca.crtAutorité de certification etcd
Certificat client/etc/kubernetes/pki/etcd/server.crtAuthentification du client
Clé privée/etc/kubernetes/pki/etcd/server.keySignature des requêtes

La sauvegarde crée un snapshot de toutes les données etcd. Cette opération est non-bloquante et peut être exécutée sur un cluster en production.

Première surprise : etcdctl n'est pas installé par kubeadm. Vérifié sur un nœud de cette formation, command -v etcdctl ne rend rien. Deux voies existent, et la seconde ne demande rien à installer.

Ou bien vous installez le paquet client d'etcd sur le nœud, ou bien vous appelez le binaire là où il vit déjà, dans le conteneur etcd lui-même :

Fenêtre de terminal
kubectl -n kube-system exec etcd-<nom-du-noeud> -- etcdctl version
Sortie
etcdctl version: 3.7.0
API version: 3.7

Appelez etcdctl directement, sans passer par sh -c : l'image d'etcd ne contient aucun shell, et toute commande enveloppée échoue sur "sh": executable file not found in $PATH.

Fenêtre de terminal
# Créer un snapshot d'etcd, depuis le conteneur etcd
kubectl -n kube-system exec etcd-<nom-du-noeud> -- etcdctl snapshot save /tmp/etcd-backup.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key
Sortie
{"level":"info",...,"msg":"fetched snapshot","size":"5.0 MB","took":"33.181333ms"}
Snapshot saved at /tmp/etcd-backup.db

Deux précisions sur cette commande, toutes deux vérifiées. ETCDCTL_API=3 n'est plus nécessaire : la variable était obligatoire du temps d'etcd 3.3 et 3.4, et le client 3.7 l'ignore purement et simplement, y compris si vous lui donnez la valeur 2. Et le certificat client employé ici est healthcheck-client, prévu pour cet usage ; server.crt fonctionne aussi, mais c'est détourner un certificat de serveur pour un rôle de client.

Vérification : contrôlez que le snapshot est valide et contient des données. En etcd 3.7, etcdctl snapshot n'offre plus que save ; la vérification appartient à etcdutl :

Fenêtre de terminal
etcdutl snapshot status /tmp/etcd-backup.db --write-out=table
Sortie, relevée sur etcd 3.7.0
┌──────────┬──────────┬────────────┬────────────┬─────────┐
│ HASH │ REVISION │ TOTAL KEYS │ TOTAL SIZE │ VERSION │
├──────────┼──────────┼────────────┼────────────┼─────────┤
│ 642e758b │ 257204 │ 360 │ 40 MB │ 3.7.0 │
└──────────┴──────────┴────────────┴────────────┴─────────┘

Il n'existe pas d'alternative avec etcdctl, et c'est le piège. Appeler etcdctl snapshot status n'échoue pas : la commande affiche l'aide de snapshot et sort en 0. Un script de sauvegarde qui vérifie ainsi son instantané signale un succès sans avoir ouvert le fichier.

Un snapshot laissé sur le nœud qu'il sauvegarde ne sert à rien le jour où ce nœud disparaît. Quatre règles séparent une sauvegarde d'une illusion de sauvegarde. Externalisez les snapshots vers S3, GCS, NFS ou tout stockage distant. Chiffrez-les : ils contiennent l'intégralité des Secrets du cluster en clair, ce qui en fait l'objet le plus sensible de votre infrastructure. Testez la restauration sur un cluster jetable, tous les mois, faute de quoi vous ne saurez pas si vos fichiers sont exploitables. Et gardez au moins sept jours d'historique, parce qu'une corruption ne se remarque pas toujours le jour même.

La restauration d'etcd est une opération destructive : elle écrase toutes les données actuelles. Utilisez-la uniquement pour récupérer d'un désastre ou migrer vers un nouveau cluster.

  1. Arrêter le control plane

    Arrêtez kubelet pour que les pods statiques (API server, etc.) s'arrêtent. etcd doit être arrêté pour la restauration.

    Fenêtre de terminal
    sudo systemctl stop kubelet
  2. Restaurer le snapshot dans un nouveau répertoire

    La restauration crée un nouveau répertoire de données. Elle ne modifie pas /var/lib/etcd directement.

    Fenêtre de terminal
    etcdutl snapshot restore /tmp/etcd-backup.db \
    --data-dir=/var/lib/etcd-restored

    etcdctl snapshot restore a été supprimé en 3.7 et répond Error: unknown flag: --data-dir. Contrairement à status, l'échec est ici bruyant.

  3. Remplacer les données etcd

    Sauvegardez l'ancien répertoire (au cas où) et mettez le nouveau en place.

    Fenêtre de terminal
    sudo mv /var/lib/etcd /var/lib/etcd.old
    sudo mv /var/lib/etcd-restored /var/lib/etcd
  4. Redémarrer le control plane

    kubelet va redémarrer les pods statiques, y compris etcd avec les données restaurées.

    Fenêtre de terminal
    sudo systemctl start kubelet
  5. Vérifier que le cluster fonctionne

    Attendez quelques minutes que tous les composants redémarrent.

    Fenêtre de terminal
    kubectl get nodes
    kubectl get pods -A

Les certificats Kubernetes générés par kubeadm expirent après 1 an par défaut. Un certificat expiré bloque complètement l'accès au cluster : kubectl ne fonctionne plus, les nœuds perdent le contact avec l'API server.

Cette commande affiche la date d'expiration de chaque certificat. Planifiez le renouvellement avant l'expiration.

Fenêtre de terminal
sudo kubeadm certs check-expiration
# CERTIFICATE EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
# admin.conf Mar 15, 2027 10:00 UTC 364d no
# apiserver Mar 15, 2027 10:00 UTC 364d no
# apiserver-etcd-client Mar 15, 2027 10:00 UTC 364d no
# ...

Surveillez la colonne RESIDUAL TIME. Renouvelez quand il reste moins de 30 jours.

Une distinction évite de surveiller ce qui n'en a pas besoin. Kubeadm configure par défaut la rotation automatique des certificats client du kubelet : ceux-là se renouvellent seuls avant expiration, vous n'avez rien à faire. Ceux qui exigent une intervention manuelle sont ceux du control plane : API server, etcd, controller-manager, scheduler, et les fichiers kubeconfig qui vont avec. C'est exactement la liste que rend kubeadm certs check-expiration.

Le renouvellement régénère tous les certificats avec une nouvelle validité d'un an. Les clients (kubectl, kubelet des workers) devront récupérer les nouveaux certificats.

Fenêtre de terminal
# Renouveler tous les certificats du control plane
sudo kubeadm certs renew all
# Redémarrer les composants pour qu'ils utilisent les nouveaux certificats
sudo systemctl restart kubelet

Après le renouvellement, mettez à jour le fichier kubeconfig de votre utilisateur :

Fenêtre de terminal
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Automatisez l'alerte, gardez la main sur l'opération. Une tâche hebdomadaire qui lance kubeadm certs check-expiration et prévient au-delà d'un seuil coûte trois lignes et rend un vrai service. Déclencher le renouvellement sans surveillance est une autre affaire : l'opération redémarre les composants du control plane, et vous ne voulez pas qu'elle se produise à trois heures du matin sans personne pour constater le résultat.

Le dépannage représente 30% de l'examen CKA, c'est le domaine le plus lourd. Cette section vous donne une méthodologie structurée et les commandes essentielles pour diagnostiquer rapidement les problèmes.

Face à un problème, suivez cette approche en couches :

  1. État du nœud : kubectl get nodes, le nœud est-il Ready ?
  2. Pods système : kubectl get pods -n kube-system, les composants tournent-ils ?
  3. kubelet : journalctl -u kubelet, le kubelet démarre-t-il ?
  4. Composants : logs des pods statiques avec crictl
  5. Réseau : connectivité entre pods, DNS, Services

Ce tableau couvre les situations les plus fréquentes. Pour chaque symptôme, la cause la plus probable et la solution directe.

SymptômeCause probableComment diagnostiquerSolution
Nœud NotReady après initCNI non installékubectl describe node → "network plugin is not ready"Installer Flannel ou Calico
Nœud NotReady après rebootkubelet ne démarre pasjournalctl -u kubelet -xeVérifier swap, cgroups, containerd
connection refused :6443API server downcrictl ps -a | grep kube-apiVérifier manifest /etc/kubernetes/manifests/kube-apiserver.yaml
Token expiréToken > 24hkubeadm token listkubeadm token create --print-join-command
Certificat expiréCertificats > 1 ankubeadm certs check-expirationkubeadm certs renew all
Pods en PendingPas de nœud disponible ou ressources insuffisanteskubectl describe pod <pod> → EventsAjouter des workers ou libérer des ressources
Pods en CrashLoopBackOffApplication qui crashkubectl logs <pod> --previousCorriger l'application
CoreDNS en CrashLoopBackOffProblème CNI ou loop DNSkubectl logs -n kube-system coredns-xxxVérifier CNI, /etc/resolv.conf
Worker ne joint pasConnectivité, token, ou cgroupsjournalctl -u kubelet -xe sur le workerVérifier les 3 causes ci-dessus

Le kubelet est l'agent qui pilote les conteneurs sur chaque nœud. S'il ne tourne pas, rien ne tourne sur ce nœud. Les commandes ci-dessous sont les premiers réflexes de diagnostic.

Fenêtre de terminal
# État du service kubelet
sudo systemctl status kubelet
# Si inactive ou failed, regarder pourquoi :
# Logs détaillés du kubelet (les 100 dernières lignes)
sudo journalctl -u kubelet -n 100 --no-pager
# Logs en temps réel (pour voir ce qui se passe pendant un test)
sudo journalctl -u kubelet -f
# Erreurs spécifiques (--since pour limiter)
sudo journalctl -u kubelet --since "5 minutes ago" | grep -i error

Erreurs fréquentes dans les logs kubelet :

Message d'erreurCauseSolution
failed to run Kubelet: running with swap on is not supportedSwap actifswapoff -a && sed -i '/swap/d' /etc/fstab
failed to get cgroupMauvais cgroup driverVérifier SystemdCgroup = true dans containerd
Unable to connect to the serverAPI server injoignableVérifier réseau et certificats
certificate has expiredCertificats expiréskubeadm certs renew all

Les composants du control plane (kube-apiserver, etcd, kube-scheduler, kube-controller-manager) sont des pods statiques gérés directement par kubelet. Leurs manifests sont dans /etc/kubernetes/manifests/. Pour voir leurs logs, utilisez crictl (pas kubectl, qui nécessite un API server fonctionnel).

Fenêtre de terminal
# Lister tous les conteneurs (y compris arrêtés)
sudo crictl ps -a
# Logs de l'API server
sudo crictl logs $(sudo crictl ps -q --name kube-apiserver)
# Logs d'etcd
sudo crictl logs $(sudo crictl ps -q --name etcd)
# Logs du scheduler
sudo crictl logs $(sudo crictl ps -q --name kube-scheduler)
# Logs du controller-manager
sudo crictl logs $(sudo crictl ps -q --name kube-controller-manager)

etcd est la base de données du cluster. Si etcd ne fonctionne pas, l'API server ne peut pas stocker ni récupérer de données.

Fenêtre de terminal
# Statut des endpoints etcd (santé du cluster etcd)
sudo etcdctl endpoint health \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
# Sortie attendue :
# https://127.0.0.1:2379 is healthy: successfully committed proposal: took = 2.5ms
# Liste des membres du cluster etcd (utile en HA)
sudo etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key

Les problèmes réseau sont fréquents. Ces commandes vérifient que les pods peuvent communiquer et que le DNS fonctionne.

Fenêtre de terminal
# Vérifier que CoreDNS fonctionne
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Les pods doivent être Running
# Tester la résolution DNS depuis un pod temporaire
kubectl run dnstest --image=busybox:1.28 --rm -it --restart=Never -- nslookup kubernetes
# Doit résoudre vers l'IP du service kubernetes (10.96.0.1 par défaut)
# Si DNS échoue, vérifier le service CoreDNS
kubectl get svc -n kube-system kube-dns
kubectl get endpointslice -n kube-system -l kubernetes.io/service-name=kube-dns
# Vérifier les logs CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns

Quand un Service ne répond pas, vérifiez la chaîne complète : Service → Endpoints → Pods.

Fenêtre de terminal
# Lister les Services avec leurs ClusterIP
kubectl get svc -A
# Vérifier qu'un Service a des endpoints (pods cibles)
kubectl get endpointslice -l kubernetes.io/service-name=<service-name>
# Si ENDPOINTS est vide, le selector ne matche aucun pod
# Vérifier les EndpointSlices (nouveau format)
kubectl get endpointslices -l kubernetes.io/service-name=<service-name>
# Tester la connectivité vers un Service depuis un pod
kubectl run curltest --image=curlimages/curl:8.21.0 --rm -it --restart=Never -- \
curl -v http://<service-name>.<namespace>.svc.cluster.local

Les événements Kubernetes sont une mine d'or pour le dépannage. Ils montrent ce qui s'est passé récemment.

Fenêtre de terminal
# Tous les événements du cluster, triés par date
kubectl get events -A --sort-by='.lastTimestamp'
# Événements d'un namespace spécifique
kubectl get events -n <namespace>
# Événements liés à un objet spécifique
kubectl describe pod <pod-name> # Section Events en bas
# Filtrer les événements de type Warning
kubectl get events -A --field-selector type=Warning

Si un nœud est trop corrompu pour être réparé, réinitialisez-le complètement. Cette procédure efface toute la configuration Kubernetes du nœud.

Fenêtre de terminal
# Réinitialiser kubeadm (supprime certificats et configuration)
sudo kubeadm reset -f
# Supprimer la configuration CNI
sudo rm -rf /etc/cni/net.d
# Supprimer le kubeconfig utilisateur
sudo rm -rf $HOME/.kube
# Nettoyer les règles iptables créées par kube-proxy
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -X
# Optionnel : nettoyer les images containerd
sudo crictl rmi --all

Après le reset, vous pouvez rejoindre le cluster avec kubeadm join.

Ce guide couvre principalement le domaine Cluster Architecture, Installation & Configuration (25%) de la CKA. Le tableau ci-dessous résume ce que vous avez appris et ce qui reste à maîtriser dans d'autres guides.

Ce guide est aligné sur le domaine Cluster Architecture, Installation & Configuration, qui pèse 25 % de l'épreuve. Le tableau met en regard chaque objectif du programme et la section qui le traite, ce qui permet de repérer ce qu'il reste à revoir plutôt que de relire la page entière. Trois points reviennent le plus souvent à l'examen : le nombre impair de control planes, imposé par le quorum d'etcd, la différence entre etcd empilé et etcd externe, et la sauvegarde puis restauration d'etcd, qui est un classique.

Objectif CKASection du guideCompétence acquise
Préparer l'infrastructure pour installer un clusterCréation des VMs, prérequisConfiguration kernel, containerd, swap
Créer et gérer des clusters avec kubeadmInitialisation, join workerskubeadm init, kubeadm join, CNI
Gérer le cycle de vie d'un clusterMise à jourkubeadm upgrade, drain/uncordon
Implémenter un control plane HAConfiguration HAHAProxy, stacked etcd, join control planes
Comprendre les interfaces d'extension (CNI, CSI, CRI)Installation containerd, CNICRI avec containerd, CNI avec Flannel
Sauvegarder et restaurer etcdSauvegarde/restaurationetcdctl snapshot save/restore

Ce que ce guide ne couvre pas (à étudier ailleurs)

Section intitulée « Ce que ce guide ne couvre pas (à étudier ailleurs) »

La CKA évalue aussi des compétences non couvertes ici. Consultez les guides dédiés :

Domaine CKAPoidsCe qu'il faut savoirGuide recommandé
Troubleshooting30%Logs, events, debug réseau, kubeletCe guide + pratique intensive
Services & Networking20%Services, Ingress, NetworkPolicies, Gateway API, CoreDNSGuide Réseau Kubernetes
Workloads & Scheduling15%Deployments, rollouts, ConfigMaps, Secrets, schedulingGuide Workloads Kubernetes
Storage10%PV, PVC, StorageClass, access modesGuide Storage Kubernetes

La CKA attend que vous maîtrisiez certains outils de packaging et configuration :

OutilCe qu'il faut savoirCouvert ici ?
kubectlToutes les commandes, output JSON/YAML, --dry-runPartiellement
kubeadminit, join, upgrade, certs, reset✅ Oui
etcdctlsnapshot save/restore, endpoint health✅ Oui
Helminstall, upgrade, rollback, values❌ Guide dédié
Kustomizebases, overlays, patches❌ Guide dédié
crictlps, logs, images✅ Oui

Dix questions sur ce qui coûte le plus cher en pratique comme à l'examen : l'ordre des opérations d'une mise à jour, l'emplacement des fichiers que kubeadm produit, et la façon d'atteindre etcdctl sur un nœud qui ne le fournit pas.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
8 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce guide vous a appris à installer et maintenir un cluster Kubernetes avec kubeadm. Voici les points essentiels à retenir :

  1. kubeadm est l'outil officiel de bootstrap, il génère certificats, manifests et configure le cluster selon les standards upstream

  2. containerd avec SystemdCgroup = true est obligatoire, c'est l'erreur n°1 des débutants, vérifiez toujours ce paramètre

  3. Les modules kernel overlay et br_netfilter sont indispensables pour le réseau des conteneurs

  4. Le swap doit être désactivé, kubelet refuse de démarrer si le swap est actif

  5. Le CNI (Flannel, Calico) s'installe après kubeadm init, sans lui, les nœuds restent en NotReady

  6. Spécifiez --control-plane-endpoint même pour un cluster single-node, cela permet de passer en HA plus tard

  7. Mettez à jour control plane avant workers, respectez le version skew (1 version mineure max)

  8. Sauvegardez etcd régulièrement, c'est la seule source de vérité du cluster, sans backup = perte totale

  9. Renouvelez les certificats avant expiration, ils expirent après 1 an, planifiez le renouvellement

  10. Pour dépanner : kubelet logs → crictl logs → events → describe, suivez cette progression systématiquement

Pour aller plus loin, consultez ces ressources officielles :

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn