Kubernetes Kubernetes

Déboguer un Pod en échec : describe, logs, exec et port-forward

Une checklist reproductible pour comprendre pourquoi un Pod est Pending, plante ou ne répond pas.

12 min Débutant

Tôt ou tard, un Pod ne démarre pas, redémarre sans cesse, ou tourne sans répondre. La solution est presque toujours visible à l’aide de quelques commandes kubectl, utilisées dans le bon ordre. Ce guide vous donne cet ordre : lire le statut, demander les événements, lire les logs, entrer dans le conteneur, puis suivre le chemin réseau.

Lire d’abord le statut

Commencez par la colonne de statut :

kubectl get pods -o wide

Les états les plus courants, et où regarder en premier :

  • Pending. Le Pod n’est pas assigné à un nœud. En général, il manque de CPU ou de mémoire, un quota est épuisé, ou un volume ne peut pas être lié. describe vous dira lequel.
  • ContainerCreating. Le nœud prépare le conteneur. Si cet état persiste, cherchez un volume manquant ou un téléchargement d’image lent.
  • ErrImagePull / ImagePullBackOff. L’image ne peut pas être téléchargée, le plus souvent à cause d’une faute de frappe dans le nom ou le tag, ou d’identifiants manquants pour un registre privé.
  • CrashLoopBackOff. Le conteneur démarre, se termine, puis est redémarré en boucle avec des délais croissants. L’application échoue : lisez ses logs.
  • CreateContainerConfigError. Le Pod référence un ConfigMap, un Secret ou une clé qui n’existe pas.
  • Running mais READY 0/1. Le conteneur est lancé mais échoue à sa readiness probe.
  • OOMKilled (visible dans describe). Le conteneur a dépassé sa limite de mémoire.

Demander à Kubernetes pourquoi : describe et les événements

kubectl describe pod POD_NAME

Descendez jusqu’à la section Events en bas. Elle se lit comme une chronologie : Scheduled, Pulling, Failed, BackOff. Des messages comme 0/3 nodes are available: insufficient cpu ou Failed to pull image "ngnix:1.25" nomment généralement le problème sans détour. Plus haut, regardez le State et le Last State de chaque conteneur pour les codes de sortie et les raisons. Pour une vue d’ensemble de ce qui s’est passé dans le namespace :

kubectl get events --sort-by=.metadata.creationTimestamp

Lire la sortie du conteneur

kubectl logs POD_NAME
kubectl logs POD_NAME -c CONTAINER_NAME
kubectl logs POD_NAME --all-containers
kubectl logs POD_NAME --previous
kubectl logs POD_NAME -f --since=10m --tail=100

Utilisez -c quand un Pod a plusieurs conteneurs, -f pour suivre en continu, --since et --tail pour limiter la sortie, et --previous pour lire les logs de la dernière instance plantée, ce dont vous avez besoin pour une boucle de plantage. Pour essayer, créez un Deployment dont le conteneur échoue volontairement :

kubectl create deployment crashy --image=busybox:1.36 -- \
  sh -c 'echo starting; echo "fatal: DB_URL is not set" >&2; exit 1'
kubectl get pods -l app=crashy
kubectl logs deployment/crashy --previous

Le statut devient CrashLoopBackOff, et le log explique pourquoi. Nettoyez avec kubectl delete deployment crashy.

Gardez en tête que les logs appartiennent au Pod. Quand il est supprimé ou remplacé, ses logs disparaissent avec lui : pour conserver un historique, il faut un collecteur de logs comme Fluentd ou Fluent Bit qui les envoie vers un stockage durable.

Entrer dedans : exec, port-forward et cp

kubectl exec -it POD_NAME -- sh
kubectl exec POD_NAME -- env
kubectl port-forward pod/POD_NAME 8080:80
curl http://localhost:8080
kubectl cp POD_NAME:/path/to/file ./file

exec exécute une commande dans le conteneur, port-forward relie un port local à un port du Pod (cela fonctionne aussi avec svc/NAME), et cp copie des fichiers vers ou depuis le conteneur. Beaucoup d’images de production sont minimales et n’ont ni shell ni curl. Dans ce cas, kubectl debug -it POD_NAME --image=busybox:1.36 --target=CONTAINER_NAME attache un conteneur de débogage temporaire qui partage les namespaces du Pod.

Tout ce que vous modifiez dans un conteneur en cours d’exécution, comme l’édition d’un fichier de configuration, est perdu dès que le Pod est remplacé. Utilisez exec pour investiguer, mais appliquez la correction dans l’image, le manifeste ou un ConfigMap.

Remonter le chemin de la requête

Si le Pod semble sain mais que l’application est injoignable, suivez le chemin de l’intérieur vers l’extérieur :

  1. Le Pod est-il en cours d’exécution et READY ? Réparez d’abord le Pod.
  2. Le sélecteur du Service correspond-il aux labels du Pod ? kubectl get pods --show-labels et kubectl describe service NAME doivent concorder.
  3. Le Service a-t-il des endpoints ? kubectl get endpointslices -l kubernetes.io/service-name=NAME doit lister des IP de Pods. Une liste vide indique un sélecteur qui ne correspond pas ou l’absence de Pods prêts.
  4. Les ports correspondent-ils ? Le targetPort du Service doit être le port sur lequel le conteneur écoute réellement.
  5. Est-ce que cela fonctionne depuis un autre Pod ? kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://NAME. Cela teste le DNS et le Service, et écarte tout ce qui est extérieur au cluster.
  6. Une NetworkPolicy pourrait-elle bloquer le trafic ? Listez-les avec kubectl get networkpolicy.
  7. Enfin, vérifiez l’Ingress ou le load balancer externe placé devant.

Chaque étape circonscrit le problème à une seule couche, ce qui est plus rapide que de deviner.

Guides populaires