Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Troubleshooting Kubernetes

22 min de lecture

logo kubernetes

Le troubleshooting représente 30% de l’examen CKA. Ce guide présente une méthodologie systématique pour diagnostiquer et résoudre les problèmes les plus courants : Pods en échec, problèmes de scheduling, erreurs réseau, et défaillances de cluster.

Appliquez cette approche systématique pour tout problème Kubernetes :

  1. Observer — Quel est l’état actuel ? (kubectl get, kubectl describe)
  2. Logs — Que disent les logs ? (kubectl logs, journald)
  3. Events — Quels événements récents ? (kubectl get events)
  4. Comparer — L’état actuel correspond-il à l’état désiré ?
  5. Isoler — Quel composant est en cause ?
  6. Corriger — Appliquer le fix et valider

Fenêtre de terminal
# Alias recommandés
alias k=kubectl
alias kd='kubectl describe'
alias kl='kubectl logs'
alias kg='kubectl get'
# Vue d'ensemble rapide
kubectl get all -A
kubectl get events -A --sort-by='.lastTimestamp' | tail -20
# État des nœuds
kubectl get nodes -o wide
kubectl top nodes
# État des Pods
kubectl get pods -A -o wide | grep -v Running

ÉtatSignificationAction
PendingEn attente de schedulingVérifier ressources, affinités, taints
ContainerCreatingImage en cours de pull ou volumes montésVérifier events, image, PVC
RunningConteneur actifVérifier readiness, logs si problème
CrashLoopBackOffRedémarre en boucleVérifier logs, commande, ressources
ErrorConteneur terminé avec erreurVérifier logs du conteneur
ImagePullBackOffÉchec de téléchargement imageVérifier nom image, credentials
ErrImagePullErreur initiale de pullVérifier registry, réseau
TerminatingEn cours de suppressionVérifier finalizers, preStop hooks

Un Pod reste Pending quand le scheduler ne trouve pas de nœud approprié.

Fenêtre de terminal
# Diagnostic
kubectl describe pod <pod-name> | grep -A10 Events
# Messages courants et solutions
# "Insufficient cpu" → Augmenter ressources cluster ou réduire requests
# "Insufficient memory" → Idem
# "node(s) had untolerated taint" → Ajouter toleration ou retirer taint
# "node(s) didn't match Pod's node affinity" → Vérifier labels des nœuds
# "persistentvolumeclaim not found" → Créer le PVC manquant

Checklist Pending :

Fenêtre de terminal
# Ressources disponibles sur les nœuds
kubectl describe nodes | grep -A5 "Allocated resources"
# Taints des nœuds
kubectl get nodes -o custom-columns='NAME:.metadata.name,TAINTS:.spec.taints[*].key'
# PVC en attente
kubectl get pvc -A | grep -v Bound

Le conteneur démarre mais crash immédiatement, Kubernetes le redémarre en boucle.

Fenêtre de terminal
# Logs du crash actuel
kubectl logs <pod-name>
# Logs du crash précédent
kubectl logs <pod-name> --previous
# Si multi-container
kubectl logs <pod-name> -c <container-name> --previous

Causes fréquentes :

CauseDiagnosticSolution
Command/Args invalidesLogs montrent erreur de syntaxeCorriger command/args
Config manquante”file not found”, “env not set”Vérifier ConfigMaps/Secrets
Dépendance indisponible”connection refused”Vérifier Services dépendants
OOMKilledkubectl describe pod → OOMKilledAugmenter memory limit
Permissions”permission denied”Vérifier securityContext, volumes
Fenêtre de terminal
# Vérifier si OOMKilled
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
# Détail du restart
kubectl describe pod <pod> | grep -A5 "Last State"

Kubernetes ne parvient pas à télécharger l’image.

Fenêtre de terminal
# Diagnostic
kubectl describe pod <pod> | grep -A5 "Events"
# Causes fréquentes :
# - Image inexistante : vérifier le nom exact
# - Registry privé : vérifier imagePullSecrets
# - Réseau : vérifier l'accès au registry depuis les nœuds

Vérifier les credentials :

Fenêtre de terminal
# Lister les secrets de type docker-registry
kubectl get secrets -A -o json | jq -r '.items[] | select(.type=="kubernetes.io/dockerconfigjson") | "\(.metadata.namespace)/\(.metadata.name)"'
# Vérifier qu'un Pod utilise le bon secret
kubectl get pod <pod> -o jsonpath='{.spec.imagePullSecrets}'

Depuis Kubernetes 1.25+, vous pouvez injecter un conteneur de debug dans un Pod running :

Fenêtre de terminal
# Ajouter un conteneur de debug
kubectl debug -it <pod-name> --image=busybox:1.36 --target=<container-name>
# Debug avec une copie du Pod
kubectl debug <pod-name> -it --copy-to=debug-pod --container=debug --image=busybox:1.36

Fenêtre de terminal
# Vue d'ensemble
kubectl get nodes -o wide
# Détail d'un nœud
kubectl describe node <node-name>
# Conditions importantes
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range .status.conditions[*]}{.type}={.status}{" "}{end}{"\n"}{end}'
ConditionTrue = problèmeDiagnostic
MemoryPressure⚠️Mémoire insuffisante
DiskPressure⚠️Disque plein
PIDPressure⚠️Trop de processus
NetworkUnavailable⚠️CNI non configuré
Ready✅ NormalNœud opérationnel
Fenêtre de terminal
# Diagnostic rapide
kubectl describe node <node> | grep -A5 Conditions
# Vérifier kubelet sur le nœud
ssh <node> "systemctl status kubelet"
ssh <node> "journalctl -u kubelet --since '10 minutes ago' | tail -50"
# Vérifier les certificats
ssh <node> "openssl x509 -in /var/lib/kubelet/pki/kubelet.crt -noout -dates"

Causes fréquentes :

SymptômeCause probableSolution
kubelet stoppedService crashésystemctl restart kubelet
certificate expiredCertificats expirésRenouveler avec kubeadm
connection refusedAPI server inaccessibleVérifier réseau, firewall
disk full/var/lib/kubelet pleinNettoyer images/logs
Fenêtre de terminal
# Voir l'utilisation CPU/mémoire
kubectl top nodes
# Détail par nœud
kubectl describe node <node> | grep -A10 "Allocated resources"
# Pods sur un nœud spécifique
kubectl get pods -A --field-selector spec.nodeName=<node> -o wide

Section 3 — Troubleshooting Services et Networking

Section intitulée « Section 3 — Troubleshooting Services et Networking »
  1. Vérifier que le Service existe et a des endpoints

    Fenêtre de terminal
    kubectl get svc <service>
    kubectl get endpoints <service>
  2. Vérifier que les labels correspondent

    Fenêtre de terminal
    # Labels du selector du Service
    kubectl get svc <service> -o jsonpath='{.spec.selector}'
    # Labels des Pods
    kubectl get pods -l <label-selector> --show-labels
  3. Vérifier que les Pods sont Ready

    Fenêtre de terminal
    kubectl get pods -l <label-selector>
    # Un Pod non-Ready n'apparaît pas dans les endpoints
  4. Tester la connectivité depuis un Pod

    Fenêtre de terminal
    kubectl run debug --rm -it --image=busybox:1.36 -- wget -qO- --timeout=2 <service>:<port>
Fenêtre de terminal
# Diagnostic
kubectl get endpoints <service>
# Si ENDPOINTS est <none> :
# 1. Vérifier le selector du Service
kubectl get svc <service> -o yaml | grep -A5 selector
# 2. Vérifier que des Pods matchent ce selector
kubectl get pods -l <key>=<value>
# 3. Vérifier que ces Pods sont Ready
kubectl get pods -l <key>=<value> -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}'
Fenêtre de terminal
# Tester la résolution DNS depuis un Pod
kubectl run dnstest --rm -it --image=busybox:1.36 -- nslookup kubernetes.default
# Vérifier que CoreDNS fonctionne
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns
# Vérifier la configuration DNS d'un Pod
kubectl exec <pod> -- cat /etc/resolv.conf
Fenêtre de terminal
# Lister les NetworkPolicies
kubectl get netpol -A
# Détail d'une policy
kubectl describe netpol <name>
# Test de connectivité
kubectl run tester --rm -it --image=nicolaka/netshoot -- curl -v <target>:<port>

Fenêtre de terminal
# Depuis un nœud control plane
kubectl cluster-info
# Vérifier les composants
kubectl get componentstatuses # Déprécié mais encore utile
kubectl get pods -n kube-system | grep -E 'api|controller|scheduler|etcd'
# Logs API server
kubectl logs -n kube-system kube-apiserver-<node>
# Ou sur le nœud :
journalctl -u kubelet | grep apiserver
Fenêtre de terminal
# État des Pods etcd
kubectl get pods -n kube-system -l component=etcd
# Logs etcd
kubectl logs -n kube-system etcd-<node>
# Santé du cluster etcd (depuis un control plane)
ETCDCTL_API=3 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
Fenêtre de terminal
# Pods kube-system
kubectl get pods -n kube-system | grep -E 'scheduler|controller'
# Logs
kubectl logs -n kube-system kube-scheduler-<node>
kubectl logs -n kube-system kube-controller-manager-<node>
# Vérifier les manifests statiques
ls -la /etc/kubernetes/manifests/

Fenêtre de terminal
# Voir les events
kubectl describe pod <pod> | grep -A10 Events
# Messages courants :
# "Liveness probe failed" → L'app ne répond pas, Pod sera redémarré
# "Readiness probe failed" → L'app n'est pas prête, retirée des endpoints

Checklist probes :

Fenêtre de terminal
# Tester manuellement l'endpoint de probe
kubectl exec <pod> -- wget -qO- http://localhost:<port>/<path>
kubectl exec <pod> -- curl -s http://localhost:<port>/<path>
# Vérifier la configuration des probes
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].livenessProbe}'
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].readinessProbe}'
Fenêtre de terminal
# Ressources utilisées vs limites
kubectl top pod <pod>
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].resources}'
# Throttling CPU (si limite trop basse)
kubectl exec <pod> -- cat /sys/fs/cgroup/cpu/cpu.cfs_throttled_periods
# Logs applicatifs
kubectl logs <pod> --tail=100 -f

Un Pod stuck-pod est Pending depuis 5 minutes. Identifiez la cause et corrigez.

Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: stuck-pod
spec:
nodeSelector:
disktype: nvme
containers:
- name: nginx
image: nginx:1.25
EOF
Solution
Fenêtre de terminal
# Diagnostic
kubectl describe pod stuck-pod | grep -A10 Events
# "0/X nodes are available: X node(s) didn't match Pod's node affinity"
# Vérifier les labels des nœuds
kubectl get nodes --show-labels | grep disktype
# Aucun nœud n'a le label disktype=nvme
# Solution A : Labelliser un nœud
kubectl label node <node-name> disktype=nvme
# Solution B : Retirer le nodeSelector
kubectl patch pod stuck-pod --type='json' -p='[{"op": "remove", "path": "/spec/nodeSelector"}]'
# Note : Patch de nodeSelector sur un Pod existant ne fonctionne pas, il faut recréer
# Solution pratique : Supprimer et recréer sans nodeSelector
kubectl delete pod stuck-pod
kubectl run stuck-pod --image=nginx:1.25

Diagnostiquez pourquoi ce Pod crash en boucle :

Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: crash-pod
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /config/app.conf && sleep 3600']
EOF
Solution
Fenêtre de terminal
# Diagnostic
kubectl describe pod crash-pod
kubectl logs crash-pod --previous
# "cat: can't open '/config/app.conf': No such file or directory"
# Le fichier /config/app.conf n'existe pas
# Solution : Créer un ConfigMap et le monter
kubectl create configmap app-config --from-literal=app.conf="key=value"
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: crash-pod-fixed
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /config/app.conf && sleep 3600']
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: app-config
EOF
kubectl delete pod crash-pod

Le Service web-svc ne route pas vers les Pods. Trouvez pourquoi.

Fenêtre de terminal
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: webapp # Attention ici
spec:
containers:
- name: nginx
image: nginx:1.25
---
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
selector:
app: web
ports:
- port: 80
EOF
Solution
Fenêtre de terminal
# Diagnostic
kubectl get endpoints web-svc
# ENDPOINTS: <none>
kubectl get svc web-svc -o jsonpath='{.spec.selector}'
# {"app":"web"}
kubectl get pods --show-labels
# app=webapp (pas app=web)
# Le selector du Service (app=web) ne matche pas les labels des Pods (app=webapp)
# Solution : Corriger les labels du Deployment
kubectl patch deployment web -p '{"spec":{"template":{"metadata":{"labels":{"app":"web"}}}}}'
# Vérifier
kubectl get endpoints web-svc
# Maintenant les endpoints apparaissent

Simulez et diagnostiquez un nœud NotReady.

Solution (concept)
Fenêtre de terminal
# Sur le nœud worker (SSH)
sudo systemctl stop kubelet
# Sur le control plane, observer
kubectl get nodes
# Le nœud passe NotReady après ~40 secondes (node-monitor-grace-period)
kubectl describe node <worker> | grep -A5 Conditions
# Ready: False - Kubelet stopped posting node status
# Diagnostic
ssh <worker> "systemctl status kubelet"
# inactive (dead)
# Solution
ssh <worker> "sudo systemctl start kubelet"
kubectl get nodes
# Ready

Fenêtre de terminal
kubectl get pod <name> # État actuel
kubectl describe pod <name> | tail -30 # Events
kubectl logs <name> --previous # Logs du crash précédent
kubectl get events --field-selector involvedObject.name=<name>
Fenêtre de terminal
kubectl get svc <name> # Ports, ClusterIP
kubectl get endpoints <name> # Pods backend
kubectl get pods -l <selector> -o wide # État des Pods
kubectl run test --rm -it --image=busybox -- wget -qO- <svc>:<port>
Fenêtre de terminal
kubectl describe node <name> | grep -A10 Conditions
ssh <node> "systemctl status kubelet"
ssh <node> "journalctl -u kubelet --since '10 min ago'"
ssh <node> "df -h; free -m" # Ressources système

  1. Toujours commencer par kubectl describe et kubectl get events
  2. Pod Pending = problème de scheduling (ressources, taints, affinités)
  3. CrashLoopBackOff = regarder kubectl logs --previous
  4. Service sans endpoints = vérifier que selector matche les labels des Pods Ready
  5. Node NotReady = vérifier kubelet et ses logs
  6. DNS = tester depuis un Pod avec nslookup/dig
  7. NetworkPolicy = peut bloquer silencieusement le trafic

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn