Retour au cours

infra / docker

Healthchecks

Leçon 111 exercice

Explication

Ce que vous allez apprendre

  • Comprendre pourquoi un conteneur "en cours d'exécution" n'est pas nécessairement fonctionnel
  • Déclarer un healthcheck dans un Dockerfile avec les bonnes options (interval, timeout, start-period, retries)
  • Comprendre le rôle du start_period pour éviter les faux négatifs au démarrage
  • Écrire un endpoint /health qui vérifie réellement les dépendances critiques, pas juste "ok"
  • Comprendre comment un état "unhealthy" est exploité par depends_on ou un orchestrateur

Dans quel contexte ?

Une API perd sa connexion à la base de données suite à un problème réseau temporaire, mais le processus principal continue de tourner sans planter — il répond simplement des erreurs 500 à toutes les requêtes. Sans healthcheck, docker ps continue d'afficher le conteneur comme normal, masquant complètement l'incident. Avec un endpoint /health qui teste réellement la connexion à la base, Docker détecte le problème en quelques secondes et peut déclencher un remplacement automatique du conteneur défaillant.

"Tourner" n'est pas la même chose que "fonctionner"

Un conteneur peut apparaître comme "en cours d'exécution" (docker ps le montre bien démarré) tout en étant totalement incapable de répondre correctement à une requête — un processus bloqué, une connexion à la base de données perdue, une fuite mémoire qui a rendu l'application inutilisable sans pour autant la faire planter. Le simple fait que le processus principal tourne ne garantit donc rien sur la santé réelle de l'application. C'est précisément le problème que résout le healthcheck.

OptionRôle
--intervalFréquence de vérification
--timeoutDélai max avant d'échouer le check
--start-periodDélai de grâce au démarrage
--retriesÉchecs consécutifs avant de marquer "unhealthy"

Piège fréquent

Un endpoint /health qui se contente de répondre "ok" sans rien vérifier n'a presque aucune valeur : il indique seulement que le serveur web répond, pas que l'application peut réellement fonctionner. Teste toujours au moins une dépendance critique (base de données, cache) dans ton endpoint de santé.

Le principe

Un healthcheck est une commande que Docker exécute à intervalle régulier, à l'intérieur du conteneur, pour vérifier que l'application répond correctement — le plus souvent en interrogeant un point d'accès dédié comme /health. Selon le résultat, Docker qualifie le conteneur de "healthy" (sain) ou "unhealthy" (défaillant), un état visible dans docker ps et interrogeable par un script ou un orchestrateur.

Le rôle de start_period

Beaucoup d'applications ont besoin de quelques secondes après leur lancement avant d'être réellement opérationnelles (chargement de configuration, connexion initiale à une base). Sans délai de grâce, le healthcheck échouerait à tort dès les premières secondes. start_period (ou --start-period) accorde ce délai avant de commencer à compter les échecs, évitant des faux positifs au démarrage.

Ce qu'un bon /health doit réellement vérifier

Un endpoint de santé qui se contente de répondre "ok" sans rien vérifier n'a presque aucune valeur : il indique seulement que le serveur web répond, pas que l'application peut réellement fonctionner. Un bon healthcheck teste les dépendances critiques (une requête simple vers la base de données, par exemple), pour détecter les pannes qui comptent vraiment.

Important à retenir

Un conteneur "unhealthy" ne s'arrête PAS tout seul : le healthcheck n'est qu'un signal, exploité ensuite par depends_on: condition: service_healthy (vu en Compose) ou par un orchestrateur pour décider de remplacer automatiquement le conteneur défaillant.

Commandes & code

Healthchecks

dockerfile
# Healthcheck directement dans le Dockerfile
FROM python:3.12-slim
# ...
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -fsS http://localhost:8000/health || exit 1
# --interval  : fréquence de vérification
# --timeout   : délai max avant de considérer le check comme échoué
# --start-period : délai de grâce au démarrage (l'appli met du temps à être prête)
# --retries   : nombre d'échecs consécutifs avant de marquer le conteneur "unhealthy"
bash
docker ps                       # colonne STATUS affiche "(healthy)" ou "(unhealthy)"
docker inspect --format '{{.State.Health.Status}}' mon-api
docker inspect --format '{{json .State.Health}}' mon-api | python3 -m json.tool   # historique des checks
yaml
# Healthcheck dans docker-compose.yml (souvent préféré au Dockerfile pour rester flexible par environnement)
services:
  api:
    build: .
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
      interval: 15s
      timeout: 3s
      retries: 3
      start_period: 10s

  db:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 3s
      retries: 5
python
# Endpoint /health minimal côté application (FastAPI) : vérifie AUSSI ses dépendances critiques
from fastapi import FastAPI, Response

app = FastAPI()

@app.get("/health")
def health():
    try:
        db.execute("SELECT 1")   # un /health qui ne vérifie rien n'a quasiment aucune valeur
    except Exception:
        return Response(status_code=503)
    return {"status": "ok"}
bash
# Un conteneur "unhealthy" ne s'arrête PAS automatiquement : le healthcheck sert de signal
# pour un orchestrateur (Swarm, Kubernetes) ou pour un "depends_on: condition: service_healthy"
docker events --filter event=health_status         # observe les changements d'état en temps réel

# Combiner avec un restart policy pour une auto-remédiation basique
docker run -d --health-cmd="curl -f http://localhost:8000/health || exit 1" \
  --restart unless-stopped mon-api:1.0

Résumé

  • Un healthcheck se déclare dans le Dockerfile ou dans compose ; start_period évite les faux négatifs au démarrage.
  • docker ps / docker inspect exposent l'état (healthy/unhealthy), consommé par depends_on ou un orchestrateur.
  • Un bon /health vérifie les dépendances critiques (base de données, cache), pas seulement que le process répond.

Exercices pratiques

1 disponible
1

Mission : détecter une API en panne que docker ps déclare pourtant saine

Objectif : Corriger un healthcheck superficiel et régler ses paramètres pour éviter les faux positifs au démarrage.

Contexte

Une API a perdu sa connexion à la base de données mais continue de tourner et répond des erreurs 500. Son endpoint /health actuel se contente de répondre "ok" sans rien vérifier, donc docker ps l'affiche toujours comme saine.

Résoudre l’exercice →