Un jour vos conteneurs vont bien. Le lendemain l’hôte est « mystérieusement » à court d’espace disque, SSH est lent, et chaque déploiement se transforme en roulette.
Vous vérifiez /var/log comme il se doit. Ce n’est pas le coupable. Puis vous vous souvenez de l’endroit obscur : /var/lib/docker.
C’est le mode de défaillance où les logs Docker grossissent jusqu’à ce que le système de fichiers lâche. C’est ennuyeux, fréquent et totalement évitable.
Corrigez-le correctement et votre futur vous n’aura pas à pratiquer une chirurgie d’urgence sur un nœud en production à 2h du matin.
Guide de diagnostic rapide
Quand le disque se remplit, vous ne voulez pas d’un cours de philosophie. Vous voulez des réponses en quelques minutes : qu’est-ce qui est gros, qu’est-ce qui grossit, et quoi changer pour que
ça ne se reproduise pas.
Première étape : est-ce vraiment les logs Docker ?
Vérifiez l’utilisation des systèmes de fichiers et confirmez quelle partition est en feu. Si / est plein, mais que Docker est sur une partition séparée, ne perdez pas de temps au
mauvais endroit. Si Docker vit sur la racine (fréquent), vous allez comprendre pourquoi c’est épicé.
Deuxième étape : trouvez vite les plus gros coupables
Identifiez quels fichiers de logs de conteneurs sont énormes. Si un seul conteneur génère des gigaoctets par heure, vous n’êtes pas en train de « réparer la rotation », vous traitez une attaque DDoS de logs.
Troisième étape : confirmez le driver de logs et ses limites
Si vous utilisez json-file sans max-size et max-file, les logs vont croître jusqu’à ce que le disque dise « non ».
Si vous utilisez journald, le volume de logs se déplace vers systemd-journald, et vous devez le limiter là-bas.
Quatrième étape : décidez du type de correctif nécessaire
- Confinement immédiat : tronquez les logs énormes, libérez de l’espace, stoppez l’hémorragie.
- Correction de configuration : définissez des valeurs par défaut au niveau du démon, imposez des limites, redémarrez Docker en sécurité.
- Correction structurelle : orientez les logs vers un système centralisé et arrêtez de traiter le disque local comme un puits sans fond.
Ce qui grossit réellement (et pourquoi)
Le comportement par défaut de Docker est trompeusement simple : tout ce que votre conteneur écrit sur stdout et stderr est capturé par le moteur Docker et
écrit quelque part. Le « quelque part » par défaut sur la plupart des systèmes Linux est le driver de logs json-file.
Avec json-file, chaque conteneur obtient un fichier de logs (et des fichiers rotatifs si vous avez configuré la rotation) sous
/var/lib/docker/containers/<container-id>/<container-id>-json.log. Ce fichier grossit. Et grossit. Et continue de grossir jusqu’à
ce que le système de fichiers soit plein—à moins que vous n’indiquiez à Docker de le faire tourner.
Voici la vérité inconfortable : « nous avons de la supervision » n’aide pas si vous n’avez pas de garde-fous. Les alertes disque déclenchent quand c’est déjà mauvais.
La rotation est une ceinture de sécurité. La centralisation des logs, des airbags. Vous voulez les deux.
Une citation à garder sur un post-it :
L’idée paraphrasée : l’espoir n’est pas une stratégie
— souvent attribuée à Gordon « Nick » Haskins (idée paraphrasée).
Aussi : les conteneurs ne rendent pas la journalisation magiquement plus simple. Ils facilitent la création d’un grand volume de logs, rapidement, depuis de nombreux petits endroits.
Si votre service est bavard, votre hôte devient le journal intime qu’il n’a jamais demandé.
Blague n°1 : les logs Docker sont comme des autocollants gratuits à une conférence — en prendre trop et soudain votre portable ne se ferme plus.
Faits intéressants et contexte historique
- Docker a initialement promu « logs vers stdout » comme séparation propre : les applis émettent des logs ; la plateforme décide où ils vont.
- Le driver
json-fileest devenu le défaut parce qu’il est simple, autonome et ne nécessite pas de dépendances externes. - Les options de rotation de Docker sont par driver : les flags qui fonctionnent pour
json-filene s’appliquent pas forcément àjournald. - Kubernetes a standardisé les logs de conteneurs comme fichiers sur le nœud (souvent sous
/var/log/containers), ce qui a fait de la rotation au niveau du nœud un problème opérationnel clé. - Systemd-journald a ses propres contrôles de rétention et peut être configuré pour conserver les logs en mémoire, sur disque, ou les deux—bien tant que quelqu’un les limite.
- Les systèmes de fichiers Overlay ont changé la perception de l’utilisation disque : vous pouvez avoir beaucoup d’espace dans une couche et quand même manquer d’espace sur le système de fichiers hôte qui la supporte.
- Les premières plateformes de conteneurs étaient souvent livrées sans politique de rétention tranchée, car la rétention dépend des exigences métier (conformité, forensique, coût).
- Beaucoup d’incidents en production attribués à « Docker storage » sont en réalité des événements disque-plein causés par des logs sans limite ou un mode debug incontrôlé.
Tâches pratiques : commandes, sorties, décisions
Ci-dessous des tâches réelles que vous pouvez exécuter sur un hôte Linux avec Docker. Chaque point inclut : la commande, ce que signifie une sortie typique, et quelle décision prendre ensuite.
L’objectif est de transformer « disque plein » en une séquence de vérifications contrôlées.
Task 1: Confirm which filesystem is full
cr0x@server:~$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 120G 116G 2.5G 98% /
/dev/nvme0n1p1 vfat 512M 8.0M 504M 2% /boot/efi
tmpfs tmpfs 32G 0 32G 0% /dev/shm
Ce que cela signifie : La racine est à 98%. Si les données Docker vivent sur /, vous êtes à un déploiement d’une mauvaise surprise.
Décision : Localisez immédiatement les consommateurs de disque de Docker ; ne commencez pas un « nettoyage » au hasard.
Task 2: Measure /var/lib/docker size quickly
cr0x@server:~$ sudo du -sh /var/lib/docker
87G /var/lib/docker
Ce que cela signifie : Docker consomme la majeure partie de la racine. Ce n’est pas nécessairement incorrect, mais c’est un fort signal.
Décision : Décomposez le stockage Docker en conteneurs, images, volumes et logs.
Task 3: See Docker’s own storage accounting
cr0x@server:~$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 42 18 19.6GB 6.2GB (31%)
Containers 65 24 3.8GB 1.1GB (28%)
Local Volumes 14 12 9.4GB 0B (0%)
Build Cache 0 0 0B 0B
Ce que cela signifie : Images/volumes ne sont pas assez gros pour expliquer 87G. Les logs ou les layers modifiables des conteneurs sont probables.
Décision : Partez à la chasse des fichiers de logs dans les répertoires des conteneurs.
Task 4: Find the largest container log files
cr0x@server:~$ sudo find /var/lib/docker/containers -name "*-json.log" -printf "%s %p\n" | sort -nr | head -n 5
32213455120 /var/lib/docker/containers/9b2c.../9b2c...-json.log
11422577664 /var/lib/docker/containers/12ad.../12ad...-json.log
2213478400 /var/lib/docker/containers/7a11.../7a11...-json.log
845312000 /var/lib/docker/containers/fe88.../fe88...-json.log
331776000 /var/lib/docker/containers/0c19.../0c19...-json.log
Ce que cela signifie : Vous avez au moins un fichier de 32 Go. Voilà votre fuite disque.
Décision : Associez l’ID du conteneur à un nom/service, puis décidez : tronquer maintenant et réparer la rotation définitivement.
Task 5: Map container ID to container name and image
cr0x@server:~$ docker ps -a --no-trunc --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}' | head
CONTAINER ID NAMES IMAGE STATUS
9b2c0f0e0d6c2f7d4c3d1b1e8c... api-prod registry/app:2.8.1 Up 3 days
12ad77c2b12d8b1f21b9e8f2aa... worker-prod registry/worker:5.1.0 Up 3 days
7a11aa90b9d9d7c0c1e0fda2bb... nginx-edge nginx:1.25 Up 10 days
Ce que cela signifie : Le gros log appartient à api-prod. Maintenant vous pouvez parler à la bonne équipe, ou au moins savoir qui regarder d’un air sévère.
Décision : Inspectez les paramètres de journalisation et le taux de croissance ; envisagez la troncature immédiate si le disque est critique.
Task 6: Check Docker daemon logging driver defaults
cr0x@server:~$ docker info --format '{{.LoggingDriver}}'
json-file
Ce que cela signifie : Vous êtes sur json-file. La rotation n’est pas automatique sauf si configurée.
Décision : Vérifiez que le démon a des log-opts ; sinon, implémentez-les.
Task 7: Inspect current daemon configuration
cr0x@server:~$ sudo cat /etc/docker/daemon.json
{
"storage-driver": "overlay2"
}
Ce que cela signifie : Aucune limite de logs n’est configurée globalement.
Décision : Ajoutez log-driver et log-opts par défaut, puis redémarrez Docker pendant une fenêtre contrôlée.
Task 8: Check a container’s effective log path (json-file)
cr0x@server:~$ docker inspect --format '{{.LogPath}}' api-prod
/var/lib/docker/containers/9b2c0f0e0d6c2f7d4c3d1b1e8c.../9b2c0f0e0d6c2f7d4c3d1b1e8c...-json.log
Ce que cela signifie : C’est le fichier exact qui grossit. Pas de conjecture.
Décision : Si la pression disque est élevée, tronquez ce fichier (en toute sécurité) comme mesure de confinement.
Task 9: Measure log growth rate (is it actively exploding?)
cr0x@server:~$ sudo ls -lh /var/lib/docker/containers/9b2c.../*-json.log
-rw-r----- 1 root root 30G Jan 2 11:41 /var/lib/docker/containers/9b2c.../9b2c...-json.log
cr0x@server:~$ sleep 10; sudo ls -lh /var/lib/docker/containers/9b2c.../*-json.log
-rw-r----- 1 root root 30G Jan 2 11:41 /var/lib/docker/containers/9b2c.../9b2c...-json.log
Ce que cela signifie : Si la taille augmente notablement en quelques secondes, le service est en train de spammer les logs. Si elle est stable, l’« explosion » a peut‑être eu lieu plus tôt.
Décision : S’il y a une croissance active rapide, traitez-le comme un incident : réduisez le niveau de logs, limitez le débit, ou corrigez la boucle de crash.
Task 10: Truncate a huge log file without restarting the container
cr0x@server:~$ sudo truncate -s 0 /var/lib/docker/containers/9b2c.../9b2c...-json.log
cr0x@server:~$ sudo ls -lh /var/lib/docker/containers/9b2c.../9b2c...-json.log
-rw-r----- 1 root root 0 Jan 2 11:44 /var/lib/docker/containers/9b2c.../9b2c...-json.log
Ce que cela signifie : Vous venez de libérer de l’espace immédiatement. Le conteneur continue de tourner ; Docker continue d’écrire sur le même inode.
Décision : C’est du confinement, pas une réparation. Implémentez la rotation ensuite ou vous reviendrez vite ici.
Task 11: Confirm disk space is back and the host can breathe
cr0x@server:~$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 120G 86G 29G 75% /
Ce que cela signifie : Vous avez gagné du temps. Employez-le judicieusement.
Décision : Appliquez des limites de rotation au niveau du démon, puis validez avec un nouveau conteneur.
Task 12: Configure daemon-level json-file rotation (the real fix)
cr0x@server:~$ sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
},
"storage-driver": "overlay2"
}
EOF
Ce que cela signifie : Les nouveaux conteneurs effectueront une rotation d’environ 10MB et conserveront 5 fichiers (environ 50MB par conteneur, plus overhead).
Décision : Redémarrez Docker de manière contrôlée ; notez que les conteneurs existants peuvent devoir être recréés pour adopter ces valeurs par défaut.
Task 13: Validate configuration is syntactically OK before restart
cr0x@server:~$ sudo jq . /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
},
"storage-driver": "overlay2"
}
Ce que cela signifie : JSON valide. Docker ne refusera pas de démarrer à cause d’une virgule manquante.
Décision : Redémarrez Docker, mais comprenez l’impact : les conteneurs peuvent redémarrer selon votre système d’init et votre orchestration.
Task 14: Restart Docker and confirm it came back cleanly
cr0x@server:~$ sudo systemctl restart docker
cr0x@server:~$ systemctl is-active docker
active
Ce que cela signifie : Docker est actif. Vérifiez maintenant que les paramètres de journalisation sont réellement appliqués aux nouveaux conteneurs.
Décision : Lancez un conteneur de test et inspectez sa configuration de logs.
Task 15: Verify a new container inherits the log opts
cr0x@server:~$ docker run --rm --name logtest alpine:3.20 sh -c 'i=0; while [ $i -lt 20000 ]; do echo "line $i"; i=$((i+1)); done'
line 0
line 1
line 2
...
cr0x@server:~$ docker inspect --format '{{json .HostConfig.LogConfig}}' logtest
{"Type":"json-file","Config":{"max-file":"5","max-size":"10m"}}
Ce que cela signifie : Les limites de rotation sont présentes dans la configuration du conteneur.
Décision : Planifiez comment appliquer ces paramètres aux conteneurs existants à longue durée de vie (généralement en les recréant).
Task 16: Identify which containers are missing rotation (mixed fleet reality)
cr0x@server:~$ for c in $(docker ps -q); do
name=$(docker inspect --format '{{.Name}}' "$c" | sed 's#^/##')
cfg=$(docker inspect --format '{{.HostConfig.LogConfig.Config}}' "$c")
echo "$name $cfg"
done | head
api-prod map[]
worker-prod map[max-file:3 max-size:50m]
nginx-edge map[]
Ce que cela signifie : map[] signifie généralement aucune surcharge par conteneur. Selon la version de Docker, il peut encore hériter des valeurs par défaut du démon, ou refléter d’anciennes configurations. Traitez-le comme suspect.
Décision : Pour tout conteneur critique et de longue durée avec des logs énormes, définissez explicitement des options de journalisation ou recréez-le après avoir appliqué les valeurs par défaut du démon.
Corriger la rotation des logs correctement
La correction dépend de la façon dont vous lancez les conteneurs. Les hôtes Docker autonomes sont un monde. Docker Compose en est un autre. Swarm existe (oui, toujours).
Kubernetes est son propre univers. Le principe est le même : vous avez besoin de logs locaux bornés, et d’un endroit pour envoyer les logs quand ils ont de la valeur.
Valeurs par défaut du démon : la base que chaque hôte devrait avoir
Si vous ne faites rien d’autre, définissez des limites globales dans /etc/docker/daemon.json. Cela évite « nouveau conteneur, nouveau fichier sans limite ».
Ça rend aussi les hôtes prévisibles entre environnements.
Un point de départ raisonnable pour beaucoup de services est :
max-size 10–50MB et max-file 3–10.
Plus petit pour les services à forte rotation, plus grand si vous avez besoin d’un historique local plus long pour le triage.
Le compromis est franc : une rotation plus petite signifie que vous risquez de perdre des logs anciens localement. C’est acceptable si vous avez une journalisation centralisée.
C’est imprudent si les logs locaux sont vos seuls logs.
Surcharges par conteneur : utilisez-les avec parcimonie, mais utilisez-les
Pour ce conteneur toujours bavard (ingress, proxy d’auth, serveur web avec logs d’accès), vous pouvez vouloir des surcharges explicites afin qu’un futur changement global
ne vous surprenne pas.
Avec le CLI Docker vous pouvez définir :
--log-opt max-size=... et --log-opt max-file=... sur docker run. Dans Compose, vous pouvez spécifier des options de logging par service.
L’important n’est pas la syntaxe ; c’est l’intention. Faites de « logs locaux bornés » une propriété de la charge de travail, pas un espoir attaché à l’hôte.
N’utilisez pas logrotate du système comme réparation principale
Les gens tentent cela parce qu’ils connaissent logrotate. C’est familier. C’est aussi la mauvaise couche pour les logs de conteneurs Docker.
Docker s’attend à contrôler ces fichiers ; si vous les faites pivoter extérieurement en renommant, Docker peut continuer à écrire sur l’ancien handle de fichier.
La troncature peut fonctionner en dépannage. Le renommage est là où commencent les bizarreries. Si vous insistez pour une rotation par le système, comprenez les descripteurs de fichiers
et le comportement copytruncate. La plupart des équipes ne veulent pas de ce drame relationnel à l’échelle.
Blague n°2 : la rotation des logs, c’est comme se brosser les dents — l’ignorer semble acceptable jusqu’à ce que ça devienne coûteux et personnel.
Rendez la rotation applicable, pas optionnelle
L’échec organisationnel le plus courant : un hôte est corrigé, le suivant est « temporaire », et le suivant est une snowflake construite de la mémoire de quelqu’un.
Votre correction doit être codifiée :
- Gestion de configuration (Ansible, Puppet, Chef) ou images immuables qui incluent
daemon.json. - Vérifications CI qui rejettent des spécifications Compose ou run sans limites de logging.
- Un runbook SRE de base : « Tout conteneur doit avoir des logs locaux bornés. »
Choisir un driver de journalisation en connaissance de cause
Le driver de journalisation de Docker décide où stdout/stderr vont. Ce n’est pas un réglage cosmétique. C’est un contrat opérationnel :
performance, fiabilité, rétention, et qui sera réveillé quand le disque se remplira.
json-file : simple, suffisant, dangereux si non borné
Avantages : les fichiers locaux sont faciles à inspecter ; pas de dépendance à systemd ; fonctionne partout ; les outils s’y attendent.
Inconvénients : non borné par défaut ; dupliqué si vous expédiez déjà les logs ; peut créer une forte amplification d’écriture sur des services occupés.
En pratique : json-file convient si vous le limitez et que vous avez de l’expédition des logs. Si vous le limitez et que vous n’expédiez pas, vous choisissez la perte de données
en échange de la sécurité de l’hôte. Cela peut être le bon choix, à condition d’en être conscient.
journald : centralisé sur le nœud, mais toujours fini
Avantages : cohérent avec la journalisation système ; méta-données riches ; consultable avec journalctl ; supporte la limitation de débit et les caps de taille via la config journald.
Inconvénients : si vous ne limitez pas journald, vous avez simplement déplacé le problème ; le débogage à travers les redémarrages et les paramètres de persistance peut surprendre.
Si vous utilisez journald, vous devez configurer la rétention de journald. Sinon vous avez fait un meilleur accumuloir de logs, pas un système plus sûr.
Drivers distants (syslog, fluentd, gelf, awslogs, splunk, etc.) : moins de fichiers locaux, plus de dépendances réseau
Avantages : les logs quittent le nœud, ce qui est tout l’intérêt ; rétention centralisée ; analyse sans SSH.
Inconvénients : la backpressure peut nuire ; les coupures réseau peuvent faire perdre des logs ou bloquer les conteneurs (selon le driver/les réglages) ; la complexité opérationnelle se déplace vers la pipeline de logs.
Une approche adaptée à la production est souvent : tampon local borné + envoi fiable. Ne basez pas votre réponse aux incidents sur un seul saut réseau.
Réponse d’urgence : l’hôte est plein, maintenant que faire
Quand l’hôte est à 100%, les modes de défaillance se combinent : Docker ne peut plus écrire de logs, les conteneurs plantent, le noyau ne peut pas allouer d’espace pour le minimum, et soudain
votre « simple problème de logs » devient un incident de disponibilité.
Objectifs de confinement (par ordre)
- Libérer de l’espace rapidement pour restaurer la stabilité système (tronquer les plus gros logs).
- Arrêter la source de logs à haut volume si c’est un comportement anormal (mode debug, boucle de crash, tempête d’exceptions).
- Appliquer des limites de rotation pour que le même schéma ne remplisse pas immédiatement le disque.
- Préserver suffisamment de preuves pour comprendre pourquoi c’est arrivé (prélever des extraits avant troncature si possible).
Ce qu’il ne faut pas faire en panique disque-plein
- Ne supprimez pas des répertoires aléatoires sous
/var/lib/docker. C’est comme « réparer les logs » en supprimant votre runtime. - Ne lancez pas
docker system prune -acomme réflexe sur un nœud de production. Vous libérerez de l’espace mais supprimerez aussi des images nécessaires à la récupération. - Ne redémarrez pas Docker en boucle en espérant qu’il « nettoiera quelque chose ». Les redémarrages peuvent déclencher des redémarrages en cascade des conteneurs.
Si vous devez conserver des preuves
Si la conformité ou le débogage exige de préserver une portion de logs, capturez un tail avant la troncature. Cela vous donne les dernières lignes sans emporter tout le fichier de 30GB.
cr0x@server:~$ sudo tail -n 5000 /var/lib/docker/containers/9b2c.../9b2c...-json.log > /root/api-prod-last-5000.jsonl
Ce que cela signifie : Vous avez préservé les événements récents. Ce n’est pas parfait, mais c’est généralement suffisant pour voir le schéma de la tempête d’erreurs.
Décision : Tronquez le gros fichier après avoir capturé ce dont vous avez besoin, puis corrigez la rotation.
Trois micro-récits issus du réel en entreprise
Micro-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne exécutait une API client sur quelques hôtes Docker costauds. L’équipe service était disciplinée sur les métriques et traces, et supposait que les logs
étaient « le problème de quelqu’un d’autre » parce qu’un agent de logs était installé sur les hôtes. Tout le monde croyait que les logs étaient envoyés hors-serveur et donc inoffensifs.
L’hypothèse était erronée d’une manière très précise : l’agent suivait des fichiers sous /var/log et quelques chemins applicatifs, mais n’ingérait pas les
json logs des conteneurs Docker sous /var/lib/docker/containers. Les conteneurs écrivaient sagement sur stdout. Docker écrivait sagement des json logs. Personne ne les limitait.
L’agent de shipping ne les voyait jamais. Les logs restèrent locaux. Pour toujours.
La panne n’a pas commencé par l’API qui échoue. Elle a commencé quand le système de fichiers root de l’hôte a atteint 100%. À ce stade, tout devint mensonger : les services échouaient
pour des raisons qui semblaient sans rapport—les handshakes TLS expiraient, les contrôles de santé flambaient, et les conteneurs redémarraient parce que leurs propres écritures de logs échouaient.
Ops vit une propagation de symptômes et chassa des fantômes.
La réparation fut peu glamour : limiter globalement les logs json-file, expédier explicitement les logs de conteneur, et ajouter de la supervision qui suit
la croissance de /var/lib/docker/containers. Le changement culturel important fut encore plus ennuyeux : « la journalisation existe en deux endroits—collecte et rétention. »
Ils avaient la collecte pour certains logs, et la rétention pour aucun.
Micro-récit 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation en eut assez des pics d’utilisation disque. Ils décidèrent de définir max-size extrêmement bas à l’échelle du parc. Pensez quelques mégaoctets.
Le raisonnement était clair : protéger les nœuds, tout expédier centralement, et faire du disque local un tampon d’urgence seulement.
Deux semaines plus tard, la réponse aux incidents commença à échouer d’une nouvelle façon. Quand la pipeline de logs centrale avait des accros (maintenance, saturation réseau, ou juste un indexeur surchargé),
les logs locaux étaient trop petits pour combler la période. Les ingénieurs SSH sur les nœuds pendant une panne trouvaient que les derniers logs locaux ne couvraient qu’une minute ou deux.
La pipeline était tombée, et le tampon local était effectivement vide. Le triage devint de la divination.
Puis vint l’effet secondaire : des rotations fréquentes créèrent beaucoup de petits fichiers et plus de churn métadonnées. Sur certains nœuds, la combinaison d’un fort volume de logs et
de seuils de rotation très bas augmenta la charge. L’« optimisation » n’était pas catastrophique, mais rendit le système plus bruyant et plus difficile à raisonner.
Le compromis final fut sensé : augmenter max-size pour conserver une fenêtre locale utile, tout en restant borné ; rendre l’expédition résiliente ; et ajouter des alertes sur
la santé de la pipeline de logs. La rotation n’est pas un substitut à une collecte fiable. C’est un filet de sécurité, pas le trapèze.
Micro-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une société du secteur financier traitait ses hôtes comme du bétail, pas comme des animaux de compagnie. L’équipe SRE avait une habitude qui semblait fastidieuse dans les revues de tickets :
chaque image de base inclut par défaut les réglages du démon Docker pour la journalisation, et chaque provision inclut un petit test qui démarre un conteneur de test et inspecte
son LogConfig effectif. Personne ne s’en vantait. C’était juste une case à cocher.
Un après-midi, une release introduisit une tempête d’exceptions. Le service commença à déverser des traces de pile à haut volume. Ce fut l’incident classique « les logs remplissent le disque
et tout meurt ». Mais sur ces nœuds, chaque conteneur avait des caps stricts de logs. L’utilisation disque monta, puis se stabilisa.
L’incident fit quand même mal : le service était malade et nécessita un rollback. Mais la flotte des hôtes resta stable. Pas d’échecs en cascade. Pas de nettoyage paniqué qui risquait de supprimer des images et casser la récupération.
L’équipe put se concentrer sur le bug réel au lieu de jouer le concierge des systèmes de fichiers.
Le postmortem fut presque ennuyeux. Et c’est le compliment. La pratique ennuyeuse—valeurs par défaut appliquées et un petit test de validation—transforma un incident potentiel de plateforme en un incident applicatif simple. Vos meilleures victoires opérationnelles ressemblent à « rien de spécial ne s’est produit ».
Erreurs courantes (symptôme → cause → correctif)
1) Symptom: Root filesystem fills every few days
Cause : driver json-file sans max-size/max-file, plus workloads bavards.
Correctif : Définir des valeurs par défaut du démon dans /etc/docker/daemon.json, redémarrer Docker en sécurité, et recréer les conteneurs de longue durée si nécessaire.
2) Symptom: You configured log rotation but old containers still have huge logs
Cause : Les conteneurs existants n’adoptent pas forcément les nouvelles valeurs par défaut du démon ; ils gardent leur LogConfig antérieur.
Correctif : Pour les services critiques, définir explicitement des options de logging par service (Compose/Swarm) ou recréer les conteneurs après application des valeurs par défaut.
3) Symptom: Disk is full but docker system df looks normal
Cause : Le comptage de Docker ne reflète pas toujours l’explosion brute des fichiers de logs ou les réalités du système de fichiers.
Correctif : Mesurez directement avec find et du sous /var/lib/docker/containers ; traitez les logs comme des consommateurs de disque à part entière.
4) Symptom: You switched to journald and disk still fills
Cause : journald retient trop ; aucune limite ou le stockage persistant croît sans contrôle.
Correctif : Configurez la rétention de journald (par ex. max use système), et surveillez l’utilisation disque du journal. Ne « mettez pas journald » et n’ignorez pas la suite.
5) Symptom: After external logrotate, Docker keeps writing to a “deleted” file and space isn’t freed
Cause : Le descripteur de fichier pointe toujours vers l’ancien inode ; renommer/supprimer ne libère pas l’espace tant que l’écrivain n’a pas fermé.
Correctif : Préférez la rotation intégrée de Docker. En urgence, utilisez truncate sur le chemin de log actif.
6) Symptom: Containers stall when logging backend is slow
Cause : Certains drivers de logs peuvent appliquer de la backpressure ; les écritures sur stdout peuvent bloquer si le driver ne suit pas.
Correctif : Testez le comportement du driver sous charge, assurez un buffering adéquat, et évitez la journalisation distante synchrone sans résilience.
7) Symptom: You set max-size tiny and now you can’t debug incidents
Cause : La fenêtre de rétention locale est trop courte ; la journalisation centralisée n’est pas assez fiable.
Correctif : Augmentez les caps locaux pour conserver une fenêtre utile, et durcissez la pipeline d’expédition et la surveillance.
8) Symptom: Log files rotate but disk usage still creeps up
Cause : Le vrai coupable est ailleurs : couches modifiables overlay2, volumes orphelins, build cache, ou logs hors Docker.
Correctif : Décomposez l’usage disque avec du et docker system df, puis nettoyez de manière ciblée (avec contrôle de changement).
Checklists / plan étape par étape
Étapes : stabiliser un hôte qui se remplit maintenant
- Confirmer quelle partition est pleine (
df -hT). - Trouver les plus gros fichiers de logs sous
/var/lib/docker/containers. - Associer l’ID du conteneur à un service et vérifier s’il est en boucle de crash ou en mode debug.
- Capturer un petit tail si vous avez besoin de preuves (
tail -n). - Tronquer les principaux coupables (
truncate -s 0). - Appliquer des valeurs par défaut de logging au démon et valider le JSON.
- Redémarrer Docker de manière contrôlée (ou le planifier). Confirmer qu’il est actif.
- Recréer ou redéployer les pires coupables pour qu’ils reprennent la nouvelle politique de logging.
- Configurer des alertes sur l’utilisation disque et sur le taux de croissance des logs (deltas de taille de répertoire), pas seulement « disque à 90% ».
Checklist de base : ce que chaque hôte Docker doit avoir
- Le démon a un
log-driverexplicite et deslog-optsbornés (ou la rétention journald est configurée). /var/lib/dockerest dimensionné pour images + volumes + logs bornés, pas « ce qui reste sur la racine ».- La supervision inclut :
- Alertes d’utilisation système de fichiers avec seuils sensés
- Alertes d’utilisation d’inodes (oui, la rotation peut déplacer le problème)
- Alertes de croissance sur
/var/lib/docker/containers
- La santé de la pipeline de logs est surveillée si les logs sont expédiés hors nœud.
- Le runbook inclut des commandes sûres : inspecter les chemins de logs, tronquer, vérifier la config du démon.
Checklist gestion des changements : déployer de nouvelles options de logs en sécurité
- Décider des valeurs par défaut (
max-size,max-file) basées sur la fenêtre de rétention locale acceptable. - Mettre à jour
/etc/docker/daemon.jsonavec un JSON valide ; valider avecjq. - Choisir une méthode de déploiement :
- Recréer les conteneurs progressivement (préféré)
- Redémarrage hôte par hôte en maintenance
- Vérifier : les nouveaux conteneurs ont le
HostConfig.LogConfigattendu. - Confirmer que l’utilisation disque se stabilise sur plusieurs jours et que la réponse aux incidents dispose toujours de logs locaux suffisants.
FAQ
1) Pourquoi les logs Docker sont-ils si gros alors que mon appli « ne logue pas tant que ça » ?
Votre appli peut ne pas « logger » volontairement, mais elle peut écrire beaucoup sur stderr, répéter des traces de pile, imprimer des logs d’accès à un fort QPS, ou fonctionner en mode debug.
De plus, certaines bibliothèques loguent plus que prévu en cas d’erreurs (les tempêtes de retry sont classiques).
2) Est-ce que définir max-size et max-file supprime des logs ?
Ça effectue une rotation et supprime les anciens morceaux une fois la limite de rétention atteinte. C’est une suppression, par conception. Si vous avez besoin d’une rétention plus longue, expédiez les logs centralement.
Le disque local n’est pas une archive.
3) Les valeurs par défaut du daemon.json s’appliqueront-elles aux conteneurs en cours ?
Pas de façon fiable. Les conteneurs en cours conservent leurs paramètres de logs configurés. Considérez les valeurs par défaut du démon comme « pour les nouveaux conteneurs », et planifiez de recréer les workloads longue durée.
4) Tronquer le json log est-ce sûr ?
Oui pour du confinement. truncate -s 0 garde le fichier et l’inode ; Docker continue d’écrire. Vous perdez l’historique dans ce fichier, donc capturez un tail d’abord si nécessaire.
5) Dois-je passer de json-file à journald ?
Si votre parc est basé sur systemd et que votre équipe maîtrise journalctl, journald peut être un bon choix. Mais limitez journald. Sinon vous avez juste déplacé le point de débordement.
6) Et Kubernetes—est-ce que ça compte toujours ?
Oui. Kubernetes dépend toujours de la gestion des logs au niveau du nœud. Les logs du runtime de conteneurs, le comportement du kubelet, et les paramètres de rotation du nœud influencent la pression disque.
Si vous l’ignorez, les nœuds sont évincés et les workloads clignotent. Différents outils, mêmes lois physiques.
7) Pourquoi ne pas simplement pruner Docker régulièrement ?
Pruner images et caches peut aider, mais n’adresse pas le problème central : des écritures de logs sans limites. De plus, un pruning agressif peut ralentir les déploiements et casser les chemins de rollback.
Corrigez la politique de logs d’abord, puis prunez intentionnellement.
8) Comment choisir de bonnes valeurs de rotation ?
Choisissez une fenêtre de rétention locale acceptable pendant une panne de la pipeline de logging. Pour beaucoup d’équipes, 30 minutes à quelques heures de logs est une fenêtre utile.
Convertissez cela en taille en fonction de votre débit de logs typique, puis limitez. Commencez conservateur, observez, ajustez.
9) Mon disque est plein mais je ne trouve pas de gros json logs—quoi vérifier d’autre ?
Regardez les volumes et les layers modifiables (caches applicatifs, uploads, fichiers temporaires), le build cache, et tout autre répertoire sur le même système de fichiers.
Vérifiez aussi l’épuisement d’inodes : beaucoup de petits fichiers rotatifs peuvent poser problème même quand l’espace semble disponible.
10) La rotation des logs peut-elle nuire à la performance ?
Une rotation excessivement fréquente peut ajouter de la charge. Mais le coût en performance de logs sans limites est pire : écritures disque constantes, fort I/O wait, et incident éventuel.
L’objectif est « borné et ennuyeux ».
Conclusion : prochaines étapes durables
Si les logs Docker explosent, ce n’est pas un cas marginal. C’est une fonctionnalité de sécurité manquante. Le disque de votre hôte n’est pas une œuvre de charité.
La réparation est simple : identifiez les fichiers de logs, limitez-les correctement, et arrêtez de traiter « stdout » comme un puits infini.
Faites cela ensuite, dans cet ordre :
- Aujourd’hui : trouvez les plus gros fichiers
*-json.log; tronquez les pires si la pression disque est élevée. - Cette semaine : définissez des valeurs de rotation au niveau du démon ; validez avec un conteneur de test ; recréez les services critiques pour adopter la politique.
- Ce mois-ci : rendez la politique de logs exécutable dans le provisioning et la CI ; assurez-vous que la journalisation centralisée est saine et surveillée ; alertez sur la croissance, pas seulement sur la saturation.
Une fois cela en place, les incidents disque-plein liés aux logs disparaissent de votre agenda. Vos futures pages d’alerte devraient concerner de vraies défaillances, pas votre plateforme qui se noie dans sa propre narration.