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_onaveccondition: service_healthy- Les restart policies et le redemarrage automatique
Prerequisites
- Savoir écrire un Dockerfile
- Connaitre Docker Compose
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 avantunhealthy(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_onaveccondition: service_healthyordonne le démarrage proprementunless-stoppedest 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