Aller au contenu
Conteneurs & Orchestration medium

Débugger une application Kubernetes

45 min de lecture

logo kubernetes

Kubernetes offre plusieurs outils de diagnostic intégrés. Vous utiliserez principalement kubectl logs pour les erreurs applicatives, kubectl describe et kubectl get events pour les problèmes d'orchestration, et kubectl exec ou kubectl debug pour l'investigation interactive. Ce guide vous montre comment combiner ces outils efficacement.

  • Diagnostiquer avec kubectl get, kubectl describe et kubectl get events
  • Analyser les logs avec kubectl logs
  • Exécuter des commandes avec kubectl exec
  • Utiliser les conteneurs éphémères (kubectl debug)
  • Investiguer les problèmes réseau et de service
  • Résoudre les erreurs courantes (CrashLoopBackOff, ImagePullBackOff, OOMKilled, Pending)

Le debug d'un Pod suit toujours la même progression, et la sauter coûte du temps plutôt que d'en gagner. La raison est que chaque phase produit l'information dont la suivante a besoin : sans avoir lu l'état et les événements, vous ne savez pas quel conteneur inspecter ni quels logs demander. Les quatre étapes ci-dessous structurent tout le reste du guide.

  1. Observer, Quel est l'état actuel du Pod, des événements ?
  2. Comprendre, Pourquoi cet état ? Logs, conditions, exit codes
  3. Investiguer, Plonger dans le conteneur si nécessaire
  4. Corriger, Appliquer la solution et vérifier

Ce tableau se lit par la colonne de gauche, celle du symptôme observé, et donne les deux commandes à lancer dans cet ordre. L'ordre n'est pas interchangeable : sur un CrashLoopBackOff, kubectl logs --previous vient avant describe parce que le conteneur courant n'a peut-être pas encore produit de logs, alors que l'instance précédente en a laissé. Si les deux commandes ne suffisent pas, la section correspondante plus bas détaille le diagnostic.

SymptômePremière commandeDeuxième commande
Pod en CrashLoopBackOffkubectl logs --previouskubectl describe pod
Pod en Pendingkubectl describe podkubectl get events
Pod Running mais application KOkubectl logskubectl port-forward / debug réseau
Image minimale sans shellkubectl debug --image=busybox:1.36ps, ss, nslookup, curl
Suspicion OOMkubectl describe podkubectl top pod
Problème de Servicekubectl get endpointskubectl port-forward pod

Première commande pour voir l'état de vos ressources :

Fenêtre de terminal
# État des Pods dans un namespace
kubectl get pods -n mon-namespace
# État de TOUS les Pods du cluster
kubectl get pods -A
# Plus de détails (node, IP)
kubectl get pods -o wide
# Rafraîchissement continu
kubectl get pods -w

Exemple de sortie :

NAME READY STATUS RESTARTS AGE
app-healthy 1/1 Running 0 2h
app-crashloop 0/1 CrashLoopBackOff 5 (30s ago) 3m
app-pending 0/1 Pending 0 5m

Commande sous-estimée mais extrêmement utile. Les événements donnent une vue chronologique de ce qui s'est passé :

Fenêtre de terminal
# Événements du namespace courant, triés par date
kubectl get events --sort-by=.lastTimestamp
# Événements de tous les namespaces
kubectl get events -A --sort-by=.lastTimestamp
# Événements d'un namespace spécifique
kubectl get events -n mon-namespace --sort-by=.lastTimestamp

Exemple de sortie :

LAST SEEN TYPE REASON OBJECT MESSAGE
2m Warning FailedScheduling pod/app-pending 0/3 nodes are available: 3 Insufficient memory
3m Normal Pulling pod/app-new Pulling image "nginx:1.25"
5m Warning BackOff pod/app-crashloop Back-off restarting failed container

Pour comprendre pourquoi un Pod est dans un état donné :

Fenêtre de terminal
kubectl describe pod mon-pod

Sections clés à examiner :

SectionInformation
StatusÉtat actuel du Pod
ConditionsReady, Initialized, ContainersReady, PodScheduled
ContainersÉtat de chaque conteneur, codes de sortie, raison
EventsHistorique chronologique des actions sur ce Pod

kubectl logs lit les logs via le kubelet sur le nœud, à partir des fichiers de logs du conteneur. C'est pourquoi votre application doit écrire vers stdout et stderr.

Fenêtre de terminal
# Logs d'un Pod (conteneur unique)
kubectl logs mon-pod
# Logs d'un conteneur spécifique (Pod multi-conteneurs)
kubectl logs mon-pod -c mon-conteneur
# Dernières 100 lignes
kubectl logs mon-pod --tail=100
# Logs en temps réel
kubectl logs mon-pod -f
# Logs de l'instance précédente du conteneur (après crash)
kubectl logs mon-pod --previous

Pour exécuter des commandes dans un conteneur en cours d'exécution :

Fenêtre de terminal
# Commande unique
kubectl exec mon-pod -- ls -la /app
# Shell interactif
kubectl exec -it mon-pod -- /bin/sh
# Avec conteneur spécifique (Pod multi-conteneurs)
kubectl exec -it mon-pod -c sidecar -- /bin/bash

Les conteneurs éphémères sont stables depuis Kubernetes v1.25. Ils permettent d'injecter un conteneur de debug temporaire dans un Pod existant :

Fenêtre de terminal
# Ajouter un conteneur éphémère basique
kubectl debug mon-pod -it --image=busybox:1.36 --target=app
# Avec des outils réseau complets
kubectl debug mon-pod -it --image=nicolaka/netshoot:v0.9 --target=app

Exemple pratique dans le conteneur de debug :

Fenêtre de terminal
# Voir les processus du conteneur cible
ps aux
# Variables d'environnement du processus principal
cat /proc/1/environ | tr '\0' '\n'
# Ports en écoute
ss -tlnp
# Test de connectivité réseau
curl -v http://mon-service:8080/health

La syntaxe reprend celle de scp : la source vient en premier, la destination ensuite, et le pod se désigne par pod:/chemin. Une limite à connaître avant de l'utiliser en incident, détaillée juste après : la commande s'appuie sur tar présent dans le conteneur distant, ce qui la rend inopérante sur les images minimales. Évitez également de récupérer un fichier volumineux d'un pod en production, la copie transite par le serveur d'API.

Fenêtre de terminal
# Pod → Local
kubectl cp mon-pod:/app/logs/error.log ./error.log
# Local → Pod
kubectl cp ./config.yaml mon-pod:/app/config.yaml
# Avec conteneur spécifique
kubectl cp mon-pod:/app/dump.txt ./dump.txt -c app

Voir la consommation CPU/mémoire en temps réel nécessite metrics-server installé dans le cluster. C'est un prérequis courant à l'examen CKAD.

Vérifier que metrics-server est présent :

Fenêtre de terminal
kubectl get pods -n kube-system | grep metrics-server
kubectl top nodes # Si metrics-server est absent : "error: Metrics API not available"

Installer metrics-server si absent (environnements de test) :

Fenêtre de terminal
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.9.0/components.yaml
# Attendre la disponibilité (30-60 secondes)
kubectl rollout status deployment/metrics-server -n kube-system

Commandes utiles :

Fenêtre de terminal
# Pods du namespace courant
kubectl top pods
# Tous les Pods (tous les namespaces)
kubectl top pods -A
# Nœuds
kubectl top nodes
# Trier par mémoire décroissante
kubectl top pods --sort-by=memory
# Trier par CPU décroissante
kubectl top pods --sort-by=cpu
# Un Pod avec détail par conteneur
kubectl top pod mon-pod --containers

Exemple de sortie et interprétation :

NAME CPU(cores) MEMORY(bytes)
api 234m 128Mi
worker 12m 64Mi
db 890m 512Mi

La colonne CPU est en millicores (1000m = 1 vCPU). Un Pod qui consomme 890m CPU alors que sa requests est à 500m mais sa limit à 1000m fonctionne mais approche la limite, risque de throttling. Pour la mémoire, si la consommation dépasse la limit, le Pod est tué en OOMKilled.

Corréler avec requests/limits :

Fenêtre de terminal
# Comparer consommation réelle vs limits définies
kubectl get pod mon-pod -o=jsonpath='{.spec.containers[*].resources}' | jq
kubectl top pod mon-pod --containers

Accédez à un Pod ou Service sans exposition externe :

Fenêtre de terminal
# Vers un Pod
kubectl port-forward mon-pod 8080:80
# Vers un Service
kubectl port-forward svc/mon-service 8080:80
# En arrière-plan
kubectl port-forward mon-pod 8080:80 &

Utile pour tester si le Pod répond indépendamment du Service.

Symptôme : Le conteneur démarre, crash, redémarre en boucle.

  1. Vérifiez les logs du crash précédent

    Fenêtre de terminal
    kubectl logs mon-pod --previous
  2. Examinez le code de sortie et la raison

    Fenêtre de terminal
    kubectl describe pod mon-pod | grep -A10 "Last State"
  3. Interprétez le code de sortie

    CodeSignification
    0Sortie normale (mais Pod censé tourner → vérifiez la commande)
    1Erreur applicative générique
    137Processus tué par SIGKILL (souvent OOM, confirmez avec describe)
    143SIGTERM reçu (arrêt propre demandé)
  4. Appliquez la solution

    • Code 1 : Corrigez le bug applicatif (voir logs)
    • Code 137 + OOMKilled : Augmentez resources.limits.memory
    • Sortie immédiate (code 0) : Vérifiez la commande/entrypoint

Symptôme : Kubernetes ne peut pas télécharger l'image. Le Pod est correctement programmé sur un nœud, mais le kubelet échoue à récupérer l'image auprès du registry et réessaie avec un délai croissant. La cause exacte figure toujours dans le message de l'événement, il faut donc le lire en entier : un manifest unknown désigne un tag inexistant, un unauthorized un problème d'identifiants, un toomanyrequests un quota atteint.

Fenêtre de terminal
kubectl describe pod mon-pod | grep -A10 Events
kubectl get events --field-selector involvedObject.name=mon-pod
CauseSolution
Image inexistanteVérifiez le nom et le tag exact
Registry privéCréez un imagePullSecret et référencez-le
Quota Docker HubAuthentifiez-vous ou utilisez un registry privé
Erreur réseauVérifiez la connectivité du nœud vers le registry

Symptôme : Le Pod reste en Pending indéfiniment. Contrairement aux deux cas précédents, aucun conteneur n'a démarré : le Pod n'a pas encore de nœud, c'est l'ordonnanceur qui refuse de le placer. Le message de l'événement FailedScheduling indique combien de nœuds ont été évalués et pourquoi chacun a été écarté, sous la forme 0/3 nodes are available: .... Cette phrase contient à elle seule la réponse dans la grande majorité des cas.

Fenêtre de terminal
kubectl describe pod mon-pod | grep -A20 Events
kubectl get events -A --sort-by=.lastTimestamp | grep -i scheduling
CauseSolution
Insufficient cpu/memoryRéduisez les requests ou ajoutez des nœuds
No nodes match nodeSelectorVérifiez les labels des nœuds
Taints not toleratedAjoutez les tolerations nécessaires
PVC pendingVérifiez le PVC et le StorageClass

Symptôme : Conteneur tué pour surconsommation mémoire. C'est le noyau du nœud, et non Kubernetes, qui met fin au processus dès que le conteneur dépasse sa limits.memory. L'arrêt est immédiat et sans avertissement, l'application n'a aucune chance de se fermer proprement. Deux conséquences pour le diagnostic : les logs s'arrêtent net au milieu d'une opération, et le seul endroit qui nomme la cause est le champ Last State de describe, pas les journaux applicatifs.

Fenêtre de terminal
# Vérifier l'état du conteneur
kubectl describe pod mon-pod | grep -A5 "Last State"
# Chercher OOMKilled explicitement
kubectl describe pod mon-pod | grep -i oom
# Voir la consommation actuelle
kubectl top pod mon-pod --containers

Solutions :

  1. Augmentez resources.limits.memory
  2. Analysez les fuites mémoire de l'application
  3. Ajoutez du monitoring pour détecter la tendance avant le crash

C'est un cas très fréquent : le Pod est Running, mais l'application ne répond pas.

Étape 1 : Vérifiez que le Pod répond directement

Section intitulée « Étape 1 : Vérifiez que le Pod répond directement »

Cette étape sert à couper le problème en deux. kubectl port-forward établit un tunnel direct vers le pod, en contournant totalement le Service, le kube-proxy et les éventuelles NetworkPolicies. Le verdict est donc net : si le pod répond ici, l'application va bien et le problème est dans la chaîne réseau ; s'il ne répond pas, inutile d'inspecter le Service.

Fenêtre de terminal
# Accès direct au Pod, contournant le Service
kubectl port-forward mon-pod 8080:80
# Dans un autre terminal
curl http://localhost:8080/health

Si le Pod répond : le problème vient du Service ou du réseau. Si le Pod ne répond pas : le problème est applicatif.

Fenêtre de terminal
# Voir le Service
kubectl get svc mon-service -o wide
# Vérifier que le Service a des endpoints
kubectl get endpoints mon-service
# Ou avec EndpointSlices (Kubernetes moderne)
kubectl get endpointslices -l kubernetes.io/service-name=mon-service

Endpoints vides ? Le selector du Service ne matche aucun Pod. Vérifiez les labels :

Fenêtre de terminal
# Labels du Service (selector)
kubectl get svc mon-service -o jsonpath='{.spec.selector}'
# Labels des Pods
kubectl get pods --show-labels

Étape 3 : Testez la connectivité depuis un autre Pod

Section intitulée « Étape 3 : Testez la connectivité depuis un autre Pod »

Les deux premières étapes ont testé depuis votre poste, ce qui ne reproduit pas les conditions réelles : à l'intérieur du cluster, la résolution DNS passe par CoreDNS et le trafic peut être filtré par des NetworkPolicies. Lancer un pod jetable dans le même namespace place le test dans les mêmes conditions que l'application appelante. Commencez toujours par nslookup : un nom de service qui ne résout pas explique à lui seul l'échec des tests suivants.

Fenêtre de terminal
# Lancer un Pod de debug avec des outils réseau
kubectl run debug-net --rm -it --image=nicolaka/netshoot:v0.9 -- /bin/bash
# Dans le Pod de debug
nslookup mon-service
curl -v http://mon-service:80/health
nc -zv mon-service 80

Quand kubectl exec échoue faute de shell (distroless, scratch), voici le workflow :

  1. Lancez un conteneur éphémère avec une image de debug

    Fenêtre de terminal
    kubectl debug mon-pod -it --image=nicolaka/netshoot:v0.9 --target=app
  2. Explorez le conteneur cible

    Fenêtre de terminal
    # Processus
    ps aux
    # Variables d'environnement
    cat /proc/1/environ | tr '\0' '\n'
    # Fichiers ouverts
    ls -la /proc/1/fd/
  3. Testez le réseau

    Fenêtre de terminal
    # Résolution DNS
    nslookup kubernetes.default
    nslookup mon-service.mon-namespace.svc.cluster.local
    # Connectivité TCP
    nc -zv mon-service 80
    # Test HTTP
    curl -v http://mon-service:80/health
    # Ports en écoute dans le conteneur cible
    ss -tlnp
    # Routes réseau
    ip route
  4. Vérifiez les volumes montés

    Fenêtre de terminal
    mount | grep -v "cgroup\|proc\|sys"
    cat /etc/resolv.conf

Cette séquence reprend dans l'ordre les commandes des sections précédentes. Elle part du plus large pour resserrer progressivement, et chaque étape sert à décider si la suivante est utile. Dans la pratique, une panne sur trois se résout dès l'étape 2, à la lecture des Events : ne sautez pas directement à l'investigation interactive, elle coûte du temps et ne dit rien sur les problèmes d'orchestration.

  1. Vue globale

    Fenêtre de terminal
    kubectl get pods -A | grep -v Running
    kubectl get events -A --sort-by=.lastTimestamp | head -20
  2. Cibler le Pod problématique

    Fenêtre de terminal
    kubectl describe pod mon-pod
  3. Logs applicatifs

    Fenêtre de terminal
    kubectl logs mon-pod --tail=100
    # Si crash récent
    kubectl logs mon-pod --previous
  4. Investigation interactive

    Fenêtre de terminal
    # Si shell disponible
    kubectl exec -it mon-pod -- /bin/sh
    # Sinon
    kubectl debug mon-pod -it --image=nicolaka/netshoot:v0.9 --target=app
  5. Vérification réseau/service

    Fenêtre de terminal
    kubectl port-forward mon-pod 8080:80
    kubectl get endpoints mon-service
  6. Métriques

    Fenêtre de terminal
    kubectl top pod mon-pod --containers

Ces cinq règles ne concernent pas le debug lui-même mais ce qui le rend possible. Une application qui écrit ses journaux dans un fichier interne au conteneur est invisible pour kubectl logs, et une application sans requests déclarées empêche l'ordonnanceur de raisonner. Les deux premières lignes sont celles qui changent le plus la vie en incident.

  1. Loguez vers stdout/stderr, Kubernetes capture automatiquement via le kubelet
  2. Implémentez des health endpoints, /health, /ready pour les probes
  3. Définissez des ressources, Évite les OOMKilled silencieux et les Pending
  4. Utilisez des labels cohérents, Facilite le filtrage (kubectl get pods -l app=api)
  5. Gérez SIGTERM proprement, Arrêt gracieux pour les rolling updates

Ces quatre réflexes portent sur votre méthode plutôt que sur les commandes. Le plus rentable est le premier : les Events répondent souvent en une ligne à une question que les logs applicatifs ne peuvent pas trancher, parce qu'ils décrivent ce que Kubernetes a tenté, pas ce que l'application a fait. Retenez aussi que les Events ont une durée de vie limitée, une heure par défaut : au-delà, seule une centralisation des journaux conserve la trace.

  1. Commencez par get events, Vue chronologique de ce qui s'est passé
  2. Utilisez -A / --all-namespaces, Quand vous ne savez pas où est le problème
  3. Gardez une image de debug épinglée, nicolaka/netshoot:v0.9 ou busybox:1.36 pour les cas difficiles
  4. Centralisez les logs, Loki, Elasticsearch, CloudWatch pour l'historique

L'examen CKAD est chronométré, ce qui change la méthode : vous n'avez pas le temps d'explorer, il faut une séquence déjà mémorisée. Les six réflexes ci-dessous sont volontairement réduits à leur forme la plus courte, sans options superflues. Entraînez-vous à les taper sans réfléchir ; le temps gagné là se reporte sur les questions de configuration, qui rapportent davantage de points.

  1. Vue globale rapide

    Fenêtre de terminal
    kubectl get pods -A
    kubectl get events -A --sort-by=.lastTimestamp
  2. Diagnostic d'un Pod

    Fenêtre de terminal
    kubectl describe pod <pod>
    kubectl logs <pod> --previous
  3. Investigation interactive

    Fenêtre de terminal
    kubectl exec -it <pod> -- /bin/sh
  4. Test de connectivité

    Fenêtre de terminal
    kubectl port-forward <pod> 8080:80
  5. Debug avancé (images minimales)

    Fenêtre de terminal
    kubectl debug <pod> -it --image=busybox:1.36 --target=<container>
  6. Vérifier les endpoints d'un Service

    Fenêtre de terminal
    kubectl get endpoints <service>
SituationCommande
Vue globale tous namespaceskubectl get pods -A
Événements chronologiqueskubectl get events --sort-by=.lastTimestamp
Détails d'un Podkubectl describe pod
Logs applicatifskubectl logs [--previous]
Shell interactifkubectl exec -it -- /bin/sh
Images minimaleskubectl debug --image=busybox:1.36 --target=app
Test direct Podkubectl port-forward pod 8080:80
Vérifier Servicekubectl get endpoints
Métriques instantanéeskubectl top pods
Copier des fichierskubectl cp (nécessite tar dans le conteneur)

Le debugging Kubernetes suit toujours le même pattern : observer l'état global, lire les événements, analyser les logs, vérifier le réseau/service, investiguer interactivement si nécessaire.

Ce questionnaire porte sur les situations traitées dans le guide : lecture d'un état de pod, choix de la commande adaptée à un symptôme, interprétation d'un code de sortie. Les questions sur --previous et sur la distinction entre describe et get events sont celles qui révèlent le mieux les points à relire.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn