Retour au cours

infra / docker

Logs et debugging (exec, logs, inspect)

Leçon 121 exercice

Explication

Ce que vous allez apprendre

  • Consulter les logs d'un conteneur avec les bons filtres (--tail, --since, -f)
  • Exécuter une commande ou ouvrir un shell dans un conteneur déjà en cours d'exécution avec docker exec
  • Déboguer un conteneur qui crashe immédiatement au démarrage en court-circuitant son entrypoint
  • Déboguer le réseau d'une image minimale (alpine, distroless) dépourvue d'outils avec netshoot
  • Limiter la taille des logs pour éviter de saturer le disque de la machine hôte

Dans quel contexte ?

Un conteneur en production redémarre en boucle toutes les quelques secondes. docker exec échoue car le conteneur n'est jamais "running" assez longtemps pour qu'une commande s'y exécute. En relançant l'image avec docker run -it --entrypoint bash mon-api:1.0, un développeur peut explorer l'environnement intérieur, vérifier les fichiers de configuration et comprendre — souvent en quelques minutes — que la commande de démarrage pointait vers un fichier qui n'existait plus après un refactoring récent.

Quand tout ne se passe pas comme prévu

Après avoir appris à construire, lancer et surveiller des conteneurs, il reste la compétence la plus utilisée au quotidien : comprendre pourquoi quelque chose ne fonctionne pas comme attendu. Un conteneur qui refuse de démarrer, une application qui plante silencieusement, une erreur mystérieuse : dans tous ces cas, deux outils couvrent l'essentiel des besoins.

SituationOutil
Comprendre un comportement inattendudocker logs -f --since
Explorer un conteneur qui tournedocker exec -it ... bash
Conteneur qui crashe au démarragedocker run -it --entrypoint bash
Image minimale sans outils réseaudocker run --network container:X nicolaka/netshoot

Piège fréquent

Sans limite configurée, les logs JSON par défaut de Docker peuvent, avec le temps, saturer complètement le disque de la machine hôte. Configure toujours --log-opt max-size et max-file (ou l'équivalent dans daemon.json) avant la mise en production.

Les logs : la première chose à consulter

Tout ce qu'une application écrit sur sa sortie standard (stdout) ou d'erreur (stderr) est automatiquement capturé par Docker et consultable avec docker logs, sans configuration particulière. C'est presque toujours le premier réflexe face à un problème — bien avant d'ouvrir un shell dans le conteneur.

exec : entrer dans un conteneur vivant

docker exec permet d'exécuter une commande, y compris un shell interactif complet, à l'intérieur d'un conteneur déjà en cours d'exécution — un peu comme se connecter à distance à une machine déjà allumée. C'est très différent de docker run, qui crée un TOUT NOUVEAU conteneur : exec agit sur un conteneur existant.

Le piège du conteneur qui crashe immédiatement

Quand un conteneur s'arrête dès son démarrage (crash au lancement), docker exec ne fonctionne plus, car il n'existe alors plus de processus vivant dans lequel entrer. La solution consiste à relancer un nouveau conteneur en remplaçant temporairement sa commande de démarrage par un simple shell (--entrypoint bash), pour explorer l'environnement et comprendre ce qui a fait échouer le vrai démarrage.

Un conteneur minimaliste n'a parfois aucun outil réseau

Les images de production légères (alpine, distroless) n'embarquent souvent ni curl, ni ping, ni les autres outils habituels de diagnostic. Pour déboguer le réseau d'un tel conteneur, on utilise plutôt une image dédiée au debug (comme netshoot), qui partage temporairement l'espace réseau du conteneur à examiner.

Ne pas laisser les logs grandir indéfiniment

Sans limite configurée, les logs JSON par défaut de Docker peuvent, avec le temps, saturer complètement le disque de la machine hôte — un piège classique en production qu'on évite avec max-size/max-file.

Commandes & code

Logs et debugging

bash
# --- Logs ---
docker logs mon-api                     # tous les logs stdout/stderr du conteneur
docker logs -f mon-api                    # suit en temps réel (comme tail -f)
docker logs --tail 100 mon-api              # 100 dernières lignes uniquement
docker logs --since 10m mon-api               # logs des 10 dernières minutes
docker logs -t mon-api                          # ajoute un timestamp à chaque ligne
docker logs mon-api 2>&1 | grep -i error          # filtre les erreurs

# --- Exécuter une commande dans un conteneur déjà lancé ---
docker exec -it mon-api bash              # ouvre un shell interactif
docker exec mon-api ps aux                  # liste les process internes sans ouvrir de shell
docker exec mon-api env                       # variables d'environnement effectives
docker exec -u root mon-api bash                # entre en tant que root même si le conteneur tourne en non-root
docker exec -w /app mon-api ls -la                # exécute dans un répertoire précis

# --- Debug d'un conteneur qui crash au démarrage (exec impossible car il n'est plus "running") ---
docker run -it --entrypoint bash mon-api:1.0        # court-circuite le CMD/ENTRYPOINT pour investiguer
docker logs mon-api                                    # voir pourquoi il s'est arrêté
docker inspect --format '{{.State.ExitCode}}' mon-api    # code de sortie du process principal
docker inspect --format '{{.State.OOMKilled}}' mon-api     # true = tué par manque de mémoire (voir cgroups)

# --- Copier des fichiers entre l'hôte et un conteneur (debug ponctuel) ---
docker cp mon-api:/app/app.log ./app.log
docker cp ./fix.py mon-api:/app/fix.py

# --- Debug réseau depuis l'intérieur d'un conteneur minimal (souvent sans outils réseau) ---
docker run -it --network container:mon-api nicolaka/netshoot
# netshoot embarque curl, dig, tcpdump, nmap... utile quand l'image de prod est distroless/alpine

# --- Attacher son terminal au process principal (rare, à utiliser avec prudence) ---
docker attach mon-api          # Ctrl+P puis Ctrl+Q pour se détacher SANS arrêter le conteneur

# --- Limiter le volume des logs pour éviter de saturer le disque hôte ---
docker run -d --log-driver json-file --log-opt max-size=10m --log-opt max-file=3 mon-api:1.0
# Config globale équivalente dans /etc/docker/daemon.json :
# { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }

Résumé

  • docker logs -f --since couvre l'essentiel du debug quotidien ; docker exec permet d'investiguer à chaud.
  • Pour un conteneur qui crash immédiatement, relancer avec --entrypoint bash court-circuite le process fautif.
  • Sans limite (max-size/max-file), les logs JSON par défaut peuvent saturer le disque de l'hôte au fil du temps.

Exercices pratiques

1 disponible
1

Mission : élucider un conteneur qui redémarre en boucle en production

Objectif : Contourner l'impossibilité d'utiliser exec sur un conteneur qui crashe, et déboguer le réseau d'une image sans outils.

Contexte

Un conteneur mon-api redémarre en boucle toutes les quelques secondes. docker exec -it mon-api bash échoue systématiquement. L'image de production est basée sur distroless, donc sans outils réseau non plus.

Résoudre l’exercice →