Exercice 08 : Projet Capstone - Stack de Production avec Docker Compose et Multi-Stage Builds
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Rédiger un
docker-compose.ymlmulti-services avec dépendances (depends_on) - ✅ Réduire drastiquement la taille d'une image avec un multi-stage build
- ✅ Configurer des
healthcheckpour fiabiliser les démarrages - ✅ Appliquer les bonnes pratiques de production :
read_only,no-new-privileges, non-root user - ✅ Utiliser des variables d'environnement via un fichier
.env
Durée estimée : 1h
Difficulté : ⭐⭐⭐⭐⭐ (Expert)
Prérequis :
- Docker et Docker Compose v2 installés
- Module Docker Avancé complété (Compose, multi-stage, optimisation, production)
- Avoir complété les exercices 06 et 07 de ce module
📖 Contexte
Vous allez construire et déployer en local une stack de production complète : une API Node.js avec un multi-stage build, une base de données PostgreSQL, et un reverse proxy Nginx. Tout est orchestré via Docker Compose avec les bonnes pratiques production.
📋 Énoncé
Rédigez un docker-compose.yml production-ready pour une stack Nginx + API Node.js + PostgreSQL, en utilisant un multi-stage Dockerfile pour l'API.
🧭 Déroulement de l'exercice
Tâche 1 : Créer l'application Node.js
Créez node-api/ contenant une application Express.js minimale avec une route /health et une route / retournant un JSON. Ajoutez un package.json avec Express comme dépendance.
Indice :
server.jsavecexpress(),.get('/', ...),.listen(3000). Lepackage.jsondoit avoir"start": "node server.js".
Vérification : L'arborescence contient node-api/server.js et node-api/package.json.
Tâche 2 : Écrire le multi-stage Dockerfile
Créez node-api/Dockerfile avec deux stages :
- Stage
builder(node:20-alpine) : installe toutes les dépendances (npm ci) y compris dev - Stage
production(node:20-alpine) : copie uniquementnode_modulesetserver.jsdu builder, crée un utilisateurnode, expose le port 3000
Indice :
FROM node:20-alpine AS builderpour le premier stage,FROM node:20-alpine AS productionpour le second.COPY --from=builder /app/node_modules ./node_modulescopie depuis le stage builder.
Vérification : docker build -t node-api:prod --target production node-api/ réussit. La taille de l'image est inférieure à 100 MB.
Tâche 3 : Configurer Nginx comme reverse proxy
Créez nginx/nginx.conf pour rediriger les requêtes HTTP port 80 vers api:3000. Nginx doit servir de point d'entrée unique.
Indice :
upstream api { server api:3000; }etlocation / { proxy_pass http://api; }. Dans Docker Compose, les services se joignent par leur nom de service.
Vérification : Le fichier nginx/nginx.conf contient proxy_pass vers le service api.
Tâche 4 : Rédiger le docker-compose.yml
Créez docker-compose.yml à la racine du projet avec les 3 services :
db: PostgreSQL 16-alpine, volume nommé, variables d'env via.env, healthcheck surpg_isreadyapi: build depuisnode-api/, dépend dedb(condition :service_healthy), variables d'envnginx: imagenginx:1.25-alpine, montenginx/nginx.conf, expose le port 80, dépend deapi
Indice : La clé
healthchecks'écrit sous le service.depends_on:aveccondition: service_healthyattend que le healthcheck passe avant de démarrer le service dépendant.
Vérification : docker compose config valide la syntaxe sans erreur.
Tâche 5 : Lancer et vérifier la stack
Créez un fichier .env avec les variables PostgreSQL. Lancez la stack avec docker compose up -d, attendez que les services soient healthy, et vérifiez que l'API répond à travers Nginx.
Indice :
docker compose up -d --buildreconstruit les images et démarre.docker compose psmontre l'état.docker compose logs -f apisuit les logs de l'API.
Vérification : curl http://localhost:80/health retourne {"status": "ok"}.
Tâche 6 : Appliquer les contraintes de sécurité production
Ajoutez dans docker-compose.yml pour les services api et nginx :
security_opt: ["no-new-privileges:true"]- Vérifiez que l'API tourne en utilisateur non-root avec
docker compose exec api whoami
Indice :
security_optest une liste YAML sous le service.no-new-privileges:trueempêche un processus d'escalader ses privilèges viasetuid.
Vérification : docker compose exec api whoami retourne node (pas root). docker compose exec nginx whoami retourne nginx.
🗂️ Mini-Projet : Stack complète production-ready
Arborescence finale :
projet-docker-avance/
├── .env
├── docker-compose.yml
├── node-api/
│ ├── server.js
│ ├── package.json
│ └── Dockerfile
└── nginx/
└── nginx.confDéploiement en une commande :
# Lancer toute la stack
docker compose up -d --build
# Vérifier l'état des health checks
docker compose ps
# Test bout en bout
curl http://localhost/
curl http://localhost/health
# Taille de l'image multi-stage
docker images node-api:prod
# Logs
docker compose logs --tail=20Checkpoints de validation :
docker compose configvalide sans erreurdocker compose psaffiche les 3 servicesrunning (healthy)ouUpcurl http://localhost/healthretourne{"status": "ok"}docker images node-api:prod --format "{{.Size}}"- image inférieure à 100 MBdocker compose exec api whoamiretournenode(non-root)docker compose down -vsupprime les conteneurs et volumes proprement