Introduction
Les pilotes de journalisation Docker déterminent où sont envoyés les flux stdout et stderr des conteneurs. Lorsqu'ils échouent, les applications peuvent sembler saines alors que les journaux disparaissent, que l'utilisation du disque croît sans limite ou que les pipelines de journalisation centralisée perdent silencieusement des événements. Cet article fournit une méthode de dépannage pratique pour les pannes les plus courantes des pilotes de journalisation Docker, avec des commandes concrètes, des sorties attendues et des étapes de récupération.
L'objectif est la sécurité opérationnelle : observer avant de modifier, limiter le rayon d'impact, utiliser des valeurs fictives au lieu de secrets, vérifier le résultat et documenter la procédure de retour en arrière si l'état attendu n'est pas atteint. Chaque section cible un mode de défaillance spécifique et inclut un diagnostic en lecture seule, le plus petit changement justifié et une commande de vérification.
Ce guide suppose un hôte Linux avec Docker Engine 20.10 ou ultérieur et une compréhension de base du cycle de vie des conteneurs. Les commandes sont présentées pour Docker autonome et les projets Docker Compose. Lorsque des fichiers ou des secrets apparaissent, remplacez les valeurs fictives par les données de votre propre environnement.
Inventaire de la version et de l'environnement
Avant de modifier toute configuration de journalisation, capturez l'état actuel du démon Docker, des conteneurs concernés et de l'environnement hôte. Le pilote de journalisation d'un conteneur est défini au moment de sa création, mais le pilote par défaut pour les nouveaux conteneurs est contrôlé par la configuration du démon. Commencez par des observations en lecture seule.
Exécutez les commandes suivantes et enregistrez leur sortie.
# Version de Docker et configuration du démon
docker version
sudo cat /etc/docker/daemon.json 2>/dev/null || echo "daemon.json introuvable"
# Pilote de journalisation par défaut effectif et options
docker info --format '{{.LoggingDriver}}'
docker info --format '{{json .Plugins.Log}}'
# Conteneurs en cours d'exécution avec leur pilote de journalisation configuré
docker ps --format 'table {{.Names}} {{.Status}} {{.LogDriver}}'
Exemple de sortie d'une configuration syslog défaillante :
LoggingDriver: syslog
Containers:
NAMES STATUS LOGDRIVER
web Up 2 hours syslog
worker Up 2 hours syslog
db Up 2 hours json-file
Si daemon.json existe, examinez-le avant d'apporter des modifications. Une mauvaise configuration courante consiste à définir log-driver sur une valeur que le démon ne peut pas charger, ce qui empêche le démarrage des conteneurs.
cat /etc/docker/daemon.json
Exemple de mauvaise configuration :
{
"log-driver": "syslog",
"log-opts": {
"syslog-address": "udp://1.2.3.4:514"
}
}
Si le serveur syslog à 1.2.3.4 est injoignable, chaque nouveau conteneur échouera avec une erreur du type failed to initialize logging driver: dial udp 1.2.3.4:514: connect: connection refused. Les conteneurs existants ne sont pas affectés car leur configuration de journalisation a été figée à la création.
Vérifiez les plugins de journalisation disponibles :
docker info | grep -A 5 "Logging Drivers"
La sortie attendue inclut json-file, syslog, journald, gelf, fluentd, awslogs, splunk et d'autres selon votre version de Docker et les plugins installés.
Pour les configurations de journalisation basées sur des données, confirmez où les fichiers journaux sont écrits avant de faire des changements. Le pilote json-file écrit dans /var/lib/docker/containers/<container-id>/<container-id>-json.log par défaut. Le pilote journald envoie les journaux au journal systemd. Le pilote syslog envoie à un démon syslog distant ou local. Utilisez docker inspect pour voir la configuration de journalisation exacte d'un conteneur.
docker inspect --format '{{json .HostConfig.LogConfig}}' web
Exemple pour un conteneur avec journalisation json-file et rotation par taille :
{"Type":"json-file","Config":{"max-size":"10m","max-file":"3"}}
Si le Type est journald, les journaux du conteneur ne sont pas stockés dans le répertoire de Docker, et docker logs peut ne pas fonctionner comme prévu.
Un test local de type production doit inclure un test de redémarrage : arrêtez le conteneur, recréez-le et confirmez que l'application émet toujours des journaux vers la destination attendue. Si les journaux disparaissent après un redémarrage, le pilote de journalisation n'a probablement pas été configuré de manière cohérente entre les recréations de conteneurs, ou la valeur par défaut du démon a été modifiée après la création du conteneur.
Chemin de configuration sûr
Lorsqu'un pilote de journalisation doit être modifié, suivez un chemin sûr : identifiez le composant, vérifiez les prérequis, apportez un seul changement limité et vérifiez le résultat. Pour les changements au niveau du conteneur, vous devez recréer le conteneur ; pour les changements au niveau du démon, vous devez redémarrer le démon Docker (ce qui redémarre tous les conteneurs en cours d'exécution sur la plupart des systèmes). Évitez de modifier les deux en même temps.
Modifier le pilote de journalisation pour un seul conteneur
Supposons que le conteneur web utilise actuellement json-file, mais que vous devez passer à syslog pour une collecte centralisée. D'abord, inspectez la configuration existante et notez les options personnalisées.
docker inspect --format '{{json .HostConfig.LogConfig}}' web
Ensuite, arrêtez et supprimez le conteneur, puis recréez-le avec les nouvelles options de journalisation. Utilisez docker run avec les drapeaux --log-driver et --log-opt.
docker stop web
docker rm web
docker run -d --name web \
--log-driver syslog \
--log-opt syslog-address=tcp://logs.internal:514 \
--log-opt syslog-facility=daemon \
--log-opt tag="web/{{.Name}}/{{.ID}}" \
myapp:latest
Si vous utilisez Docker Compose, modifiez la définition du service dans docker-compose.yml :
services:
web:
image: myapp:latest
logging:
driver: syslog
options:
syslog-address: "tcp://logs.internal:514"
syslog-facility: "daemon"
tag: "web/{{.Name}}/{{.ID}}"
Puis recréez le service :
docker compose up -d web
Vérifiez que les journaux sont envoyés au serveur syslog. Sur le serveur syslog, vérifiez les messages entrants. Si vous utilisez un syslog local, inspectez /var/log/syslog ou /var/log/messages pour les entrées marquées avec web.
Sur l'hôte Docker, vous pouvez aussi vérifier que le conteneur a démarré sans erreur de journalisation :
docker inspect --format '{{.State.Status}} {{.State.Error}}' web
Attendu : running sans erreur. Si le conteneur est dans l'état created ou exited avec une erreur mentionnant le pilote de journalisation, le pilote n'a pas pu s'initialiser, souvent à cause d'un serveur syslog injoignable ou d'options invalides.
Modifier le pilote de journalisation par défaut dans daemon.json
Modifier la valeur par défaut du démon n'affecte que les conteneurs créés après le changement. Les conteneurs existants conservent leurs paramètres d'origine. Pour définir le pilote de journalisation par défaut sur json-file avec rotation par taille :
- Éditez
/etc/docker/daemon.json(créez-le s'il est manquant) :
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
- Redémarrez le démon Docker :
sudo systemctl restart docker
- Vérifiez la nouvelle valeur par défaut :
docker info --format '{{.LoggingDriver}}'
Attendu : json-file. Créez ensuite un conteneur de test et inspectez sa configuration de journalisation :
docker run -d --name logtest alpine sleep infinity
docker inspect --format '{{json .HostConfig.LogConfig}}' logtest
La sortie attendue inclut "Type":"json-file" avec "max-size":"10m" et "max-file":"3". Si le redémarrage du démon échoue, validez le fichier JSON avec python -m json.tool /etc/docker/daemon.json. Une erreur courante est une virgule finale.
Ne modifiez jamais daemon.json sans un plan de retour en arrière. Avant de l'éditer, copiez le fichier :
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak
Si le démon ne démarre pas après le changement, restaurez la sauvegarde et redémarrez. Conservez la sauvegarde jusqu'à ce que vous ayez vérifié que tous les nouveaux conteneurs démarrent et journalisent correctement.
Vérification et diagnostics
Après un changement de configuration de journalisation, vérifiez que les journaux circulent comme prévu. La première vérification est docker logs pour les conteneurs utilisant les pilotes json-file ou journald. Pour syslog ou fluentd, docker logs peut être vide car les journaux sont envoyés à une destination externe.
Lire les journaux pour le pilote json-file
Pour un conteneur utilisant json-file :
docker logs --tail 50 web
Si cela ne retourne rien alors que l'application génère une sortie, vérifiez le fichier journal sur le disque. D'abord, obtenez l'ID du conteneur :
docker inspect --format '{{.Id}}' web
Puis examinez le fichier journal (l'ID complet est nécessaire) :
sudo ls -lh /var/lib/docker/containers/<full-id>/<full-id>-json.log
Si la taille du fichier est 0 et que l'application fonctionne depuis un certain temps, le processus peut écrire dans un fichier à l'intérieur du conteneur au lieu de stdout. Vérifiez le comportement du processus du conteneur :
docker exec web sh -c 'ls -l /proc/1/fd/1 /proc/1/fd/2'
La sortie attendue montre des liens symboliques vers /dev/null ou un fichier à l'intérieur du conteneur, ce qui signifie que stdout est redirigé. Corrigez l'application pour qu'elle écrive les journaux vers stdout et stderr, pas vers des fichiers.
Tester la livraison syslog
Pour le pilote syslog, vérifiez que les journaux atteignent le serveur syslog. Sur l'hôte Docker, cherchez des erreurs de connexion dans le journal d'exécution du conteneur (pas les journaux du conteneur). Les journaux du démon Docker contiennent des erreurs sur les défaillances du pilote de journalisation.
sudo journalctl -u docker.service | grep -i syslog
Recherchez des messages tels que :
Failed to log message: write udp 192.168.1.10:514->logs.internal:514: connection refused
Si vous voyez « connection refused », le serveur syslog est en panne ou n'écoute pas. Confirmez la connectivité depuis l'hôte Docker :
nc -vz logs.internal 514
Si le port est fermé, réparez le serveur syslog ou ajustez l'adresse. Si le port est ouvert mais que les messages n'apparaissent pas, vérifiez la configuration du serveur syslog pour les filtres de facilité et de gravité. La facilité par défaut est daemon, et la gravité par défaut est info. Certains serveurs syslog suppriment les messages avec la facilité daemon s'ils n'attendent que local0.
Pour changer la facilité d'un conteneur en cours d'exécution, vous devez le recréer. Utilisez --log-opt syslog-facility=local0 dans docker run ou l'équivalent Compose.
Analyser les journaux journald
Si vous utilisez journald, docker logs peut afficher des journaux, mais le journal peut aussi contenir des entrées dupliquées ou manquantes. Consultez les journaux du journal pour un conteneur spécifique :
sudo journalctl CONTAINER_NAME=web
Si le nom du conteneur n'est pas défini comme champ de journal, utilisez l'ID du conteneur :
sudo journalctl CONTAINER_ID=$(docker inspect --format '{{.Id}}' web)
Vérifiez les options du pilote de journalisation pour tag et labels. L'option tag ajoute un identifiant personnalisé à chaque entrée de journal. Définissez-la pour inclure le nom du conteneur ou un autre champ unique.
Si les journaux journald sont manquants, vérifiez la limite de débit de journald. La valeur par défaut RateLimitBurst dans /etc/systemd/journald.conf peut supprimer des journaux lorsqu'un conteneur produit un volume élevé. Augmentez la limite et redémarrez systemd-journald :
RateLimitBurst=10000
RateLimitInterval=1s
Puis redémarrez à la fois journald et le conteneur.
Modes de défaillance et récupération
Cette section décrit les scénarios de défaillance courants pour les pilotes de journalisation Docker, leurs symptômes, le diagnostic et les étapes de récupération.
Défaillance : le conteneur ne démarre pas avec « failed to initialize logging driver »
Symptôme : docker run ou docker compose up renvoie une erreur comme :
docker: Error response from daemon: failed to initialize logging driver: dial tcp 10.0.0.5:514: connect: connection refused.
Diagnostic :
- Vérifiez le pilote de journalisation configuré et l'adresse :
docker inspect --format '{{json .HostConfig.LogConfig}}' <container-name-or-id>
Si le conteneur n'a pas pu être créé, inspectez la commande que vous avez utilisée ou le fichier Compose.
- Vérifiez la connectivité vers le point de terminaison de journalisation :
Pour syslog TCP :
nc -vz 10.0.0.5 514
Pour syslog UDP (sans connexion, donc nc -vz peut ne pas fonctionner ; utilisez nc -u) :
nc -u -w1 10.0.0.5 514 < /dev/null && echo "UDP envoyé"
Vérifiez que le serveur syslog fonctionne et est joignable.
Récupération :
- Si le serveur syslog est temporairement en panne, vous pouvez démarrer le conteneur avec un pilote de journalisation de repli qui ne dépend pas du réseau, comme
json-file.
docker run -d --name web --log-driver json-file myapp:latest
- Pour changer définitivement le pilote, mettez à jour la configuration du conteneur ou le fichier Compose. Pour les valeurs par défaut du démon, éditez
daemon.jsonet redémarrez Docker.
- Si vous devez utiliser syslog mais que le serveur est en panne, démarrez un récepteur syslog local sur l'hôte Docker (par exemple,
socatoursyslog) et pointez le conteneur verslocalhost:514. Lorsque le serveur distant est de retour, changez l'adresse.
Défaillance : docker logs n'affiche aucune sortie mais l'application fonctionne
Symptôme : docker logs <container> renvoie vide, mais le conteneur fonctionne et produit probablement une sortie.
Diagnostic :
- Vérifiez le pilote de journalisation du conteneur :
docker inspect --format '{{.HostConfig.LogConfig.Type}}' <container>
Si c'est syslog, fluentd, gelf ou awslogs, docker logs n'affichera pas les journaux à moins que le pilote ne prenne en charge le drapeau --details (ce n'est pas le cas). Les journaux sont envoyés à la destination externe.
- Pour
json-file, vérifiez que l'application écrit vers stdout/stderr. Exécutez une commande de test dans un nouveau conteneur qui écrit définitivement vers stdout :
docker run --rm --log-driver json-file alpine echo "test log"
Puis vérifiez docker logs pour ce conteneur (il aura disparu après --rm, donc utilisez --name et supprimez manuellement) :
docker run -d --name logtest echo_image
docker logs logtest
Si cela fonctionne, l'application n'écrit pas vers stdout. Capturez les descripteurs de fichiers de l'application comme indiqué précédemment.
Récupération :
- Modifiez l'application pour qu'elle journalise vers stdout/stderr. Pour de nombreux frameworks, définissez la sortie de journalisation sur la console. Pour Nginx, utilisez
access_log /dev/stdout;eterror_log /dev/stderr;. Pour Python, utilisezlogging.basicConfig(stream=sys.stdout).
- Si vous ne pouvez pas modifier l'application, utilisez un conteneur sidecar ou un agent de journalisation pour suivre les fichiers à l'intérieur du conteneur et les transférer. Par exemple, exécutez un sidecar qui monte le répertoire des journaux du conteneur et lit les fichiers.
Défaillance : les fichiers journaux remplissent le disque
Symptôme : l'hôte Docker manque d'espace disque et /var/lib/docker est volumineux.
Diagnostic :
- Trouvez les plus gros fichiers journaux :
sudo find /var/lib/docker/containers -name "*-json.log" -exec du -h {} + | sort -rh | head -5
- Vérifiez les options de journalisation du conteneur fautif :
docker inspect --format '{{json .HostConfig.LogConfig}}' <container>
S'il n'y a pas de max-size et max-file, le pilote json-file croît sans limite par défaut.
Récupération :
- Pour un conteneur existant, vous ne pouvez pas modifier les options de journalisation sans le recréer. Recréez avec des limites de taille :
docker stop <container>
docker rm <container>
docker run -d --name <container> \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
<image>
- Pour tous les futurs conteneurs, définissez les valeurs par défaut dans
daemon.jsoncomme indiqué précédemment.
- Récupérez immédiatement de l'espace en tronquant les gros fichiers journaux. C'est sûr pour les journaux
json-file, mais cela perd les anciennes entrées :
sudo truncate -s 0 /var/lib/docker/containers/<id>/<id>-json.log
Ne supprimez pas le fichier ; Docker pourrait continuer à écrire dans l'inode supprimé.
Défaillance : messages syslog manquants ou non conformes RFC3164
Symptôme : les messages de journal apparaissent dans syslog mais sont malformés, il manque des champs, ou ils ne sont pas analysés par les systèmes en aval.
Diagnostic :
- Vérifiez les options du pilote syslog :
docker inspect --format '{{json .HostConfig.LogConfig.Config}}' <container>
- Vérifiez le format syslog. Docker envoie le format RFC5424 par défaut (
syslog-format=rfc5424). Certains serveurs syslog hérités attendent RFC3164. Changez le format :
--log-opt syslog-format=rfc3164
- Assurez-vous que l'option
tagest définie sur un identifiant unique. Le tag par défaut est{{.ID}}, une longue chaîne hexadécimale. Utilisez un tag plus utile :
--log-opt tag="{{.Name}}/{{.ID}}"
Récupération : recréez le conteneur avec des options corrigées. Si le serveur syslog analyse toujours mal, activez la journalisation de débogage sur le serveur syslog pour voir les messages bruts. Ajustez la facilité, la gravité ou le format en conséquence.
Liste de contrôle opérationnelle
Utilisez cette liste avant et après tout changement de configuration de journalisation.
| Étape | Action | Commande ou fichier | Résultat attendu |
|---|---|---|---|
| 1 | Enregistrer la version Docker actuelle et les valeurs par défaut de journalisation du démon | docker version, docker info --format '{{.LoggingDriver}}' | Version 20.10+, le nom du pilote correspond à l'attente |
| 2 | Inspecter la configuration de journalisation actuelle du conteneur cible | docker inspect --format '{{json .HostConfig.LogConfig}}' <container> | Affiche le pilote et les options actuels |
| 3 | Capturer des journaux d'exemple avant le changement | docker logs --tail 20 <container> (si json-file) | Lignes de journal récentes visibles |
| 4 | Sauvegarder la configuration du démon si on modifie les valeurs par défaut | sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak | Le fichier de sauvegarde existe |
| 5 | Apporter un seul changement limité (conteneur ou démon) | Modifier le fichier Compose ou daemon.json ; recréer le conteneur ou redémarrer le démon | Aucune erreur de syntaxe |
| 6 | Vérifier la nouvelle configuration de journalisation | docker inspect --format '{{json .HostConfig.LogConfig}}' <container> | Le pilote et les options correspondent au changement prévu |
| 7 | Générer une entrée de journal de test | docker exec <container> logger "test" ou équivalent | L'entrée apparaît à la destination |
| 8 | Vérifier les erreurs dans les journaux du démon | sudo journalctl -u docker.service --since "5 minutes ago" | grep -i "log" | Aucune erreur liée au pilote de journalisation |
| 9 | Confirmer l'absence de redémarrages ou de changements d'état inattendus | docker ps -a | Conteneurs dans l'état attendu |
| 10 | Documenter le plan de retour en arrière et le déclencheur | Écrire la procédure dans le runbook | Si la nouvelle configuration échoue, revenir à la sauvegarde et recréer les conteneurs |
Pour chaque ligne, remplacez le paramètre fictif <container> par le nom réel du conteneur. Si une étape échoue, arrêtez-vous et enquêtez avant de continuer. Ne faites pas plusieurs changements à la fois ; si le système casse, vous ne saurez pas quel changement en est la cause.
Exemple de retour en arrière pour un changement de daemon.json qui casse le démarrage des conteneurs :
sudo systemctl stop docker
sudo cp /etc/docker/daemon.json.bak /etc/docker/daemon.json
sudo systemctl start docker
docker ps
Conclusion
Les problèmes de pilote de journalisation Docker résultent souvent d'une mauvaise configuration, de problèmes réseau ou de limites de rotation manquantes. En suivant la méthode structurée de cet article — observer, apporter un changement limité, vérifier et revenir en arrière si nécessaire — vous pouvez résoudre la plupart des défaillances sans affecter la stabilité de la production.
Commencez par l'Inventaire de la version et de l'environnement pour capturer l'état actuel. Utilisez le Chemin de configuration sûr pour changer les pilotes avec un rayon d'impact minimal. Vérifiez la livraison des journaux avec des commandes concrètes. Lorsque les choses tournent mal, référez-vous à la section Modes de défaillance et récupération pour une récupération étape par étape.
Enfin, adoptez la Liste de contrôle opérationnelle comme runbook standard pour tout changement de journalisation. Documentez les valeurs spécifiques à votre environnement, conservez des sauvegardes et testez les changements d'abord dans un environnement de préproduction. Avec ces pratiques, les pilotes de journalisation Docker deviennent une partie fiable de votre pile d'observabilité plutôt qu'une source de surprises opérationnelles.