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é)
kubectlinstallé 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 :
| Phase | Description |
|---|---|
| Pending | Pod accepté par le cluster mais un ou plusieurs conteneurs ne sont pas encore en cours d’exécution |
| Running | Pod assigné à un nœud et tous les conteneurs démarrés |
| Succeeded | Tous les conteneurs se sont terminés avec succès (code de sortie 0) |
| Failed | Tous les conteneurs se sont terminés et au moins un est sorti en erreur |
| Unknown | L’é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
latestquand seuls des tags versionnés sont publiés) imagePullSecretsmanquants 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
| État | Signification | Solution Typique |
|---|---|---|
| CreateContainerConfigError | ConfigMap ou Secret manquant | Vérifiez que les ConfigMaps et Secrets référencés existent |
| RunContainerError | Échec du runtime conteneur | Vérifiez le security context, les montages de volumes et les logs du runtime sur le nœud |
| Evicted | Nœud sous pression de ressources | Vérifiez les conditions de pression disque/mémoire du nœud et définissez des requests appropriés |
| Init:Error | Init container échoué | Vérifiez les logs de l’init container avec kubectl logs <pod> -c <init-container> |
| Terminating (bloqué) | Finalizers bloquant la suppression | Vé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’Échec | Première Commande | Information Clé |
|---|---|---|
| CrashLoopBackOff | kubectl logs <pod> --previous | Sortie d’erreur de l’application avant le crash |
| ImagePullBackOff | kubectl describe pod <pod> | Nom de l’image, erreurs de pull, références de secrets |
| Pending | kubectl describe pod <pod> | Raison de l’échec du scheduler dans la section Events |
| OOMKilled | kubectl describe pod <pod> | Raison de terminaison dans Last State et code de sortie |
| CreateContainerConfigError | kubectl get configmap,secret -n <ns> | Ressources référencées manquantes |
| Evicted | kubectl describe node <node> | Conditions de pression de ressources du nœud |
| Init:Error | kubectl 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 tagslatest), 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 logsutilise 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 nodedans la sectionConditions. -
PVC dans la mauvaise zone de disponibilité : Dans les environnements cloud, un PVC lié à un volume dans
us-east-1ane peut pas être monté par un pod planifié sur un nœud dansus-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
--previouspour 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 podetkubectl get eventspour comprendre le contexte de l’échec - Utilisez
kubectl debugpour 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