Diagnostiquer les Pods et les conteneurs Kubernetes

KubernetesBeginner
Pratiquer maintenant

Introduction

Un état de Pod tel que Pending ou CrashLoopBackOff n’est qu’un symptôme, et non la cause racine. Pour diagnostiquer efficacement un problème, il faut examiner les éléments disponibles dans un ordre cohérent : configuration de l’objet, conditions, événements, état de chaque conteneur, journaux, puis inspection en direct.

Vous allez diagnostiquer deux problèmes préparés, les corriger, puis examiner un Pod sain composé de plusieurs conteneurs. L’objectif est de vous transmettre une méthode de dépannage réutilisable, plutôt que de vous faire mémoriser une correction particulière.

Pour chaque incident, suivez le même ordre : commencez par une vue d’ensemble, isolez un objet, consultez son état actuel et ses événements, examinez les informations propres au conteneur, puis corrigez le contrôleur qui le gère. Cette démarche évite les modifications aléatoires et permet d’expliquer pourquoi une correction a fonctionné.

Commencer par l’état du Pod

Dans cette étape, vous allez identifier les charges de travail saines et celles qui nécessitent une investigation. Commencez par une vue d’ensemble du namespace. -o wide conserve le tableau habituel tout en ajoutant des colonnes utiles, comme l’adresse IP du Pod et le nœud qui lui est attribué.

cd /home/labex/project/pod-troubleshooting
kubectl get deployments,pods -n practice-debug -o wide

Le Pod sain doit afficher 2/2 Running. Le Pod d’un Deployment devrait présenter un problème de récupération d’image, tandis que le worker devrait redémarrer à plusieurs reprises.

Dans l’environnement de test, le résultat ressemble généralement à ceci (les suffixes des Pods, leur ancienneté et le nombre de redémarrages peuvent différer) :

NAME                                 READY   STATUS             RESTARTS   AGE
pod/broken-image-58758b4d69-c7j7g    0/1     ImagePullBackOff   0          33s
pod/crashy-worker-5557cfb974-xwpst   0/1     Error              2          33s
pod/web-with-sidecar                 2/2     Running            0          33s

Ne vous inquiétez pas si le worker affiche actuellement Error au lieu de CrashLoopBackOff. Il s’exécute brièvement, se termine, puis attend avant que Kubernetes ne le relance ; l’état affiché évolue donc sous vos yeux. L’augmentation de la valeur RESTARTS est l’indice le plus révélateur : elle indique que le conteneur échoue à répétition.

Affichez les raisons d’attente au niveau des conteneurs sans consulter l’intégralité du YAML du Pod :

kubectl get pods -n practice-debug -o custom-columns='POD:.metadata.name,PHASE:.status.phase,READY:.status.containerStatuses[*].ready,WAITING_REASON:.status.containerStatuses[*].state.waiting.reason,RESTARTS:.status.containerStatuses[*].restartCount'

[*] signifie « chaque élément de la liste » ; le même tableau fonctionne donc aussi pour les Pods à plusieurs conteneurs. Utilisez cette première vue pour déterminer quelle commande de diagnostic exécuter ensuite.

Par exemple, WAITING_REASON=ImagePullBackOff oriente vers la configuration de l’image ou l’accès au registre. Le worker peut n’afficher aucune raison d’attente alors que sa dernière tentative est dans l’état Terminated ; c’est pourquoi ce tableau ne constitue qu’un point de départ et non un diagnostic complet.

À ce stade, ne corrigez encore rien. Le problème d’image et le redémarrage du worker sont deux symptômes différents qui nécessitent des éléments de preuve différents. Une bonne première commande réduit le champ des recherches sans présumer de la cause.

Diagnostiquer un échec de récupération d’image

Dans cette étape, vous allez remonter du statut aux événements pour comprendre l’échec de récupération de l’image. Enregistrez le nom généré du Pod afin que les commandes suivantes ciblent le même objet :

IMAGE_POD=$(kubectl get pods -n practice-debug -l app=broken-image -o jsonpath='{.items[0].metadata.name}')
echo "$IMAGE_POD"
kubectl describe pod "$IMAGE_POD" -n practice-debug

Lisez la sortie en commençant par le bas et en remontant. La section Events devrait indiquer que Kubernetes ne parvient pas à récupérer nginx:not-a-real-tag. Vérifiez directement l’image configurée :

kubectl get pod "$IMAGE_POD" -n practice-debug -o jsonpath='{.spec.containers[0].image}{"\n"}'
kubectl events -n practice-debug --for "pod/${IMAGE_POD}"

Les événements expliquent l’action qui a échoué, tandis que la requête JSONPath confirme l’image souhaitée à l’origine du problème. Les utiliser conjointement évite de confondre un problème temporaire du registre avec un tag définitivement invalide.

Dans l’environnement testé, les lignes déterminantes ressemblaient à ceci :

Image:          nginx:not-a-real-tag
State:          Waiting
  Reason:       ErrImagePull
Failed to pull image "nginx:not-a-real-tag": ... manifest unknown
Error: ImagePullBackOff

ErrImagePull indique qu’une tentative de récupération a échoué. ImagePullBackOff signifie que Kubernetes attend désormais plus longtemps avant de retenter la même opération. Le message manifest unknown, associé au tag invalide, relie le symptôme à sa cause.

Corrigez le Deployment plutôt que de modifier le Pod généré. kubectl set image modifie le modèle de Pod ; le contrôleur du Deployment remplace donc le Pod défaillant :

kubectl set image deployment/broken-image web=nginx:1.27-alpine -n practice-debug
kubectl rollout status deployment/broken-image -n practice-debug --timeout=60s

La fin de la commande doit afficher :

deployment.apps/broken-image image updated
deployment "broken-image" successfully rolled out

L’ancien Pod est remplaçable ; le modèle du Deployment constitue l’état souhaité durable. Après le déploiement, un nouveau nom de Pod doit apparaître et le Deployment doit signaler une réplique disponible.

Lire les journaux actuels et précédents

Dans cette étape, vous allez diagnostiquer un conteneur qui démarre puis s’arrête. Trouvez son Pod et examinez les informations relatives à ses redémarrages :

WORKER_POD=$(kubectl get pods -n practice-debug -l app=crashy-worker -o jsonpath='{.items[0].metadata.name}')
kubectl get pod "$WORKER_POD" -n practice-debug
kubectl describe pod "$WORKER_POD" -n practice-debug

Pour un conteneur qui redémarre, kubectl logs lit les journaux de la tentative en cours. L’option --previous demande au runtime du conteneur les journaux de la tentative terminée juste avant celle-ci :

kubectl logs "$WORKER_POD" -n practice-debug -c worker
kubectl logs "$WORKER_POD" -n practice-debug -c worker --previous || \
  echo "The previous container log is no longer available; continue with the current log and describe output."

La sortie actuelle ou précédente devrait révéler le message produit par l’application elle-même :

configuration missing

--previous est particulièrement utile lorsqu’une nouvelle tentative démarre avant que vous puissiez examiner celle qui a échoué. Il s’agit toutefois d’un élément historique qui n’est pas conservé indéfiniment : le runtime local peut déjà avoir supprimé l’enregistrement de ce conteneur. Si vous voyez unable to retrieve container logs, le message de secours est attendu. Combinez alors le journal actuel avec la sortie de describe : les champs Last State, Exit Code et Restart Count prouvent toujours que le processus s’est arrêté à plusieurs reprises.

L’option -c worker désigne explicitement le conteneur. Corrigez le contrôleur en remplaçant la commande défaillante par une boucle qui s’exécute durablement :

kubectl patch deployment crashy-worker -n practice-debug --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/command","value":["sh","-c","echo worker-ready; while true; do sleep 30; done"]}]'
kubectl rollout status deployment/crashy-worker -n practice-debug --timeout=60s

Le correctif peut sembler impressionnant ; lisez-le de l’intérieur vers l’extérieur : path sélectionne la command du premier conteneur, op: replace indique le type de modification, et value fournit le tableau de commandes de remplacement. Vous ne modifiez qu’un seul champ ; le reste du Deployment demeure inchangé.

Le correctif JSON remplace précisément un champ dans le modèle de Pod du Deployment. En production, appliquez également la modification équivalente dans le manifeste source.

Attendez le statut du déploiement : la commande modifie immédiatement l’état souhaité, mais le remplacement et la mise en état Ready se déroulent de manière asynchrone. Une réponse positive de l’API ne suffit pas encore à prouver que la charge de travail est saine.

Cibler les conteneurs d’un Pod à plusieurs conteneurs

Dans cette étape, vous allez examiner un Pod dont les conteneurs ont des responsabilités différentes. Commencez par afficher leurs noms :

kubectl get pod web-with-sidecar -n practice-debug -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}'

Le range de JSONPath parcourt la liste des conteneurs. Lisez séparément les journaux de chaque conteneur :

kubectl logs web-with-sidecar -n practice-debug -c reporter --tail=5
kubectl logs web-with-sidecar -n practice-debug -c web --tail=5

--tail=5 limite la sortie aux cinq dernières lignes. Le reporter affiche normalement plusieurs messages reporter-alive. NGINX peut afficher des messages de démarrage ou aucun journal de requête pour le moment ; ces deux situations sont normales puisqu’aucune requête HTTP ne lui a encore été envoyée.

Exécutez une commande dans le conteneur web :

kubectl exec web-with-sidecar -n practice-debug -c web -- nginx -v

L’image testée a renvoyé :

nginx version: nginx/1.27.5

Le numéro exact de correctif peut changer si l’image est actualisée ; l’élément important est que la commande s’est exécutée correctement dans le conteneur web.

Le premier -- marque la fin des options de kubectl. Tout ce qui le suit est la commande exécutée dans le conteneur sélectionné.

Cette distinction est importante : -c web est une option interprétée par kubectl, tandis que nginx -v s’exécute à l’intérieur du conteneur. Si vous omettez -c pour un Pod à plusieurs conteneurs, kubectl peut en sélectionner un par défaut et vous montrer des informations provenant du mauvais processus.

Les conteneurs d’un même Pod partagent son espace de noms réseau. Le reporter BusyBox peut donc lui aussi interroger NGINX via l’adresse de bouclage. Dans wget -qO-, -q masque les messages de progression et -O- écrit le corps de la réponse sur la sortie standard. Le tube transmet ensuite ce corps à head, qui n’en conserve que les premières lignes :

kubectl exec web-with-sidecar -n practice-debug -c reporter -- wget -qO- http://127.0.0.1 | head

La réponse devrait commencer par <!DOCTYPE html>. Ce modèle de diagnostic est réutilisable pour les prochains exercices : exécuter un petit client HTTP à l’intérieur d’un Pod préparé et observer le comportement de l’application sans exposer de nouveau port externe.

Confirmer la récupération sous plusieurs angles

Dans cette étape, vous allez vérifier que la correction des contrôleurs a bien produit de nouveaux Pods sains. Contrôlez simultanément la disponibilité des contrôleurs et l’état Ready des Pods :

kubectl get deployments,pods -n practice-debug
kubectl get pods -n practice-debug -o custom-columns='POD:.metadata.name,READY:.status.containerStatuses[*].ready,RESTARTS:.status.containerStatuses[*].restartCount,OWNER:.metadata.ownerReferences[0].name'

Enfin, vérifiez si les événements d’avertissement récents signalent encore des échecs actifs :

--field-selector type=Warning demande au serveur d’API de ne renvoyer que les événements de type Warning. --sort-by=.metadata.creationTimestamp les trie ensuite du plus ancien au plus récent, ce qui permet de distinguer les incidents historiques de l’état actuel.

kubectl get events -n practice-debug --field-selector type=Warning --sort-by=.metadata.creationTimestamp

D’anciens avertissements peuvent rester présents après la récupération. Pour évaluer l’état actuel, fiez-vous aux conditions des Pods et à la disponibilité des contrôleurs ; les événements expliquent quant à eux comment le système est arrivé à cet état.

Après les deux corrections, la sortie correspondant à l’état actuel devrait avoir cette forme :

NAME                            READY   UP-TO-DATE   AVAILABLE
deployment.apps/broken-image    1/1     1            1
deployment.apps/crashy-worker   1/1     1            1

NAME                                 READY   STATUS    RESTARTS
pod/broken-image-...                 1/1     Running   0
pod/crashy-worker-...                1/1     Running   0
pod/web-with-sidecar                 2/2     Running   0

Vous pouvez encore voir d’anciens avertissements Failed, BackOff, voire un avertissement initial FailedCreatePodSandBox provenant du démarrage du cluster. Comparez les noms des objets et les horodatages : ces avertissements concernent les Pods remplacés, tandis que les nouveaux Pods sont Running et que les deux Deployments sont disponibles. Les événements constituent un historique, pas un tableau de bord de santé en temps réel.

Votre conclusion finale doit s’appuyer sur plusieurs éléments actuels : les deux Deployments sont disponibles, tous les conteneurs attendus sont prêts et aucun Pod actif ne reste dans un état de récupération d’image ou de boucle de redémarrage. Le dépannage se termine par une vérification, et non par l’exécution de la commande de correction.

Résumé

Vous avez diagnostiqué des problèmes d’image et de démarrage à l’aide des conditions, des événements, de l’état des conteneurs, des journaux actuels et des journaux précédents. Vous avez corrigé les modèles des contrôleurs plutôt que les Pods générés, et appris à cibler chaque conteneur avec logs et exec. L’exercice suivant vous demandera d’appliquer cette méthode fondée sur les faits, sans guide détaillé.