Docker : diagnostiquer un conteneur en boucle de redémarrage sans masquer l’erreur

Un conteneur qui affiche sans cesse Restarting n’est pas réparé par restart: always : Docker relance simplement un processus qui continue d’échouer. Le bon réflexe consiste à capturer les journaux, l’état final et le code de sortie avant de modifier l’image, les ressources ou la politique de redémarrage.

Cette procédure concerne Docker Engine et Docker Compose. Elle aide à distinguer une commande introuvable, une erreur applicative, un manque de mémoire, une dépendance indisponible, un problème de volume ou un contrôle de santé mal conçu. Les commandes utilisent mon-conteneur et mon-service comme exemples : remplacez-les par vos noms réels.

Réponse rapide : les quatre commandes qui donnent le contexte

docker ps -a --no-trunc
docker logs --tail=200 --timestamps mon-conteneur
docker inspect --format '{{json .State}}' mon-conteneur
docker inspect --format 'restart={{.HostConfig.RestartPolicy.Name}} count={{.RestartCount}}' mon-conteneur

Avec Compose, commencez plutôt dans le répertoire du projet :

docker compose ps -a
docker compose logs --tail=200 --timestamps mon-service
docker compose config --quiet

docker compose config --quiet valide la configuration sans afficher sa version résolue. C’est préférable lors d’un premier contrôle, car une sortie complète de configuration ou de docker inspect peut révéler des variables d’environnement, des chemins internes et parfois des secrets. Ne collez pas ces sorties intégrales dans un ticket public.

1. Distinguer « arrêté », « restarting » et « unhealthy »

État observéSignificationPriorité du diagnostic
ExitedLe processus principal du conteneur s’est terminé.Lire le code de sortie et les derniers journaux.
RestartingLe processus s’arrête et la politique de redémarrage le relance.Capturer la première erreur, pas seulement les derniers cycles.
Up … (unhealthy)Le processus tourne, mais les contrôles de santé échouent.Inspecter le HEALTHCHECK et son historique.
CreatedLe conteneur existe mais n’a pas démarré correctement.Contrôler commande, montage, réseau et erreur du moteur.
UpLe processus principal tourne.Vérifier encore le service depuis l’extérieur : « Up » ne garantit pas qu’il répond.

Un point souvent mal compris : le contrôle de santé ajoute un état starting, healthy ou unhealthy, mais il ne remplace pas l’état du processus. Avec Docker Engine seul, une politique de redémarrage s’applique quand le conteneur s’arrête ; elle ne constitue pas, à elle seule, un mécanisme qui redémarre automatiquement tout conteneur devenu unhealthy.

2. Lire l’état sans exposer les variables sensibles

Demandez uniquement les champs nécessaires :

docker inspect --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} error={{printf "%q" .State.Error}} started={{.State.StartedAt}} finished={{.State.FinishedAt}} restarts={{.RestartCount}}' mon-conteneur

Vous cherchez exactement :

  • le code de sortie ;
  • la valeur OOMKilled ;
  • une erreur remontée par le moteur Docker ;
  • la durée entre StartedAt et FinishedAt ;
  • l’augmentation de RestartCount.

Évitez docker inspect mon-conteneur dans un partage public : la configuration complète peut inclure le tableau Env. Un format ciblé réduit à la fois le bruit et le risque de divulgation.

3. Interpréter le code de sortie sans tirer de conclusion trop vite

CodeLecture utileContrôle suivant
0Le processus s’est terminé sans signaler d’erreur.Vérifier s’il devait réellement rester au premier plan.
1 ou autre valeur applicativeL’application a signalé un échec.Lire ses journaux et sa documentation.
125Docker n’a pas pu lancer correctement la commande.Contrôler options, montages et configuration du moteur.
126La commande existe mais ne peut pas être invoquée.Vérifier droits d’exécution, architecture et format du fichier.
127La commande demandée est introuvable.Contrôler ENTRYPOINT, CMD et le contenu de l’image.
137Le processus a reçu un arrêt brutal, souvent associé à SIGKILL.Vérifier OOMKilled, les limites mémoire et les événements de l’hôte.
143Le processus a généralement reçu SIGTERM.Rechercher un arrêt demandé, un délai dépassé ou un déploiement.

Un code 137 ne prouve pas, à lui seul, un manque de mémoire : docker kill envoie aussi SIGKILL par défaut. La valeur OOMKilled, les journaux du noyau et le contexte d’exploitation permettent de départager les hypothèses.

4. Capturer la première erreur de l’application

docker logs --tail=200 --timestamps mon-conteneur
docker compose logs --tail=200 --timestamps mon-service

Les journaux Docker correspondent principalement aux sorties standard et d’erreur du processus du conteneur. Si l’application écrit seulement dans un fichier interne, docker logs peut rester vide. Dans ce cas, vérifiez la documentation de l’image et les volumes de journaux, sans modifier le conteneur au hasard.

Dans une boucle rapide, cherchez la première erreur complète : message de migration, variable absente, fichier illisible, connexion refusée, permission sur un volume ou syntaxe de configuration. Les dizaines de redémarrages suivants répètent souvent la conséquence, pas la cause.

5. Suspendre proprement la boucle pour enquêter

Sur un système en production, évaluez d’abord l’impact et prévoyez une fenêtre d’intervention. Arrêter manuellement le conteneur empêche normalement la politique de redémarrage de le relancer jusqu’au prochain démarrage manuel ou redémarrage du démon Docker :

docker stop mon-conteneur

Si vous devez neutraliser explicitement la politique pendant le diagnostic, Docker permet de la modifier à chaud :

docker update --restart=no mon-conteneur

Notez impérativement la politique précédente avant ce changement et corrigez ensuite le fichier compose.yaml ou la définition d’infrastructure. Une modification manuelle du conteneur ne remplace pas la source de vérité et peut disparaître lors de sa recréation.

6. Vérifier un manque de mémoire sans augmenter la limite au hasard

docker inspect --format 'oom={{.State.OOMKilled}} limit={{.HostConfig.Memory}}' mon-conteneur
docker stats --no-stream mon-conteneur

docker stats ne fournit des mesures utiles que pendant que le conteneur tourne. Si le processus meurt immédiatement, complétez avec les journaux système de l’hôte et l’état OOMKilled.

Avant d’augmenter la mémoire, cherchez une fuite, une taille de lot excessive, un nombre de workers trop élevé ou une limite de cache mal adaptée. Ajouter de la RAM peut seulement retarder le prochain arrêt. À l’inverse, une limite manifestement trop basse doit être ajustée à partir d’une mesure reproductible.

7. Diagnostiquer un HEALTHCHECK en échec

docker inspect --format '{{json .State.Health}}' mon-conteneur

L’historique indique les dernières commandes de contrôle, leurs codes de sortie et leurs messages. Vérifiez quatre points :

  • la commande utilisée existe-t-elle réellement dans l’image ?
  • l’URL, le port ou le socket testé est-il celui du conteneur, et non celui de l’hôte ?
  • le délai de démarrage start_period laisse-t-il le temps à l’application de s’initialiser ?
  • le test est-il sans effet de bord et suffisamment léger ?

Un test comme curl -f http://localhost/ échoue si curl n’est pas installé dans l’image, même lorsque le service fonctionne. Corrigez la sonde ou fournissez un outil adapté ; ne désactivez pas durablement le contrôle uniquement pour obtenir un statut vert.

8. Contrôler la commande, l’image et les volumes

Pour afficher la commande configurée sans dévoiler l’environnement :

docker inspect --format 'image={{.Config.Image}} entrypoint={{json .Config.Entrypoint}} cmd={{json .Config.Cmd}} workdir={{.Config.WorkingDir}}' mon-conteneur
docker inspect --format '{{range .Mounts}}{{println .Type .Source "->" .Destination "rw=" .RW}}{{end}}' mon-conteneur

Les causes classiques comprennent un script absent après une nouvelle image, un fichier sans droit d’exécution, un volume monté sur le mauvais chemin, un répertoire devenu accessible en lecture seule ou une architecture d’image incompatible avec l’hôte.

Pour Compose, validez la syntaxe puis comparez le service défini à celui réellement déployé. Ne lancez pas simplement docker compose restart après avoir modifié des variables ou le fichier Compose : la documentation Docker précise que cette commande ne prend pas en compte les changements de configuration. Une recréation contrôlée est nécessaire.

9. Examiner les dépendances sans créer une attente infinie

Une application peut quitter parce que sa base de données, son broker ou son service d’authentification n’est pas encore prêt. Vérifiez la résolution DNS, le port et l’état de la dépendance depuis le même réseau Docker. Mais évitez une boucle infinie qui masque une mauvaise configuration : utilisez un nombre limité de tentatives, un délai progressif et un message explicite avant l’arrêt.

Dans Compose, un ordre de démarrage n’est pas une preuve de disponibilité fonctionnelle. Si un service doit attendre une base saine, utilisez un contrôle de santé pertinent et une condition adaptée, tout en conservant une logique de reconnexion dans l’application.

10. Corriger la source puis vérifier la recréation

La correction doit vivre dans le dépôt : Dockerfile, compose.yaml, fichier de configuration, secret géré ou code applicatif. Après revue et sauvegarde des données nécessaires :

docker compose config --quiet
docker compose up -d --build mon-service
docker compose ps
docker compose logs --tail=100 --timestamps mon-service

Vérifiez ensuite que le compteur de redémarrages n’augmente plus, que le service répond réellement et que son contrôle de santé devient stable. Un conteneur affiché Up depuis quelques secondes n’est pas encore une validation complète.

Checklist d’un diagnostic reproductible

  1. Identifier le conteneur et l’heure du premier échec.
  2. Capturer les 200 dernières lignes avec leurs horodatages.
  3. Lire Status, ExitCode, OOMKilled, Error et RestartCount.
  4. Noter la politique de redémarrage sans encore la renforcer.
  5. Distinguer un arrêt du processus d’un simple état unhealthy.
  6. Contrôler commande, image, volumes, dépendances et ressources.
  7. Corriger la source de vérité, puis recréer proprement le service.
  8. Vérifier la stabilité, les journaux et le service depuis l’extérieur.

FAQ sur les boucles de redémarrage Docker

Pourquoi restart: always ne résout-il pas la panne ?

Parce qu’il définit seulement ce que Docker doit faire après l’arrêt du conteneur. Si la commande, la configuration ou l’application reste défectueuse, Docker relance la même erreur.

Un conteneur unhealthy redémarre-t-il automatiquement ?

Pas avec la seule politique de redémarrage de Docker Engine. Le processus peut continuer à tourner tout en échouant au contrôle de santé. Un orchestrateur ou un mécanisme supplémentaire peut agir sur cet état, mais il faut d’abord comprendre pourquoi la sonde échoue.

Le code 137 signifie-t-il toujours un OOM ?

Non. Il correspond généralement à un arrêt par SIGKILL, qui peut venir d’un manque de mémoire mais aussi d’une commande d’arrêt forcé. Vérifiez OOMKilled et les événements de l’hôte.

Pourquoi mes nouvelles variables ne sont-elles pas appliquées après docker compose restart ?

Parce qu’un redémarrage réutilise la configuration du conteneur existant. Pour appliquer une modification du fichier Compose ou de l’environnement, il faut recréer le service de façon contrôlée.

Conclusion

Une boucle de redémarrage est un symptôme, pas un diagnostic. Les journaux horodatés, le code de sortie, OOMKilled, le compteur de redémarrages et l’état du contrôle de santé suffisent souvent à réduire rapidement le champ des causes. La politique restart devient alors ce qu’elle doit être : un mécanisme de reprise après une panne ponctuelle, pas un cache-misère.

Si la boucle implique plusieurs services, des volumes persistants ou une base de données à préserver, l’assistance informatique FuretSplick peut aider à établir un diagnostic et un plan de remise en service avant toute recréation risquée.

Sources

Publications similaires