Les pods Kubernetes peuvent échouer de nombreuses manières, et chaque état d’échec raconte une histoire différente. Que vous fassiez face à CrashLoopBackOff, ImagePullBackOff, Pending, OOMKilled ou d’autres états d’erreur, savoir diagnostiquer et corriger ces problèmes de manière systématique est essentiel pour tout opérateur Kubernetes. Ce guide vous accompagne à travers les états d’échec les plus courants, les commandes kubectl pour les diagnostiquer et les stratégies éprouvées pour résoudre chacun d’entre eux.

Prérequis

  • Un cluster Kubernetes en fonctionnement (v1.24 ou ultérieur recommandé)
  • kubectl installé et configuré avec un accès au cluster
  • Compréhension de base des objets Kubernetes (pods, deployments, services)
  • Permissions pour lire les pods, événements et ressources de nœuds dans votre namespace cible
  • Familiarité avec les concepts de conteneurs (images, registres, limites de ressources)

Comprendre le Cycle de Vie des Pods et leurs États

Avant de plonger dans le dépannage, il est utile de comprendre le cycle de vie d’un pod dans Kubernetes. Un pod traverse plusieurs phases :

PhaseDescription
PendingPod accepté par le cluster mais un ou plusieurs conteneurs ne sont pas encore en cours d’exécution
RunningPod assigné à un nœud et tous les conteneurs démarrés
SucceededTous les conteneurs se sont terminés avec succès (code de sortie 0)
FailedTous les conteneurs se sont terminés et au moins un est sorti en erreur
UnknownL’état du pod ne peut pas être déterminé, généralement à cause d’une panne de communication avec le nœud

Au sein de ces phases, les conteneurs peuvent entrer dans des états d’attente spécifiques qui indiquent ce qui s’est mal passé. Ce sont les messages de statut que vous voyez dans la sortie de kubectl get pods — et ils sont votre premier indice de diagnostic.

États d’Échec Courants des Pods

CrashLoopBackOff

CrashLoopBackOff est l’échec de pod le plus courant que vous rencontrerez. Il signifie que le conteneur démarre, plante, et Kubernetes continue de le redémarrer avec des délais croissants (10s, 20s, 40s, jusqu’à 5 minutes).

Causes courantes :

  • Erreur applicative provoquant une sortie immédiate (configuration manquante, exception non gérée)
  • Variables d’environnement ou secrets montés manquants
  • Commande ou entrypoint incorrect dans le spec du conteneur
  • Health check (liveness probe) échouant trop agressivement
  • Dépendance à un service non disponible

Commandes de diagnostic :

# Vérifier le statut du pod et le nombre de redémarrages
kubectl get pods -o wide

# Voir les logs du dernier conteneur
kubectl logs <nom-pod> --previous

# Vérifier les événements du pod
kubectl describe pod <nom-pod>

Le flag --previous est critique — sans lui, vous pourriez obtenir des logs vides ou partiels parce que la nouvelle instance du conteneur vient juste de démarrer. La sortie de describe affiche le Last State avec le code de sortie, qui vous indique si le processus a planté (code de sortie 1) ou a été tué (code de sortie 137 pour OOM, 143 pour SIGTERM).

ImagePullBackOff

ImagePullBackOff signifie que Kubernetes ne peut pas télécharger l’image du conteneur depuis le registre. Le pod reste dans cet état, réessayant avec un backoff exponentiel.

Causes courantes :

  • Faute de frappe dans le nom ou le tag de l’image
  • Le tag de l’image n’existe pas (ex : référencer latest quand seuls des tags versionnés sont publiés)
  • imagePullSecrets manquants ou expirés
  • Registre privé sans identifiants configurés
  • Network policy ou pare-feu bloquant l’accès au registre
  • Limites de débit du registre (throttling Docker Hub)

Commandes de diagnostic :

# Vérifier la référence exacte de l'image
kubectl describe pod <nom-pod> | grep -A 5 "Image:"

# Chercher les erreurs de pull dans les événements
kubectl get events --field-selector involvedObject.name=<nom-pod>

# Vérifier que les imagePullSecrets existent
kubectl get secrets -n <namespace>

Pending

Un pod en état Pending signifie que le scheduler ne l’a pas encore assigné à un nœud. Cela peut persister indéfiniment si le problème sous-jacent n’est pas résolu.

Causes courantes :

  • CPU ou mémoire insuffisante sur tous les nœuds
  • Node selectors, taints ou affinities qu’aucun nœud ne satisfait
  • PersistentVolumeClaim (PVC) non lié — pas de PersistentVolume correspondant disponible
  • ResourceQuota dépassé dans le namespace
  • Trop de pods sur le cluster (limite max-pods sur les nœuds)

Commandes de diagnostic :

# Vérifier pourquoi le pod est en attente
kubectl describe pod <nom-pod>

# Voir les événements du scheduler
kubectl get events --sort-by='.lastTimestamp' -n <namespace>

# Vérifier les ressources du nœud
kubectl describe nodes | grep -A 5 "Allocated resources"

# Vérifier le statut du PVC si le pod utilise des volumes
kubectl get pvc -n <namespace>

OOMKilled

OOMKilled (code de sortie 137) signifie que le OOM killer du noyau Linux a terminé le conteneur parce qu’il a dépassé sa limite de mémoire.

Causes courantes :

  • Limite de mémoire trop basse pour l’application
  • Fuite de mémoire (memory leak) dans l’application
  • Taille du heap JVM non alignée avec la limite de mémoire du conteneur
  • Conteneurs sidecar consommant la mémoire partagée
  • Chargement de grands jeux de données en mémoire

Commandes de diagnostic :

# Vérifier la raison de la terminaison
kubectl describe pod <nom-pod> | grep -A 3 "Last State"

# Voir l'utilisation actuelle de mémoire (nécessite metrics-server)
kubectl top pod <nom-pod>

# Vérifier les limites configurées
kubectl get pod <nom-pod> -o jsonpath='{.spec.containers[*].resources}'

Autres États d’Échec

ÉtatSignificationSolution Typique
CreateContainerConfigErrorConfigMap ou Secret manquantVérifiez que les ConfigMaps et Secrets référencés existent
RunContainerErrorÉchec du runtime conteneurVérifiez le security context, les montages de volumes et les logs du runtime sur le nœud
EvictedNœud sous pression de ressourcesVérifiez les conditions de pression disque/mémoire du nœud et définissez des requests appropriés
Init:ErrorInit container échouéVérifiez les logs de l’init container avec kubectl logs <pod> -c <init-container>
Terminating (bloqué)Finalizers bloquant la suppressionVérifiez les finalizers avec kubectl get pod -o json et supprimez si c’est sûr

Diagnostiquer les Problèmes avec kubectl

Le processus de diagnostic suit un schéma cohérent quel que soit le type d’échec :

Étape 1 : Obtenir la Vue d’Ensemble

# Tous les pods dans le namespace avec leur statut
kubectl get pods -n <namespace> -o wide

# Événements récents triés par temps
kubectl get events --sort-by='.lastTimestamp' -n <namespace> | tail -20

Étape 2 : Approfondir l’Analyse du Pod

# Description complète du pod avec événements
kubectl describe pod <nom-pod> -n <namespace>

# Logs du conteneur (instance actuelle)
kubectl logs <nom-pod> -n <namespace>

# Logs du conteneur (instance précédente qui a planté)
kubectl logs <nom-pod> -n <namespace> --previous

# Logs d'un conteneur spécifique dans un pod multi-conteneur
kubectl logs <nom-pod> -c <nom-conteneur> -n <namespace>

Étape 3 : Vérifier le Nœud

# Conditions du nœud (pression disque, mémoire, PIDs)
kubectl describe node <nom-noeud> | grep -A 10 "Conditions"

# Allocation des ressources sur le nœud
kubectl describe node <nom-noeud> | grep -A 20 "Allocated resources"

Étape 4 : Débogage Interactif

# Exec dans un conteneur en cours d'exécution
kubectl exec -it <nom-pod> -- /bin/sh

# Utiliser un conteneur de débogage éphémère (K8s 1.23+)
kubectl debug -it <nom-pod> --image=busybox --target=<nom-conteneur>

# Lancer un pod de débogage dans le même namespace réseau
kubectl run debug --rm -it --image=busybox -- /bin/sh

Comparaison des Commandes kubectl

État d’ÉchecPremière CommandeInformation Clé
CrashLoopBackOffkubectl logs <pod> --previousSortie d’erreur de l’application avant le crash
ImagePullBackOffkubectl describe pod <pod>Nom de l’image, erreurs de pull, références de secrets
Pendingkubectl describe pod <pod>Raison de l’échec du scheduler dans la section Events
OOMKilledkubectl describe pod <pod>Raison de terminaison dans Last State et code de sortie
CreateContainerConfigErrorkubectl get configmap,secret -n <ns>Ressources référencées manquantes
Evictedkubectl describe node <node>Conditions de pression de ressources du nœud
Init:Errorkubectl logs <pod> -c <init-container>Logs d’échec de l’init container

Scénario Réel

Vous gérez un cluster de production exécutant une application de microservices. Après un déploiement, le pod payment-service continue de redémarrer et affiche CrashLoopBackOff. Voici comment diagnostiquer :

$ kubectl get pods -n production
NAME                              READY   STATUS             RESTARTS   AGE
payment-service-7d4f8b9c6-x2k9m  0/1     CrashLoopBackOff   5          8m
api-gateway-5c8f7d6b4-h3j7n      1/1     Running            0          2d
user-service-6b7c8d9e5-m4n8p     1/1     Running            0          2d

Vous vérifiez les logs du conteneur précédent :

$ kubectl logs payment-service-7d4f8b9c6-x2k9m --previous
2026-02-28 10:15:03 ERROR: Failed to connect to database
  ConnectionRefused: tcp://db-service:5432
2026-02-28 10:15:03 FATAL: Cannot start without database connection. Exiting.

L’application nécessite une connexion à la base de données au démarrage, mais db-service est inaccessible. Vous vérifiez le service :

$ kubectl get svc db-service -n production
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
db-service   ClusterIP   10.96.45.123   <none>        5432/TCP   30d

$ kubectl get endpoints db-service -n production
NAME         ENDPOINTS   AGE
db-service   <none>      30d

Pas d’endpoints — le pod de la base de données est en panne. Vous découvrez qu’il a été expulsé à cause de la pression disque sur le nœud :

$ kubectl get pods -n production | grep db
db-postgresql-0   0/1     Evicted   0   30d

La solution : libérer de l’espace disque sur le nœud (ou ajouter plus de nœuds), redémarrer le pod de la base de données, et le service de paiement récupère automatiquement. Vous ajoutez également un startupProbe avec un timeout généreux pour que le service de paiement attende la base de données au lieu de planter immédiatement.

Pièges et Cas Particuliers

  • Code de sortie 137 vs 143 : Le code de sortie 137 signifie que le conteneur a été tué par SIGKILL (généralement OOMKilled). Le code 143 signifie SIGTERM (arrêt gracieux). Ne les confondez pas — 137 nécessite une investigation mémoire, 143 est généralement normal pendant les rollouts.

  • Délai de CrashLoopBackOff : Kubernetes utilise un backoff exponentiel jusqu’à 5 minutes entre les redémarrages. Si vous corrigez le problème, vous devrez peut-être attendre ou supprimer le pod pour le redémarrer immédiatement.

  • ImagePullPolicy: Always : Si votre spec de pod utilise imagePullPolicy: Always (la valeur par défaut pour les tags latest), chaque redémarrage de pod déclenche un pull d’image. Cela peut causer ImagePullBackOff si le registre est temporairement inaccessible, même si l’image était précédemment en cache sur le nœud.

  • Resource requests vs limits : Les pods sont planifiés en fonction des requests, pas des limits. Un pod demandant 100Mi mais limité à 500Mi peut être OOMKilled à 500Mi même si le nœud a 2Gi de libre — la limite est appliquée indépendamment de la capacité du nœud.

  • Pods multi-conteneur : Dans un pod avec des sidecars, kubectl logs utilise par défaut le premier conteneur. Spécifiez toujours -c <nom-conteneur> lors du débogage de pods multi-conteneur.

  • Expulsions par stockage éphémère : Même si le CPU et la mémoire sont corrects, une utilisation élevée du stockage éphémère (logs, fichiers temporaires) peut déclencher l’expulsion. Vérifiez avec kubectl describe node dans la section Conditions.

  • PVC dans la mauvaise zone de disponibilité : Dans les environnements cloud, un PVC lié à un volume dans us-east-1a ne peut pas être monté par un pod planifié sur un nœud dans us-east-1b. Le pod reste en Pending sans erreur évidente.

  • Retard de résolution DNS : Les services nouvellement créés peuvent ne pas résoudre immédiatement. Si votre conteneur plante parce qu’il ne peut pas résoudre un nom de service, ajoutez un délai de démarrage ou une logique de retry au lieu de compter sur la propagation DNS instantanée.

Résumé

  • CrashLoopBackOff signifie que votre conteneur continue de planter — vérifiez les logs avec --previous pour voir l’erreur avant le crash
  • ImagePullBackOff indique un échec de téléchargement d’image — vérifiez le nom de l’image, le tag et les identifiants du registre
  • Pending signifie que le scheduler ne peut pas placer le pod — vérifiez la disponibilité des ressources, le statut du PVC et les règles d’affinité
  • OOMKilled (code de sortie 137) signifie que le conteneur a dépassé sa limite de mémoire — augmentez les limites ou optimisez l’utilisation mémoire
  • Commencez toujours par kubectl describe pod et kubectl get events pour comprendre le contexte de l’échec
  • Utilisez kubectl debug pour les conteneurs éphémères quand vous avez besoin de débogage interactif sans modifier le spec du pod
  • Configurez correctement les requests, limits et probes pour prévenir de nombreux échecs courants de pods avant qu’ils ne surviennent

Articles Connexes