Aller au contenu
English
Conteneurs & Orchestration medium

Troubleshooting du cluster Kubernetes

55 min de lecture

logo kubernetes

Le domaine Troubleshooting pèse 30% de la CKA et inclut souvent des pannes d'infrastructure à diagnostiquer puis corriger rapidement. Ce guide cible principalement les clusters déployés avec kubeadm : control plane en static Pods, certificats gérés par kubeadm, et diagnostic local via kubelet et le runtime CRI. Il couvre le dépannage du control plane (kube-apiserver, scheduler, controller-manager), d'etcd, des nœuds workers, des certificats et des composants réseau du cluster.

Ce guide traite les pannes d'infrastructure : composants système, nœuds, certificats, etcd. Deux autres pages couvrent ce qu'il laisse de côté. Le debug d'applications prend le relais dès que le cluster est sain et que c'est un Pod ou un Service qui dysfonctionne. La méthode de diagnostic d'incident donne la démarche générale, celle qui vous dit dans lequel des deux cas vous vous trouvez avant d'ouvrir l'un ou l'autre.

Si vous n'avez pas de cluster accessible en SSH, un cluster kind fait l'affaire pour presque tout ce guide : ses nœuds sont de vrais nœuds kubeadm, avec /etc/kubernetes/manifests/, crictl, le journal du kubelet et kubeadm certs check-expiration. Remplacez simplement ssh <nœud> par docker exec <nom-du-nœud>.

Face à une panne d'infrastructure, la tentation est de sauter directement à l'hypothèse la plus probable. C'est le meilleur moyen de perdre dix minutes sur un composant sain. La séquence ci-dessous part du plus général, le cluster répond-il, pour descendre vers le plus précis, quel fichier est mal configuré, et s'arrête dès qu'une étape renvoie une anomalie.

  1. État global : le cluster répond-il ? (kubectl cluster-info, kubectl get nodes)
  2. Composants système : les Pods kube-system tournent-ils ? (kubectl get pods -n kube-system)
  3. Nœuds : tous les nœuds sont-ils Ready ? (kubectl get nodes -o wide)
  4. Logs : que disent kubelet et les composants ? (journalctl, kubectl logs)
  5. Certificats : sont-ils valides ? (kubeadm certs check-expiration)
  6. Réseau : les composants communiquent-ils ? (ss, curl, crictl)

Un raccourci est permis, et il fait gagner du temps. Si le symptôme désigne clairement un worker isolé, entrez directement à l'étape 3 sur ce nœud. En revanche, dès que kubectl ne répond plus, aucune des étapes suivantes n'est jouable depuis votre poste : basculez immédiatement en diagnostic local sur le control plane, où le kubelet et le runtime restent interrogeables même quand l'API server est à terre.

Avant de plonger dans les scénarios, voici les commandes clés à maîtriser pour le diagnostic d'infrastructure :

Fenêtre de terminal
# === Depuis votre poste (si kubectl fonctionne) ===
kubectl cluster-info # API server accessible ?
kubectl get nodes -o wide # État de tous les nœuds
kubectl get pods -n kube-system -o wide # Composants du cluster
kubectl get events -n kube-system --sort-by='.lastTimestamp' | tail -20
# === Depuis un nœud (en SSH) ===
systemctl status kubelet # État du service kubelet
journalctl -u kubelet --no-pager --since "10 min ago" | tail -50
crictl ps -a # Conteneurs (via CRI)
crictl pods # Pods vus par le runtime
ss -tlnp | grep -E '6443|2379|10250|10256|10259|10257' # Ports critiques

crictl est l'outil le plus pratique pour inspecter les conteneurs du runtime CRI, mais il n'est pas installé partout. À défaut, appuyez-vous sur les journaux du kubelet, journalctl -u kubelet, et sur les outils natifs du runtime, ctr pour containerd.

Les ports critiques d'un cluster Kubernetes. La colonne d'écoute est celle qu'on oublie, et elle explique la moitié des diagnostics ratés : trois de ces ports ne sont joignables que depuis le nœud lui-même. Sonder 10257 depuis votre poste ne prouve donc rien, pas plus qu'un curl vers 10259.

PortComposantÉcoute surRôle
6443kube-apiservertoutes les interfacesEntrée principale de l'API
2379etcd (client)boucle locale et IP du nœudRequêtes lecture/écriture
2380etcd (peer)IP du nœudRéplication entre membres
10250kubelettoutes les interfacesAPI kubelet (métriques, exec)
10256kube-proxytoutes les interfacesHealth check
10257kube-controller-managerboucle locale seulementHealth check
10259kube-schedulerboucle locale seulementHealth check

Relevé sur un nœud control plane en 1.37. Les deux ports de santé du contrôleur et du scheduler ne sont pas exposés, et c'est volontaire : c'est le kubelet, local lui aussi, qui interroge leurs sondes.


Sur un cluster kubeadm, les composants du control plane tournent en static Pods : leurs manifests YAML sont dans /etc/kubernetes/manifests/. Le kubelet les surveille et les redémarre automatiquement en cas de crash. Une erreur dans un manifest = le composant ne démarre pas.

C'est la panne la plus critique, sans API Server, kubectl est inutilisable.

Symptômes :

  • kubectl retourne The connection to the server was refused ou timeout
  • Les autres composants écrivent des erreurs de connexion dans leurs logs

Diagnostic :

Fenêtre de terminal
# 1. Vérifier si le processus tourne
ssh <control-plane> "crictl ps | grep kube-apiserver"
# 2. Si pas de processus, vérifier le manifest
ssh <control-plane> "cat /etc/kubernetes/manifests/kube-apiserver.yaml | head -30"
# 3. Logs kubelet (c'est lui qui lance les static Pods)
ssh <control-plane> "journalctl -u kubelet --since '5 min ago' | grep -i apiserver"
# 4. Si le conteneur existe mais crash
ssh <control-plane> "crictl logs \$(crictl ps -a --name kube-apiserver -q | head -1)"

Causes fréquentes (CKA) :

CauseSymptôme dans les logsCorrection
Erreur dans le manifest YAMLfailed to create pod dans kubeletCorriger la syntaxe YAML dans /etc/kubernetes/manifests/kube-apiserver.yaml
Mauvais chemin de certificatopen /etc/kubernetes/pki/xxx: no such fileVérifier les chemins --tls-cert-file, --tls-private-key-file
Port déjà utilisébind: address already in useIdentifier le processus sur le port 6443 (ss -tlnp | grep 6443)
etcd inaccessibleconnection refused vers :2379Réparer etcd d'abord (voir section suivante)
Certificats expiréscertificate has expiredRenouveler avec kubeadm certs renew apiserver

Sans scheduler, les nouveaux Pods restent en Pending indéfiniment, ils ne sont assignés à aucun nœud.

Symptômes :

  • Nouveaux Pods en état Pending, sans événement Scheduled dans les events
  • Peu ou pas d'événements récents liés au placement des Pods
  • Le Pod kube-scheduler est absent ou en CrashLoopBackOff dans kube-system

Diagnostic :

Fenêtre de terminal
# Vérifier si le scheduler tourne
kubectl get pods -n kube-system -l component=kube-scheduler
# Logs du scheduler
kubectl logs -n kube-system kube-scheduler-<node>
# Si kubectl ne fonctionne pas, aller sur le nœud
ssh <control-plane> "crictl ps -a | grep scheduler"
ssh <control-plane> "crictl logs \$(crictl ps -a --name kube-scheduler -q | head -1)"

Correction :

Fenêtre de terminal
# Vérifier le manifest
cat /etc/kubernetes/manifests/kube-scheduler.yaml
# Comparer avec la config attendue
# Vérifier notamment :
# - --kubeconfig=/etc/kubernetes/scheduler.conf
# - image: registry.k8s.io/kube-scheduler:v1.XX.X
# - ports: containerPort 10259
# Après correction du manifest, kubelet redémarre le Pod automatiquement
# Vérifier :
crictl ps | grep scheduler

Sans kube-controller-manager, de nombreuses boucles de contrôle cessent de converger : réplication, gestion des Jobs, node lifecycle, service accounts, garbage collection, etc.

Symptômes :

  • Les ReplicaSets ne créent pas de nouveaux Pods
  • Les Deployments ne progressent pas
  • Le node lifecycle controller ne met plus à jour l'état des nœuds
  • Les certificats ne sont plus automatiquement renouvelés

Diagnostic :

Fenêtre de terminal
# Vérifier l'état
kubectl get pods -n kube-system -l component=kube-controller-manager
# Logs
kubectl logs -n kube-system kube-controller-manager-<node>
# Ou directement sur le nœud :
crictl logs $(crictl ps -a --name kube-controller -q | head -1)

Erreurs classiques dans les manifests :

Fenêtre de terminal
# Vérifier le manifest
cat /etc/kubernetes/manifests/kube-controller-manager.yaml
# Points à contrôler :
# --kubeconfig=/etc/kubernetes/controller-manager.conf
# --root-ca-file=/etc/kubernetes/pki/ca.crt
# --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
# --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
# --service-account-private-key-file=/etc/kubernetes/pki/sa.key

Pour tout composant du control plane qui ne démarre pas, appliquez cette séquence :

  1. Identifier : le conteneur tourne-t-il ? (crictl ps -a | grep <composant>)
  2. Logs conteneur, crictl logs <container-id>, chercher l'erreur exacte
  3. Logs kubelet, journalctl -u kubelet | grep <composant>, le kubelet signale-t-il une erreur de manifest ?
  4. Manifest, cat /etc/kubernetes/manifests/<composant>.yaml, comparer avec un manifest fonctionnel
  5. Corriger : éditer le manifest, kubelet détecte le changement et redémarre le Pod (quelques secondes)
  6. Valider, crictl ps | grep <composant> puis kubectl get pods -n kube-system

etcd est la base de données clé/valeur qui stocke tout l'état du cluster. Si etcd est en panne, l'API Server ne peut ni lire ni écrire, le cluster est paralysé.

etcd tourne lui aussi en static Pod sur un cluster kubeadm, ce qui crée une dépendance circulaire au diagnostic : si etcd est mort, l'API Server l'est aussi, et kubectl logs ne répond plus. Commencez donc par la commande kubectl, mais basculez immédiatement sur crictl logs en SSH dès qu'elle échoue.

Fenêtre de terminal
# État du Pod etcd
kubectl get pods -n kube-system -l component=etcd
# Logs etcd
kubectl logs -n kube-system etcd-<node>
# Ou en SSH sur le nœud
crictl logs $(crictl ps -a --name etcd -q | head -1)

etcdctl n'utilise aucune configuration implicite : les trois options --cacert, --cert et --key sont obligatoires, sinon la connexion TLS est refusée avant même d'atteindre la base. La variable ETCDCTL_API=3 que l'on voit encore dans beaucoup de procédures ne sert plus à rien depuis etcd 3.4 et est ignorée par le client 3.7.

Trois commandes, deux binaires. snapshot save s'adresse au serveur etcd vivant, c'est etcdctl. snapshot status et snapshot restore travaillent sur un fichier hors ligne, c'est etcdutl. Appeler etcdctl snapshot status n'affiche aucune erreur : la commande sort en 0 sans rien vérifier, ce qui fait passer un script de sauvegarde pour vert alors qu'il ne contrôle rien. endpoint health répond par un simple verdict de disponibilité, tandis que member list révèle la composition du cluster etcd, indispensable pour diagnostiquer une perte de quorum en HA.

Fenêtre de terminal
# Depuis le nœud control plane
etcdctl \
--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 \
endpoint health
# Réponse attendue :
# https://127.0.0.1:2379 is healthy: successfully committed proposal: took = 2.5ms
# Vérifier les membres du cluster etcd
etcdctl \
--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 \
member list -w table

Le message d'erreur d'etcd suffit presque toujours à trancher, à condition de savoir le lire. Deux familles dominent : le manque d'espace (etcd applique un quota et bascule en lecture seule bien avant que le disque soit plein) et les certificats, qui expirent au même rythme que le reste de ceux de kubeadm. Les cas de corruption de données sont plus rares mais imposent une restauration.

CauseSymptômeCorrection
Espace disque pleinmvcc: database space exceededCompacter et défragmenter (voir ci-dessous)
Certificats expiréscertificate has expired or is not yet validkubeadm certs renew etcd-server
Données corrompueswal: crc mismatch ou crash en boucleRestaurer depuis un snapshot
Perte de quorum (HA)etcdserver: no leaderRestaurer un membre ou le cluster complet
Mauvais data-diropen /var/lib/etcd/: permission deniedCorriger les permissions ou le chemin dans le manifest

etcd conserve l'historique de toutes les révisions de chaque clé. Le compactage supprime les révisions antérieures à celle que vous indiquez : l'opération est irréversible, les anciennes versions ne sont plus consultables. Il ne rend toutefois pas l'espace au système de fichiers, c'est le rôle de la défragmentation, qui réécrit le fichier de base. Cette seconde étape bloque brièvement les écritures, à lancer membre par membre sur un cluster HA.

Quand etcd signale database space exceeded :

Fenêtre de terminal
# Récupérer la révision actuelle
rev=$(etcdctl \
--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 \
endpoint status --write-out="json" | jq '.[0].Status.header.revision')
# Compacter
etcdctl \
--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 \
compact "$rev"
# Défragmenter
etcdctl \
--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 \
defrag

Si etcd est irrécupérable, restaurez depuis un snapshot (voir le guide Sauvegarder et restaurer Kubernetes pour la procédure complète).

La procédure ci-dessous correspond au cas le plus fréquent en lab ou en CKA : cluster kubeadm avec un seul control plane. Sur un cluster HA, la restauration etcd dépend de la topologie, des membres et du quorum, et doit être traitée séparément.

Fenêtre de terminal
# Arrêter etcd (renommer le manifest pour que kubelet l'arrête)
mv /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/etcd.yaml.bak
# Supprimer l'ancien data-dir
rm -rf /var/lib/etcd
# Restaurer depuis le snapshot, avec etcdutl
etcdutl snapshot restore /backup/etcd-snapshot.db \
--data-dir=/var/lib/etcd
# Remettre le manifest en place
mv /etc/kubernetes/manifests/etcd.yaml.bak /etc/kubernetes/manifests/etcd.yaml
# Attendre que kubelet redémarre etcd, puis vérifier
crictl ps | grep etcd
kubectl get pods -n kube-system -l component=etcd

C'est l'exercice qui revient le plus souvent à l'examen, et il se joue sur un détail : etcdctl n'a aucune configuration implicite. Les trois options --cacert, --cert et --key sont obligatoires à chaque appel, et leurs chemins par défaut sur un cluster kubeadm sont dans /etc/kubernetes/pki/etcd/. Un candidat qui doit les chercher le jour J perd la moitié du temps alloué.


Un nœud qui passe en NotReady ne reçoit plus de nouveaux Pods, et les Pods existants sont évincés au bout de cinq minutes. Ce délai n'est plus réglé par un drapeau du contrôleur : il vient d'une tolérance posée automatiquement sur chaque Pod à sa création. Vous pouvez la lire sur n'importe lequel :

Fenêtre de terminal
kubectl get pod <pod> -o jsonpath='{.spec.tolerations}'
Sortie
node.kubernetes.io/not-ready = 300s (NoExecute)
node.kubernetes.io/unreachable = 300s (NoExecute)

La nuance est utile en pratique : puisque c'est une tolérance, elle se règle par charge de travail. Un service qui doit basculer vite peut abaisser ses tolerationSeconds, un traitement long peut les allonger, là où un réglage global imposait la même valeur à tout le cluster.

kubectl get nodes signale le symptôme, mais c'est kubectl describe node qui contient la réponse. Les conditions listées en bas de la sortie distinguent deux situations très différentes : un nœud à court de ressources (MemoryPressure, DiskPressure ou PIDPressure à True) et un nœud qui ne communique plus du tout, où seule la condition Ready bascule avec un message de type kubelet stopped posting node status.

Fenêtre de terminal
# Identifier le nœud en échec
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# worker-1 NotReady <none> 10d v1.37.0
# Examiner les conditions
kubectl describe node worker-1 | grep -A15 Conditions
# Conditions normales (tout à False sauf Ready) :
# MemoryPressure False
# DiskPressure False
# PIDPressure False
# Ready True ← Doit être True

Le kubelet est le composant fondamental de chaque nœud, s'il ne tourne pas, le nœud ne communique plus avec le control plane.

Fenêtre de terminal
# Se connecter au nœud
ssh worker-1
# Vérifier l'état du service
systemctl status kubelet
# Si "inactive (dead)" ou "activating (auto-restart)"
# Lire les logs pour l'erreur exacte
journalctl -u kubelet --no-pager --since "5 min ago" | tail -50

Causes fréquentes et corrections :

CauseMessage dans journalctlCorrection
kubelet arrêtéActive: inactive (dead)systemctl start kubelet
Crash en bouclecode=exited, status=1/FAILURELire le message d'erreur exact dans les logs
Mauvaise configfailed to load kubelet configVérifier /var/lib/kubelet/config.yaml
Container runtime downfailed to get sandbox image ou CRI connection errorsystemctl status containerd puis systemctl restart containerd
Certificat expirécertificate has expiredRenouveler le certificat kubelet (voir section certificats)
Mauvais serveur APIdial tcp <ip>:6443: connect: connection refusedVérifier /etc/kubernetes/kubelet.conf, l'URL du serveur API est-elle correcte ?

Les pannes de kubelet posées à l'examen sont toujours subtiles : un chemin de configuration modifié d'un caractère, un drapeau inconnu, un runtime arrêté. Aucune ne se devine, toutes se lisent. La séquence qui les résout ne change jamais : systemctl status kubelet pour savoir si le service tient, journalctl -u kubelet pour l'erreur exacte, la correction, puis systemctl restart kubelet. Résistez à l'envie de redémarrer avant d'avoir lu : un kubelet qui repart sans que la cause soit traitée retombe quarante secondes plus tard.

Le kubelet a besoin d'un container runtime fonctionnel. Si containerd (ou CRI-O) est arrêté, aucun Pod ne peut démarrer.

Fenêtre de terminal
# Vérifier containerd
systemctl status containerd
# Si arrêté :
systemctl start containerd
systemctl enable containerd
# Vérifier que le kubelet se reconnecte
systemctl restart kubelet
journalctl -u kubelet --since "1 min ago" | grep -i "connected"

Pression sur les ressources (MemoryPressure, DiskPressure, PIDPressure)

Section intitulée « Pression sur les ressources (MemoryPressure, DiskPressure, PIDPressure) »

Quand un nœud manque de ressources, le kubelet place des conditions qui déclenchent l'éviction des Pods.

Fenêtre de terminal
# Vérifier les conditions du nœud
kubectl describe node <node> | grep -A5 Conditions
# Sur le nœud : diagnostic des ressources
df -h /var/lib/kubelet # Espace disque
free -m # Mémoire disponible
cat /proc/sys/kernel/pid_max # Limite PID
ls /proc | grep -c '^[0-9]' # Nombre de processus actifs

Les conditions MemoryPressure, DiskPressure et PIDPressure sont dérivées de signaux calculés par le kubelet. Les valeurs exactes dépendent de sa configuration. Par défaut sur Linux, Kubernetes applique notamment memory.available<100Mi, nodefs.available<10% et imagefs.available<15%.

ConditionIndicationRemarque
MemoryPressureMémoire réellement disponible insuffisanteLe kubelet utilise ses propres signaux, pas uniquement free -m
DiskPressurePression sur nodefs, imagefs ou containerfsPas seulement /var/lib/kubelet
PIDPressurePlus assez de PID disponiblesDépend des seuils configurés du nœud

Correction :

Fenêtre de terminal
# DiskPressure : libérer de l'espace
crictl rmi --prune # Supprimer les images inutilisées
journalctl --vacuum-size=500M # Nettoyer les logs
# MemoryPressure : identifier les processus gourmands
ps aux --sort=-%mem | head -10
# Si un processus hors-Kubernetes consomme trop, l'arrêter
# Après nettoyage, le kubelet réévalue périodiquement les signaux d'éviction
# selon son housekeeping-interval (10 secondes par défaut)

Un cluster kubeadm utilise des dizaines de certificats TLS pour sécuriser les communications entre composants. Sur un cluster géré par kubeadm, les certificats clients générés par kubeadm expirent en général après 1 an. Leur expiration rend le cluster inutilisable.

kubeadm certs check-expiration liste tous les certificats gérés par kubeadm, avec leur date d'expiration et le temps restant. La colonne RESIDUAL TIME est celle à surveiller : c'est le seul indicateur qui anticipe la panne, puisqu'un certificat expiré ne prévient pas avant de bloquer net les communications. La vérification par openssl sert de recours quand kubeadm n'est pas sur la machine.

Fenêtre de terminal
# Commande kubeadm (la plus simple)
kubeadm certs check-expiration
# Résultat type :
# CERTIFICATE EXPIRES RESIDUAL TIME
# admin.conf Mar 25, 2027 08:30 UTC 364d
# apiserver Mar 25, 2027 08:30 UTC 364d
# apiserver-etcd-client Mar 25, 2027 08:30 UTC 364d
# apiserver-kubelet-client Mar 25, 2027 08:30 UTC 364d
# ...
# etcd-server Mar 25, 2027 08:30 UTC 364d
# Vérification manuelle d'un certificat spécifique
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates -subject
# notBefore=Mar 25 08:30:00 2026 GMT
# notAfter=Mar 25 08:30:00 2027 GMT

Le renouvellement seul ne suffit pas : les composants du control plane chargent leurs certificats au démarrage et continuent d'utiliser les anciens jusqu'à leur redémarrage. D'où la manipulation qui suit le renew, sortir puis remettre chaque manifest du répertoire surveillé par le kubelet, ce qui force la recréation du static Pod. Pensez également à recopier admin.conf dans votre ~/.kube/config, sinon kubectl continue de présenter un certificat périmé.

Fenêtre de terminal
# Renouveler TOUS les certificats
kubeadm certs renew all
# Ou renouveler un certificat spécifique
kubeadm certs renew apiserver
kubeadm certs renew etcd-server
kubeadm certs renew apiserver-etcd-client
# Après renouvellement, redémarrer les composants du control plane
# Méthode : déplacer puis remettre les manifests
cd /etc/kubernetes/manifests
mv kube-apiserver.yaml /tmp/ && sleep 5 && mv /tmp/kube-apiserver.yaml .
mv kube-controller-manager.yaml /tmp/ && sleep 5 && mv /tmp/kube-controller-manager.yaml .
mv kube-scheduler.yaml /tmp/ && sleep 5 && mv /tmp/kube-scheduler.yaml .
mv etcd.yaml /tmp/ && sleep 5 && mv /tmp/etcd.yaml .
# Vérifier que tout redémarre
kubectl get pods -n kube-system

Si un worker node perd la connexion à cause d'un certificat kubelet expiré :

Fenêtre de terminal
# Sur le worker : vérifier le certificat
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
# Si expiré et la rotation automatique est activée :
# La rotation se fait automatiquement si le kubelet a le flag :
# --rotate-certificates=true (dans /var/lib/kubelet/config.yaml)
# Vérifier la config
grep rotateCertificates /var/lib/kubelet/config.yaml
# rotateCertificates: true ← doit être présent
# Si la rotation automatique ne permet plus la récupération :
# 1. Vérifier d'abord le bootstrap kubeconfig et la configuration de rotation
cat /etc/kubernetes/bootstrap-kubelet.conf
# 2. Sur le control plane, créer un nouveau token
kubeadm token create --print-join-command
# 3. Sur le worker, arrêter le kubelet et supprimer l'ancien certificat
systemctl stop kubelet
rm /var/lib/kubelet/pki/kubelet-client-current.pem
# 4. Mettre à jour le bootstrap-kubeconfig avec le nouveau token, puis redémarrer
systemctl start kubelet

Trois commandes suffisent à traiter tout ce que l'examen pose sur les certificats, et la troisième est celle qu'on oublie : kubeadm certs check-expiration pour constater, kubeadm certs renew all pour renouveler, puis le redémarrage des static Pods, sans lequel les composants continuent d'utiliser les anciens certificats chargés en mémoire. Un renouvellement qui ne change rien à l'état du cluster est presque toujours un redémarrage oublié.


Les problèmes réseau au niveau cluster empêchent la communication entre les composants, et sont souvent les plus difficiles à diagnostiquer.

Le plugin CNI, Calico, Flannel ou Cilium, porte le réseau entre Pods. S'il ne fonctionne pas, les Pods ne communiquent plus et les nœuds nouvellement ajoutés restent en NotReady.

Fenêtre de terminal
# Vérifier les Pods CNI
kubectl get pods -n kube-system | grep -iE 'calico|flannel|cilium|weave'
# Si des Pods CNI sont en CrashLoopBackOff
kubectl logs -n kube-system <cni-pod> --previous
# Vérifier la config CNI sur un nœud
ls /etc/cni/net.d/
cat /etc/cni/net.d/*.conflist

Symptômes d'un CNI défaillant :

SymptômeDiagnosticCorrection
Nœuds NotReady + NetworkUnavailable: Truekubectl describe nodeRéinstaller le CNI ou vérifier ses Pods
Pods ne communiquent pas entre nœudskubectl exec pod-A -- ping <pod-B-IP>Vérifier les logs CNI et les routes réseau
Pods bloqués en ContainerCreatingkubectl describe pod → erreur networkPluginVérifier /etc/cni/net.d/ et le binaire CNI dans /opt/cni/bin/

kube-proxy gère les règles réseau qui routent les Services. Sans lui, ni ClusterIP ni NodePort ne répondent. Trois modes existent, et savoir lequel tourne change les commandes de diagnostic : iptables reste le mode par défaut, nftables est stable depuis la 1.33 et devient la cible recommandée sur les noyaux récents, et ipvs est déprécié depuis la 1.35. Un cluster encore en ipvs n'est pas en panne, mais il est sur un chemin qui se ferme : la migration se prépare maintenant, pas le jour du retrait.

Fenêtre de terminal
# Vérifier le DaemonSet kube-proxy
kubectl get ds -n kube-system kube-proxy
kubectl get pods -n kube-system -l k8s-app=kube-proxy
# Logs
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=30
# Identifier le mode de fonctionnement
kubectl logs -n kube-system -l k8s-app=kube-proxy | grep "Using .* Proxier"
# Vérifier les règles selon le mode (iptables par défaut sur kubeadm)
ssh <node> "iptables -t nat -L KUBE-SERVICES | head -20"

CoreDNS assure la résolution DNS interne du cluster. S'il est en panne, les Pods ne peuvent plus résoudre les noms de Services.

Fenêtre de terminal
# Vérifier les Pods CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Tester la résolution depuis un Pod
kubectl run dnstest --rm -it --image=busybox:1.37@sha256:9db7b59979c38555a39def84a31fb98b5296952f9e3afd4f6f11f05b07adfab0 --restart=Never -- nslookup kubernetes.default
# Si la résolution échoue, vérifier les logs CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=30
# Vérifier le Service kube-dns
kubectl get svc -n kube-system kube-dns
kubectl get endpointslice -n kube-system -l kubernetes.io/service-name=kube-dns

Pour le DNS, le bon ordre de vérification est : Pod CoreDNSlogsService kube-dnsendpointstest nslookup depuis un Pod. Cette approche méthodique permet d'isoler rapidement l'origine du problème.


Les quatre exercices ci-dessous reproduisent des pannes réellement posées en examen, avec la commande qui les provoque. Cassez le cluster volontairement, puis diagnostiquez sans regarder la solution : c'est le seul moyen de vérifier que la méthode est acquise et pas seulement lue. Travaillez sur un cluster de lab jetable, jamais sur un cluster utilisé par quelqu'un d'autre. Les durées indiquées correspondent au temps alloué le jour de l'examen.

Le kube-apiserver ne démarre pas. Diagnostiquez et corrigez.

Simulation :

Fenêtre de terminal
# Sur le control plane (en SSH)
# Introduire une erreur dans le manifest
sudo sed -i 's/--etcd-servers=https:\/\/127.0.0.1:2379/--etcd-servers=https:\/\/127.0.0.1:2399/' \
/etc/kubernetes/manifests/kube-apiserver.yaml
Solution
Fenêtre de terminal
# kubectl ne fonctionne plus
kubectl get nodes
# The connection to the server was refused
# Aller sur le nœud control plane en SSH
ssh control-plane
# Vérifier si le conteneur tourne
crictl ps -a | grep apiserver
# Le conteneur est en état "Exited"
# Lire les logs du conteneur
crictl logs $(crictl ps -a --name kube-apiserver -q | head -1)
# "dial tcp 127.0.0.1:2399: connect: connection refused"
# → Le port etcd est mauvais (2399 au lieu de 2379)
# Corriger le manifest
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
# Changer --etcd-servers=https://127.0.0.1:2399
# en --etcd-servers=https://127.0.0.1:2379
# Kubelet détecte le changement et redémarre le Pod
# Patienter ~30 secondes
crictl ps | grep apiserver
# Le conteneur est en state "Running"
# Vérifier
kubectl get nodes
# Les nœuds réapparaissent

Un nœud worker est NotReady. Identifiez la cause et corrigez.

Simulation :

Fenêtre de terminal
# Sur le worker (en SSH)
sudo sed -i 's|/var/lib/kubelet/config.yaml|/var/lib/kubelet/config-bad.yaml|' \
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo systemctl daemon-reload
sudo systemctl restart kubelet
Solution
Fenêtre de terminal
# Sur le control plane
kubectl get nodes
# worker-1 NotReady
# Se connecter au worker
ssh worker-1
# Vérifier kubelet
systemctl status kubelet
# Active: activating (auto-restart)
# Lire les logs
journalctl -u kubelet --since "2 min ago" | tail -20
# "failed to load kubelet config file, error: failed to read kubelet
# config file /var/lib/kubelet/config-bad.yaml, error: open ... no such file"
# Le fichier de config est mauvais, vérifier le service
cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
# --config=/var/lib/kubelet/config-bad.yaml ← erreur
# Corriger
sudo sed -i 's|config-bad.yaml|config.yaml|' \
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# Vérifier
systemctl status kubelet
# Active: running
# Sur le control plane (après ~40 secondes)
kubectl get nodes
# worker-1 Ready

Le cluster ne répond plus, les certificats sont expirés.

Solution (procédure)
Fenêtre de terminal
# Vérifier l'expiration
kubeadm certs check-expiration
# CERTIFICATE EXPIRES RESIDUAL TIME
# apiserver Mar 25, 2025 08:30 UTC EXPIRED
# Renouveler tous les certificats
kubeadm certs renew all
# certificate embedded in admin.conf renewed
# certificate apiserver renewed
# ...
# Redémarrer les composants du control plane
crictl pods --name kube-apiserver -q | xargs crictl rmp
crictl pods --name kube-controller -q | xargs crictl rmp
crictl pods --name kube-scheduler -q | xargs crictl rmp
crictl pods --name etcd -q | xargs crictl rmp
# Kubelet les redémarre automatiquement
# Mettre à jour le kubeconfig admin
cp /etc/kubernetes/admin.conf ~/.kube/config
# Vérifier
kubectl get nodes

Le cluster est dans un état incohérent, vous devez restaurer depuis un snapshot etcd.

Solution (procédure)
Fenêtre de terminal
# 1. Arrêter etcd
mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd.yaml
# 2. Attendre que le conteneur s'arrête
crictl ps | grep etcd # Doit être vide après ~10 secondes
# 3. Sauvegarder l'ancien data-dir
mv /var/lib/etcd /var/lib/etcd.bak
# 4. Restaurer depuis le snapshot, avec etcdutl
etcdutl snapshot restore /backup/etcd-snapshot.db \
--data-dir=/var/lib/etcd
# 5. Remettre le manifest
mv /tmp/etcd.yaml /etc/kubernetes/manifests/etcd.yaml
# 6. Attendre le redémarrage
sleep 15
crictl ps | grep etcd
# 7. Vérifier l'état du cluster
kubectl get nodes
kubectl get pods -A

Ces quatre blocs condensent le guide en commandes prêtes à taper, regroupées par symptôme de départ. Mémorisez-les dans cet ordre : pendant l'incident, il ne faut plus avoir à réfléchir à la méthode.

Ces quatre commandes se lancent depuis votre poste et suffisent à localiser la panne au niveau du cluster, du nœud ou d'un composant précis.

Fenêtre de terminal
kubectl cluster-info # API accessible ?
kubectl get nodes -o wide # Nœuds Ready ?
kubectl get pods -n kube-system # Composants OK ?
kubectl get events -n kube-system --sort-by='.lastTimestamp' | tail -10

À sortir dès que kubectl ne répond plus : tout se fait en local sur le nœud, par le runtime et les journaux du kubelet.

Fenêtre de terminal
ssh <control-plane>
crictl ps -a | grep -E 'apiserver|scheduler|controller|etcd'
journalctl -u kubelet --since "5 min ago" | grep -i error
cat /etc/kubernetes/manifests/<composant>.yaml # Vérifier syntaxe

Sur un worker, l'ordre compte : le kubelet dépend du container runtime, qui dépend des ressources disponibles. Remonter cette chaîne évite de redémarrer un service qui n'était pas en cause.

Fenêtre de terminal
ssh <worker>
systemctl status kubelet # Service actif ?
journalctl -u kubelet --since "5 min ago" | tail -20 # Erreur ?
systemctl status containerd # Runtime OK ?
df -h && free -m # Ressources ?

Réflexe à avoir devant tout message contenant certificate has expired, quel que soit le composant qui l'émet. Le redémarrage des Pods statiques fait partie de la procédure : il n'est pas optionnel.

Fenêtre de terminal
kubeadm certs check-expiration # Expiration ?
kubeadm certs renew all # Renouveler
# Puis redémarrer les static Pods du control plane

  1. Si kubectl ne répond pas : connectez-vous en SSH au control plane et utilisez crictl + journalctl
  2. Static Pods du control plane : manifest dans /etc/kubernetes/manifests/, kubelet les redémarre automatiquement à toute modification
  3. kubelet arrêté = nœud NotReady, diagnostic avec systemctl status kubelet puis journalctl -u kubelet
  4. etcd défaillant = cluster paralysé, vérifier avec etcdctl endpoint health, restaurer depuis un snapshot si nécessaire
  5. Certificats kubeadm expirent après 1 an, vérifier avec kubeadm certs check-expiration, renouveler avec kubeadm certs renew all
  6. CNI en panne = nœuds NotReady avec NetworkUnavailable: True, vérifier les Pods CNI dans kube-system
  7. Méthode systématique : état global → composants → logs → manifest/config → corriger → valider

Sept questions sur ce qui sépare un diagnostic rapide d'une demi-heure perdue : où vivent les manifestes du control plane, ce que describe node répond vraiment, et quel port se sonde depuis où.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
6 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

  • Préparer une maintenance : La panne qu'on provoque soi-même se prépare, à l'inverse de celle qu'on vient de diagnostiquer.
  • Gérer les nœuds : Sortir un nœud du service proprement, geste qui suit souvent un diagnostic de panne matérielle.
  • Mettre à jour un cluster : L'opération qui casse le plus de plans de contrôle, et celle où ce diagnostic sert le plus.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. 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