TP11 : Docker en production — Healthchecks, Résilience & Volumes
Objectif du TP
- Distinguer un conteneur « qui tourne » d'un conteneur « qui fonctionne » grâce aux healthchecks.
- Configurer des stratégies de déploiement avancées avec rollback automatique en Swarm.
- Sauvegarder et restaurer des volumes Docker.
Rendu attendu
- Notes dans votre carnet répondant aux questions posées.
- Stack Swarm avec healthcheck,
update_configet rollback configurés. - Commandes de backup/restore d'un volume documentées.
Questions d'ouverture
- Qu'est-ce qui différencie un conteneur « Running » d'un conteneur « Healthy » ?
- Où stocker un mot de passe de base de données dans une architecture Docker en production ?
- Que faut-il sauvegarder dans une architecture Docker pour pouvoir la reconstruire entièrement ?
Focus : La résilience en production
Un conteneur Running n'est pas forcément sain. Il peut tourner mais renvoyer des erreurs 500, avoir une connexion DB cassée ou être bloqué dans une boucle infinie.
Les healthchecks permettent à Docker de distinguer « qui tourne » de « qui fonctionne », et de déclencher des actions automatiques en conséquence (restart, rollback).
Mise en pratique
1. Healthchecks
Un healthcheck est une commande que Docker exécute périodiquement à l'intérieur du conteneur pour vérifier qu'il est opérationnel. Si la commande échoue trop souvent, le conteneur passe à l'état unhealthy.
1.1. Dans un Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .
HEALTHCHECK --interval=10s --timeout=3s --retries=3 --start-period=15s \
CMD wget -qO- http://localhost:3000/ || exit 1
CMD ["node", "app.js"]
| Paramètre | Description |
|---|---|
--interval |
Délai entre deux vérifications (défaut : 30s) |
--timeout |
Durée max d'exécution de la commande avant échec |
--retries |
Nombre d'échecs consécutifs avant de passer en unhealthy |
--start-period |
Délai de grâce au démarrage (le conteneur ne compte pas les échecs pendant ce temps) |
Vérifier le statut d'un conteneur :
# La colonne STATUS affiche "(healthy)" ou "(unhealthy)"
docker ps
# Voir le détail du healthcheck
docker inspect --format='{{json .State.Health}}' <container> | jq
1.2. Dans docker-compose.yml
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
app:
build: .
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/"]
interval: 10s
timeout: 3s
retries: 3
start_period: 15s
depends_on:
db:
condition: service_healthy # Attend réellement que db soit opérationnelle
Le
condition: service_healthyrésout le problème évoqué dans le TP06 : on attend maintenant que le service soit réellement prêt, pas juste démarré.
Question : Pourquoi start_period est-il important pour une application Node.js qui doit se connecter à une base de données avant de démarrer ?
1.3. Dans Docker Swarm
En Swarm, le healthcheck est utilisé par le scheduler pour valider qu'un réplica est sain avant de continuer le rolling update. Associé à failure_action: rollback, il garantit qu'une version cassée n'est jamais mise en production.
services:
web:
image: registry.local/my-app:v2
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/"]
interval: 10s
timeout: 3s
retries: 3
start_period: 15s
deploy:
replicas: 3
update_config:
parallelism: 1 # Met à jour 1 réplica à la fois
delay: 15s # Délai entre chaque réplica
order: start-first # Démarre le nouveau avant de stopper l'ancien
failure_action: rollback # Rollback automatique si unhealthy
rollback_config:
parallelism: 1
order: stop-first
Question : Quelle différence entre order: start-first et order: stop-first en termes de disponibilité et de consommation de ressources ?
2. Rollback et stratégies de déploiement
2.1. Rolling update avec rollback automatique
Reprenez la stack du TP08. Vous allez simuler un déploiement raté.
- Construire une image volontairement cassée :
FROM node:20-alpine
WORKDIR /app
# Simule une application qui crash au démarrage
CMD ["sh", "-c", "echo 'Démarrage...' && sleep 3 && exit 1"]
- Déployer la version cassée avec failure_action rollback :
docker service update \
--image <REGISTRY_URL>/mon-image:broken \
--update-parallelism 1 \
--update-delay 10s \
--update-failure-action rollback \
tp8stack_web
- Observer le comportement :
# Regarder les tâches en temps réel
watch docker service ps tp8stack_web
# Voir les logs du service
docker service logs tp8stack_web
Vous devriez voir les tâches passer par Starting → Running → Failed, puis le rollback s'enclencher automatiquement.
- Rollback manuel (si vous n'avez pas configuré
failure_action: rollback) :
Question : Quelle différence entre une image qui « build correctement » et une image réellement « déployable » ? Comment le healthcheck et le rollback répondent-ils à cette question ?
2.2. Stratégie Blue-Green
La stratégie blue-green consiste à avoir deux environnements identiques tournant en parallèle. On bascule le trafic vers la nouvelle version (green) uniquement quand elle est validée. En cas de problème, retour immédiat vers l'ancienne (blue).
flowchart TD
U[Utilisateurs] --> N[Nginx proxy]
N --> B[blue:v1]
N --> G[green:v2<br/>testée mais pas encore exposée]
Exemple avec Docker Compose :
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
blue:
image: registry.local/my-app:v1
green:
image: registry.local/my-app:v2
# nginx.conf - pointer vers "green" pour basculer
upstream backend {
server blue:3000;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
Procédure de bascule :
# 1. Tester green sans l'exposer
docker compose exec green wget -qO- http://localhost:3000
# 2. Modifier nginx.conf : remplacer "blue:3000" par "green:3000"
# 3. Recharger Nginx sans coupure
docker compose exec nginx nginx -s reload
# 4. En cas de problème, revenir à blue dans nginx.conf et recharger
| Stratégie | Temps de bascule | Ressources | Risque |
|---|---|---|---|
| Rolling update | Progressif | Normal | Faible |
| Blue-Green | Instantané | x2 | Très faible |
| Canary | Progressif (% trafic) | Normal | Faible |
Question : Dans quel cas préféreriez-vous une stratégie blue-green plutôt qu'un rolling update ? Donnez un exemple concret.
3. Sauvegarde et restauration de volumes
Les images Docker peuvent être reconstruites ou repullées depuis un registry. Ce qui est irremplaçable, ce sont les données dans les volumes (base de données, uploads utilisateurs, certificats...).
3.1. Qu'est-ce qu'il faut sauvegarder ?
| Élément | Perte si non sauvegardé | Solution |
|---|---|---|
| Volumes (DB, uploads...) | ✅ Données définitivement perdues | Backup régulier |
docker-compose.yml |
✅ Configuration perdue | Git |
.env / secrets |
✅ Configuration sensible perdue | Coffre-fort / vault |
| Images | ❌ Rebuildables ou repullables | Registry |
| Conteneurs | ❌ Éphémères par nature | Pas nécessaire |
3.2. Backup d'un volume
# Créer le dossier de backup
mkdir -p backups
# Sauvegarder le volume "mysql_data"
docker run --rm \
-v mysql_data:/data \
-v $(pwd)/backups:/backup \
alpine \
tar czf /backup/mysql_data_$(date +%Y%m%d_%H%M%S).tar.gz -C /data .
Décomposé :
- --rm : supprime le conteneur temporaire après exécution
- -v mysql_data:/data : monte le volume source en lecture
- -v $(pwd)/backups:/backup : monte le dossier local de destination
- alpine tar czf ... : crée une archive compressée horodatée
3.3. Restauration d'un volume
# Créer un nouveau volume vide
docker volume create mysql_data_restore
# Restaurer l'archive dans le nouveau volume
docker run --rm \
-v mysql_data_restore:/data \
-v $(pwd)/backups:/backup \
alpine \
sh -c "tar xzf /backup/mysql_data_20240101_120000.tar.gz -C /data"
3.4. Exercice complet : backup et disaster recovery
- Lancez une stack avec PostgreSQL et insérez quelques données :
docker compose up -d
docker compose exec db psql -U postgres -c "CREATE TABLE test (id serial, msg text);"
docker compose exec db psql -U postgres -c "INSERT INTO test (msg) VALUES ('Donnée importante');"
-
Sauvegardez le volume.
-
Simulez un incident :
-
Restaurez le volume et relancez la stack.
-
Vérifiez que les données sont présentes :
Question : Pourquoi un docker commit (qui transforme un conteneur en image) n'est-il pas une bonne stratégie de sauvegarde pour les données d'une base de données ?
Conclusion
- Un healthcheck transforme un conteneur « qui tourne » en un conteneur « qui fonctionne » : Docker peut alors prendre des décisions automatiques (restart, rollback).
- Le
failure_action: rollbacken Swarm, couplé aux healthchecks, garantit qu'une version cassée ne remplace jamais une version stable. - Les données dans les volumes sont le seul élément véritablement irremplaçable d'une architecture Docker : sauvegardes régulières obligatoires.
- Le pipeline CI/CD complet (SBOM, scan Trivy, push avec SHA Git) est couvert en TP10.