Docker pour les devs - 28 - Healthchecks et restart policies

Comment vérifier que ton conteneur est vivant et le relancer automatiquement quand il crashe.

  1. 01 Docker pour les devs - 00 - Pourquoi Docker change tout
  2. 02 Docker pour les devs - 01 - Containers vs VMs
  3. 03 Docker pour les devs - 02 - L'architecture de Docker
  4. 04 Docker pour les devs - 03 - Docker Desktop, Engine et alternatives
  5. 05 Docker pour les devs - 04 - Écrire un Dockerfile
  6. 06 Docker pour les devs - 05 - Layers et cache
  7. 07 Docker pour les devs - 06 - Le .dockerignore
  8. 08 Docker pour les devs - 07 - Multi-stage builds
  9. 09 Docker pour les devs - 08 - Choisir son image de base
  10. 10 Docker pour les devs - 09 - Docker Compose, les bases
  11. 11 Docker pour les devs - 10 - Docker Compose avance
  12. 12 Docker pour les devs - 11 - Networking Docker, les bases
  13. 13 Docker pour les devs - 12 - Networking Docker avance
  14. 14 Docker pour les devs - 13 - Volumes et persistance
  15. 15 Docker pour les devs - 14 - Variables d'environnement et secrets
  16. 16 Docker pour les devs - 15 - Permissions et utilisateurs
  17. 17 Docker pour les devs - 16 - Docker et monorepo
  18. 18 Docker pour les devs - 17 - Dev vs Prod
  19. 19 Docker pour les devs - 18 - ENTRYPOINT, CMD et scripts d'initialisation
  20. 20 Docker pour les devs - 19 - Debugger ses conteneurs
  21. 21 Docker pour les devs - 20 - Bases de donnees dans Docker
  22. 22 Docker pour les devs - 21 - Sauvegardes et restauration
  23. 23 Docker pour les devs - 22 - Conteneuriser un frontend
  24. 24 Docker pour les devs - 23 - Sécurité des conteneurs
  25. 25 Docker pour les devs - 24 - Optimisation des images
  26. 26 Docker pour les devs - 25 - Builds multi-platform
  27. 27 Docker pour les devs - 26 - Limiter les ressources de tes conteneurs
  28. 28 Docker pour les devs - 27 - Gerer les logs comme un adulte
  29. 29 Docker pour les devs - 28 - Healthchecks et restart policies
  30. 30 Docker pour les devs - 29 - Nettoyer Docker avant qu'il mange ton disque
  31. 31 Docker pour les devs - 30 - Registries et stratégie de tags
  32. 32 Docker pour les devs - 31 - Docker en CI/CD
  33. 33 Docker pour les devs - 32 - Au-dela de Compose
  34. 34 Docker pour les devs - 33 - Glossaire Docker de A a Z

28 - Healthchecks et restart policies

Ce que tu vas apprendre

  • L'instruction HEALTHCHECK dans un Dockerfile
  • Les health checks dans Compose
  • La différence entre curl et un script custom
  • Les paramètres interval, timeout, retries
  • depends_on avec condition: service_healthy
  • Les restart policies et le redemarrage automatique

Prerequisites


Mon API renvoyait des 502 depuis deux heures. Le conteneur tournait. docker ps affichait "Up 14 hours". Tout semblait normal. Sauf que le processus Node.js a l'intérieur avait plante silencieusement. Le conteneur existait, mais l'application etait morte.

Docker sait si un conteneur tourne. Il ne sait pas si l'application a l'intérieur fonctionne. C'est exactement le problème que les healthchecks resolvent.

HEALTHCHECK dans le Dockerfile

L'instruction HEALTHCHECK definit une commande que Docker exécuté a intervalles réguliers pour vérifier que l'application répond :

dockerfileFROM node:20-slim
WORKDIR /app
COPY . .
RUN npm ci --production

HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=10s \
  CMD curl -f http://localhost:3000/health || exit 1

CMD ["node", "server.js"]

Docker exécuté la commande toutes les 30 secondes. Si elle echoue 3 fois de suite, le conteneur passe en état unhealthy.

Les paramètres :

  • --interval : delai entre deux verifications (défaut : 30s)
  • --timeout : temps max pour que la commande reponde (défaut : 30s)
  • --retries : nombre d'échecs consecutifs avant unhealthy (défaut : 3)
  • --start-period : delai de grace au démarrage pendant lequel les échecs ne comptent pas (défaut : 0s)

Le --start-period est souvent oublie. Si ton application met 15 secondes a démarrer, sans cette option les premiers checks echouent et Docker la marque unhealthy avant meme qu'elle soit prete.

curl vs script custom

La méthode classique utilise curl :

dockerfileHEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1

Ca marche, mais ca suppose que curl est installe dans ton image. Les images slim et distroless ne l'ont pas. Tu dois soit l'ajouter (et alourdir ton image), soit utiliser une autre approche.

Alternatives sans curl :

dockerfile# Avec wget (present dans Alpine)
HEALTHCHECK CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

# Avec un script custom Node.js
HEALTHCHECK CMD node healthcheck.js || exit 1

Un script custom te donne plus de contrôle. Tu peux vérifier la connexion a la base de donnees, l'état du cache, ou n'importe quelle condition métier :

javascript// healthcheck.js
const http = require("http");

const req = http.get("http://localhost:3000/health", (res) => {
  process.exit(res.statusCode === 200 ? 0 : 1);
});

req.on("error", () => process.exit(1));
req.setTimeout(3000, () => {
  req.destroy();
  process.exit(1);
});

Pour les applications sans endpoint HTTP (un worker qui traite une queue par exemple), tu peux écrire un fichier timestamp a chaque traitement reussi et vérifier sa fraicheur :

dockerfileHEALTHCHECK --interval=60s CMD \
  test $(find /tmp/heartbeat -mmin -2 | wc -l) -gt 0 || exit 1

Health checks dans Compose

En Compose, tu peux définir le healthcheck directement dans le service :

yamlservices:
  api:
    image: mon-app:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

Ca ecrase le HEALTHCHECK du Dockerfile si les deux existent. Pratique pour adapter le check selon l'environnement sans reconstruire l'image.

depends_on avec condition

Le vrai pouvoir des healthchecks dans Compose, c'est le démarrage ordonne :

yamlservices:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

  api:
    image: mon-app:latest
    depends_on:
      db:
        condition: service_healthy

Sans la condition, depends_on garantit seulement que le conteneur db est démarré. Pas qu'il est pret. PostgreSQL peut mettre quelques secondes a accepter des connexions apres le démarrage du conteneur.

Avec condition: service_healthy, Compose attend que le healthcheck de db passe avant de lancer api. Fini les erreurs "connection refused" au démarrage.

J'utilise ca systématiquement sur paltemps.fr pour toutes les dépendances entre services. Ca supprime les scripts "wait-for-it" bidouilles que je trainais avant.

Restart policies

Un conteneur peut crasher. L'application peut planter. Le OOM killer peut frapper (comme on l'a vu dans l'article sur les ressources). La question est : que fait Docker quand ca arrive ?

Par défaut, rien. Le conteneur s'arrêté et reste arrêté.

Les quatre restart policies :

Policy Comportement
no Ne redemarre jamais (défaut)
always Redemarre toujours, meme apres un arrêt manuel
unless-stopped Redemarre toujours, sauf si arrêté manuellement
on-failure Redemarre seulement si exit code non zero
bashdocker run --restart=unless-stopped mon-app

En Compose :

yamlservices:
  api:
    image: mon-app:latest
    restart: unless-stopped

Mon choix par défaut est unless-stopped. Ca couvre les crashs et les redemarrages du serveur, sans relancer un conteneur que j'ai arrêté volontairement.

always est piège : si tu fais docker stop mon-app et que le serveur reboot, Docker relance le conteneur. Pas toujours ce que tu veux.

on-failure accepte un compteur de tentatives :

bashdocker run --restart=on-failure:5 mon-app

Apres 5 tentatives, Docker arrêté de relancer. Ca évité les boucles infinies quand l'erreur est permanente.

Healthcheck + restart : le combo

Les restart policies se declenchent quand le processus principal du conteneur s'arrêté. Elles ne reagissent pas a un healthcheck en échec.

Pour relancer un conteneur unhealthy, tu as besoin d'un orchestrateur. Docker Swarm le fait. Kubernetes le fait. Docker Compose seul ne le fait pas.

En Compose pur, un conteneur unhealthy reste en fonctionnement. C'est une limitation. L'état unhealthy sert principalement a depends_on et au monitoring externe.

Si tu veux un redemarrage automatique sur healthcheck en échec sans orchestrateur, un outil comme autoheal peut surveiller les conteneurs et les relancer :

yamlservices:
  autoheal:
    image: willfarrell/autoheal
    environment:
      - AUTOHEAL_CONTAINER_LABEL=all
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: always

C'est un bricolage, pas une solution de production. Pour du serieux, regarde les orchestrateurs dans l'article sur ce qui vient apres Compose.

Résumé

  • HEALTHCHECK vérifié que l'application fonctionne, pas juste que le conteneur tourne
  • depends_on avec condition: service_healthy ordonne le démarrage proprement
  • unless-stopped est la restart policy la plus sensee pour la plupart des cas
  • Les restart policies ne reagissent pas aux healthchecks en échec sans orchestrateur
  • Le start_period évité les faux positifs au démarrage

Precedent : 27 - Logs | Suivant : 29 - Nettoyage

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.