Introduction
Les healthchecks Docker sont un mécanisme intégré permettant de détecter si l'application d'un conteneur fonctionne réellement, et pas seulement si le conteneur est en cours d'exécution. Un conteneur peut être actif alors que son processus principal est bloqué, surchargé ou coincé dans une boucle de redémarrage. Les healthchecks comblent cette lacune en permettant à Docker, aux orchestrateurs et aux équilibreurs de charge de prendre des décisions plus intelligentes concernant le routage du trafic, les redémarrages et les alertes.
Cet article explique l'architecture sous-jacente des healthchecks Docker, leur intégration avec le cycle de vie des conteneurs, et fournit des exemples pratiques pour les utiliser efficacement dans des configurations Docker autonomes et Docker Compose. Nous couvrirons les composants impliqués, le flux de données du healthcheck au statut, les modèles de conception pour l'écriture des healthchecks et les bonnes pratiques opérationnelles pour la production.
À la fin, vous serez en mesure de mettre en œuvre des healthchecks fiables, à faible surcharge et exploitables. Tous les exemples sont adaptés à Docker Engine 20.10+ et Docker Compose v2, et incluent des commandes que vous pouvez exécuter dans un environnement de test.
Principes fondamentaux des healthchecks Docker
Un healthcheck est une commande exécutée à l'intérieur du conteneur à un intervalle défini. Docker évalue le code de sortie de la commande : 0 signifie sain, 1 signifie malade et 2 est réservé (héritage). Si la commande échoue (code de sortie non nul) de manière répétée, le conteneur est marqué comme unhealthy (malade). Inversement, s'il réussit régulièrement après avoir été malade, le conteneur redevient healthy (sain).
Les healthchecks sont définis dans le Dockerfile avec l'instruction HEALTHCHECK, ou au moment de l'exécution avec l'option docker run --health-cmd, ou dans un fichier Compose sous healthcheck. Si les deux sont présents, la configuration d'exécution remplace la définition du Dockerfile de l'image.
Voici un exemple simple de healthcheck dans un Dockerfile pour un serveur web :
FROM nginx:1.25
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/ || exit 1
Dans cet exemple :
--interval=30s: Docker exécute la vérification toutes les 30 secondes.--timeout=3s: Si la vérification prend plus de 3 secondes, elle est considérée comme un échec.--start-period=5s: Période de grâce au démarrage avant le début des vérifications (importante pour les applications à démarrage lent).--retries=3: Nombre d'échecs consécutifs requis pour marquer le conteneur comme malade.CMD curl -f http://localhost/ || exit 1: La commande réelle.curl -féchoue sur les erreurs HTTP, et|| exit 1garantit un code de sortie non nul.
Pour que les healthchecks fonctionnent, l'image doit inclure l'outil requis (comme curl ou wget). Les images minimales comme alpine n'incluent pas curl ; utilisez wget ou installez un outil, ou écrivez un healthcheck en utilisant le shell ou le binaire de l'application elle-même.
Architecture et flux de données des healthchecks
Les healthchecks impliquent plusieurs composants dans Docker :
- Runtime du conteneur : Exécute la commande du healthcheck à l'intérieur du conteneur en utilisant l'utilisateur et l'environnement par défaut du conteneur.
- Moniteur de santé : Un processus de surveillance par conteneur dans le démon Docker qui planifie et suit les exécutions du healthcheck.
- Stockage du statut : Le statut de santé est stocké dans les métadonnées du conteneur et peut être interrogé via
docker inspect,docker pset l'API Docker. - Système d'événements : Les transitions de statut de santé (par exemple, de
startingàhealthyouunhealthy) génèrent des événements Docker qui peuvent être consommés par des outils externes.
Le flux de données est simple :
- Le démon démarre le moniteur de santé pour chaque conteneur avec un healthcheck défini.
- À l'intervalle configuré, le moniteur exécute la commande du healthcheck à l'intérieur du conteneur.
- Le code de sortie de la commande est capturé.
- En fonction du code de sortie et de la logique de nouvelle tentative, le moniteur met à jour le statut de santé du conteneur.
- Le statut devient visible dans
docker ps,docker inspectet est utilisé par le mode Swarm pour l'orchestration des services.
Par exemple, la sortie suivante de docker inspect montre le statut de santé d'un conteneur en cours d'exécution :
$ docker inspect --format='{{.State.Health.Status}}' my-web
healthy
Vous pouvez également voir les 5 derniers résultats du healthcheck :
$ docker inspect --format='{{json .State.Health}}' my-web | jq
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2024-01-15T10:00:00.123456789Z",
"End": "2024-01-15T10:00:00.234567890Z",
"ExitCode": 0,
"Output": ""
},
...
]
}
Notez que la sortie du healthcheck est limitée aux 5 dernières exécutions pour éviter un gonflement de la mémoire.
Concevoir des healthchecks efficaces
Une erreur courante est de rendre le healthcheck identique à la sonde de vivacité. Les healthchecks doivent répondre à la question : « Ce conteneur peut-il servir le trafic en ce moment ? » et non « Le processus est-il vivant ? »
Principes pour de bons healthchecks
- Utiliser des endpoints de niveau application : Si votre application possède un endpoint
/healthou/readyqui vérifie les dépendances (base de données, cache, etc.), utilisez-le. Il fournit un signal plus précis. - Éviter les vérifications lourdes : Le healthcheck s'exécute fréquemment ; une vérification complexe (par exemple, exécuter une grosse requête) ajoute de la charge et peut provoquer des faux positifs en cas de stress.
- Le rendre rapide : Définissez un
--timeoutraisonnable (par exemple, 5 secondes). Un healthcheck lent peut être tué et marqué comme ayant échoué. - Tenir compte du temps de démarrage : Utilisez
--start-periodpour donner à l'application le temps de s'initialiser avant que les vérifications ne commencent à échouer. - Utiliser les nouvelles tentatives judicieusement : Un petit nombre de tentatives (par exemple, 3) évite le battement dû à des problèmes transitoires.
Commandes et modèles de healthcheck
Pour les services web, curl ou wget sont courants :
HEALTHCHECK CMD curl --fail http://localhost:8080/health || exit 1
Pour les bases de données, vous pouvez utiliser un client natif ou pg_isready :
HEALTHCHECK CMD pg_isready -U postgres || exit 1
Pour les applications sans endpoint réseau, vous pouvez utiliser un script shell qui vérifie un fichier ou l'état du processus.
Exemple : Healthcheck pour une API Node.js
Supposons une application Express exposant /health qui renvoie 200 lorsque toutes les dépendances sont connectées. Le Dockerfile :
FROM node:18-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD node -e "fetch('http://localhost:3000/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"
CMD ["node", "server.js"]
Ici, nous utilisons fetch intégré de Node (Node 18+) pour éviter d'installer curl.
Exemple : Healthcheck dans Docker Compose
Dans un fichier Compose, définissez le healthcheck sous le service :
version: '3.8'
services:
web:
image: my-web:latest
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
Vous pouvez ensuite voir le statut de santé dans docker compose ps :
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
myproject-web-1 "node server.js" web running (healthy) 0.0.0.0:8080->8080/tcp
Bonnes pratiques opérationnelles
Utiliser les healthchecks avec les politiques de redémarrage
Les healthchecks seuls ne redémarrent pas les conteneurs. La politique de redémarrage de Docker (--restart=unless-stopped, etc.) ne tient pas compte du statut de santé ; elle ne redémarre qu'à la sortie. Cependant, certains orchestrateurs (comme Docker Swarm) utilisent le statut de santé pour remplacer les tâches malades. Dans Docker autonome, vous pouvez avoir besoin d'outils externes comme Autoheal ou un système de surveillance pour agir sur les conteneurs malades.
Surveiller et alerter sur le statut de santé
Intégrez les échecs des healthchecks à votre pile de surveillance. Les événements Docker incluent des événements health_status :
$ docker events --filter event=health_status
2024-01-15T10:05:00.123456789Z container health_status: unhealthy 1234... (image=my-web:latest, name=my-web)
Vous pouvez capturer ces événements avec un outil comme docker-events pour déclencher des alertes.
Éviter les pièges
- Outil de healthcheck manquant : Utilisez des images avec curl/wget ou incluez le vôtre.
- Intervalles trop agressifs : Un intervalle d'une seconde augmente la surcharge et peut affecter les performances de l'application. Restez entre 10 et 60 secondes en production.
- Ne pas définir start_period : Les applications à initialisation lente (par exemple, Java) seront marquées malades prématurément.
- Healthcheck qui ne vérifie que le processus : Cela rate les défaillances au niveau de l'application. Préférez les vérifications basées sur des endpoints.
- Inondation des logs : Les commandes du healthcheck peuvent produire une sortie ; redirigez la sortie vers
/dev/nullou utilisez le mode silencieux.
Pièges courants et comment les éviter
Piège 1 : La commande du healthcheck sort avec 0 en cas d'échec
Certaines commandes renvoient 0 même lorsque le service est cassé. Par exemple, curl http://localhost/health sans --fail renvoie 0 pour les erreurs HTTP 500.
Pourquoi cela arrive : Ne pas comprendre les codes de sortie ; utiliser des outils qui ne reflètent pas les erreurs HTTP.
Comment éviter : Utilisez toujours curl --fail ou wget --spider --server-response et vérifiez les critères de succès appropriés. Sinon, analysez le corps de la réponse et sortez avec un code non nul si le contenu attendu est absent.
Piège 2 : Le healthcheck hérite des variables d'environnement mais pas du shell
Si vous utilisez un script shell, assurez-vous que l'image dispose d'un shell. Certaines images minimales (comme distroless) n'ont pas de shell, vous devez donc utiliser une forme exec avec un binaire.
Pourquoi cela arrive : Le healthcheck utilise /bin/sh -c par défaut si vous utilisez la forme shell, mais seulement si un shell existe.
Comment éviter : Utilisez la forme exec (tableau JSON) lorsque c'est possible, ou utilisez une construction multi-étapes pour inclure les outils nécessaires au healthcheck uniquement dans une image de débogage.
Piège 3 : Battement du statut de santé dû à des problèmes réseau transitoires
Si l'endpoint du healthcheck échoue de manière intermittente à cause de la gigue du réseau, le conteneur peut basculer entre sain et malade.
Pourquoi cela arrive : Trop peu de tentatives ou un délai d'attente trop court.
Comment éviter : Augmentez les tentatives à 5 et le délai d'attente à 10 secondes, et assurez-vous que l'endpoint est localisé (par exemple, localhost) pour éviter la dépendance au réseau externe.
Piège 4 : Négliger la sécurité dans les commandes de healthcheck
Les healthchecks s'exécutent à l'intérieur du conteneur avec le même utilisateur et peuvent accéder à des fichiers sensibles. Un healthcheck malveillant pourrait exfiltrer des données.
Pourquoi cela arrive : Copier-coller des extraits de healthcheck provenant de sources non fiables.
Comment éviter : Examinez les commandes de healthcheck comme tout autre code, suivez le principe du moindre privilège et évitez d'inclure des identifiants dans la commande.
Healthchecks dans l'orchestration : Docker Swarm et Kubernetes
Bien que cet article se concentre sur Docker autonome et Compose, les healthchecks sont fondamentaux dans l'orchestration.
Docker Swarm
En mode Swarm, les services avec healthchecks utilisent le statut de santé pour déterminer l'état de préparation des tâches. Si une tâche devient malade, Swarm l'arrêtera et la redémarrera (sous réserve de --restart-condition). La update-config du service peut spécifier --monitor durée pour attendre la santé avant de déclarer une mise à jour réussie.
Kubernetes
Kubernetes a ses propres sondes de vivacité et de préparation, qui sont similaires mais distinctes. Lors de la migration vers Kubernetes, vous devez traduire les healthchecks Docker en sondes Kubernetes. Le healthcheck Docker est souvent utilisé comme solution de repli lorsqu'aucune sonde Kubernetes n'est définie, mais il est préférable de définir explicitement les sondes dans la spécification du pod.
Vérification et diagnostics
Après avoir configuré les healthchecks, vérifiez-les dans un environnement contrôlé.
- Vérifier le statut de santé en temps réel :
docker ps --format "table {{.Names}}\t{{.Status}}"
- Inspecter les détails de santé :
docker inspect --format='{{json .State.Health}}' my-container | jq
- Voir les journaux du healthcheck dans le conteneur :
docker logs my-container 2>&1 | grep -i health
La sortie peut inclure la sortie de la commande du healthcheck si elle n'est pas supprimée.
- Simuler une défaillance : Cassez intentionnellement l'endpoint du healthcheck (par exemple, arrêtez une dépendance) et observez le statut passer à
unhealthyaprès les tentatives. Ensuite, restaurez et confirmez qu'il revient àhealthy.
Exemple :
$ docker compose exec web sh -c "kill 1" # simuler un crash, mais vérifier la santé avant de tuer
$ docker inspect --format='{{.State.Health.Status}}' web
unhealthy
Liste de contrôle des opérations
Utilisez cette liste de contrôle lors de la mise en œuvre des healthchecks Docker en production :
- [ ] Définir un endpoint de santé clair qui vérifie toutes les dépendances critiques sans effets secondaires.
- [ ] Définir des intervalles appropriés : 30 secondes est un bon point de départ ; ajustez en fonction de la dynamique de l'application.
- [ ] Définir start_period au moins au temps de démarrage maximal attendu.
- [ ] Surveiller les échecs des healthchecks via la journalisation et les événements.
- [ ] Tester le comportement en cas d'échec dans un environnement de préproduction.
- [ ] S'assurer que la commande du healthcheck est sécurisée et ne fuit pas de secrets.
- [ ] Documenter les étapes de récupération pour les conteneurs malades.
Conclusion
Les healthchecks Docker sont un outil essentiel pour garantir que les applications conteneurisées sont réellement saines et prêtes à servir le trafic. En comprenant l'architecture et en suivant les bonnes pratiques, vous pouvez construire des systèmes plus résilients. Commencez par un healthcheck simple, validez-le soigneusement et itérez en fonction des observations du monde réel.
Prochaine étape : choisissez un conteneur dans votre environnement, mettez en œuvre un healthcheck en utilisant les modèles de cet article, et observez son comportement pendant une semaine avant de le déployer sur tous les services. N'oubliez pas de revoir la configuration tous les trimestres pour l'aligner sur les changements de l'application.