🐳 Module Docker
Orchestrer des applications multi-conteneurs
Utilisez les flèches, cliquez ou glissez pour naviguer
1. Comprendre le rôle de Docker Compose
Dans une stack multi-conteneurs
2. Écrire un fichier docker-compose.yml
Services, ports, volumes, variables d'env
3. Configurer depends_on + health checks
Démarrage ordonné des services
4. Utiliser les commandes essentielles
up, down, logs, ps, exec
5. Gérer les variables d'environnement avec .env
Sécurité et configuration externalisée
Rappel du problème
Lancer 3 conteneurs manuellement avec docker run est pénible
Docker Compose : l'outil magique
Un fichier YAML pour tout lancer
Syntaxe docker-compose.yml
Services, ports, environment, volumes, depends_on, networks
Health checks
S'assurer qu'un service est vraiment prêt
Live coding
docker-compose.yml pour backend + PostgreSQL + Redis
Commandes essentielles & .env
up/down/logs/ps/exec, sécurité des mots de passe
Pour une app backend + PostgreSQL + Redis :
docker network create myapp-net
docker run -d \
--name postgres \
--net myapp-net \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16
docker run -d \
--name redis \
--net myapp-net \
redis:7-alpine
docker run -d \
--name backend \
--net myapp-net \
-p 3000:3000 \
-e DATABASE_URL=postgres://... \
myapp-backend
❌ 10+ lignes de commandes, fragiles, non versionnables
À chaque dev, il faut tout retaper — ou sauvegarder un script shell
Un outil pour définir et lancer
des applications multi-conteneurs
en un seul fichier YAML
📄
Déclaratif
Tout dans un fichier
🔄
Reproductible
Même environnement partout
📦
Versionnable
Dans Git avec le code
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
pgdata:
networks:
appnet:
📐 3 sections racines principales : services, volumes, networks
Chaque conteneur = un service dans Compose
image
Image publique ou privée
image: postgres:16
build
Construire depuis un Dockerfile
build: ./app
container_name
Nom personnalisé du conteneur
container_name: mon-backend
restart
Politique de redémarrage
restart: unless-stopped
Mapper les ports du conteneur vers l'hôte
ports:
- "3000:3000" # hôte:conteneur
- "8080:80"
- "5432" # port aléatoire sur l'hôte
1 port = 1 service
Deux services ne peuvent pas partager le même port hôte
Communication interne
Les services se parlent sans exposer de ports
💡 En développement, exposer le port du backend. La BDD et Redis n'ont pas besoin de ports exposés.
Persister les données, partager des fichiers
volumes:
- pgdata:/var/lib/postgresql/data # volume nommé
- ./app:/app # bind mount (dev)
volumes: # section racine
pgdata:
📦 Volume nommé
Persistance gérée par Docker. Idéal pour les bases de données.
📁 Bind mount
Monte un dossier local. Idéal pour le hot-reload en dev.
⚠️ Si vous oubliez la section volumes: racine, Docker crée un volume anonyme !
Configurer les services sans modifier le code
Méthode 1 : directe
environment:
POSTGRES_PASSWORD=secret
NODE_ENV=production
❌ Mot de passe en clair dans le fichier
Méthode 2 : fichier .env
env_file:
- .env
✅ Le .env est dans .gitignore
📌 Les variables sont accessibles dans le conteneur via process.env (Node) ou $_ENV (PHP)
Déclarer les dépendances entre services
services:
backend:
build: ./backend
depends_on:
- db
- redis
db:
image: postgres:16
redis:
image: redis:7-alpine
⚠️ Le piège
depends_on démarre db avant backend, mais ne garantit PAS que PostgreSQL est prêt à accepter des connexions.
💡 Solution : combiner depends_on + health check
Vérifier qu'un service est vraiment prêt
Un health check = une commande exécutée périodiquement
Si elle retourne 0 → ✅ le service est healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U monuser"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
test
Commande de vérification
interval
Toutes les 5s
retries
5 échecs max
start_period
Grâce au démarrage
💡 Pour Redis : redis-cli ping | grep PONG
La combinaison gagnante pour un démarrage ordonné
backend:
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
❌ Sans health check
backend démarre → se connecte à PostgreSQL → erreur !
✅ Avec health check
backend attend que PostgreSQL soit healthy → connexion réussie
Par défaut, Compose crée un réseau pour vous
🔗 Découverte par nom de service
Dans le code du backend, connectez-vous à db:5432 et non localhost:5432
// ❌ Faux — chaque conteneur a son propre localhost
const client = new Client({ host: 'localhost' });
// ✅ Correct — Docker résout le nom du service
const client = new Client({ host: 'db' });
💡 Pour un réseau personnalisé : networks: - appnet sur chaque service
Backend Node/Express + PostgreSQL + Redis
services:
db:
image: postgres:16
container_name: myapp-db
restart: unless-stopped
env_file:
- .env
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
redis:
image: redis:7-alpine
container_name: myapp-redis
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
backend:
build: ./backend
container_name: myapp-backend
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ./backend:/app # hot-reload
volumes:
pgdata:
Variables externalisées, hors du versionnage
📄 .env (dans .gitignore)
POSTGRES_USER=monapp
POSTGRES_PASSWORD=s3cr3t!
POSTGRES_DB=ma_base
DATABASE_URL=postgres://monapp:s3cr3t!@db:5432/ma_base
REDIS_URL=redis://redis:6379
NODE_ENV=development
📄 .env.example (versionné)
POSTGRES_USER=monapp
POSTGRES_PASSWORD=# à remplir
POSTGRES_DB=ma_base
DATABASE_URL=postgres://monapp:???@db:5432/ma_base
REDIS_URL=redis://redis:6379
NODE_ENV=development
✅ .env dans .gitignore | .env.example versionné pour servir de template
Le quotidien avec Docker Compose
docker compose up -d # Lance tous les services en arrière-plan
docker compose down # Arrête et supprime conteneurs + réseau
docker compose logs -f # Suit les logs de tous les services
docker compose ps # Liste les conteneurs et leur état
docker compose exec backend sh # Ouvre un shell dans le conteneur
docker compose logs backend # Logs d'un service spécifique
up -d
Démarrer en arrière-plan
down
Arrêter + nettoyer
logs -f
Logs en temps réel
💡 docker compose down -v supprime aussi les volumes (attention aux données !)
docker compose up -d
Je lance toute ma stack
docker compose logs -f
Je vérifie que tout démarre bien
docker compose ps
Je vérifie l'état des services (healthy ?)
docker compose exec backend sh
J'ouvre un shell pour debugguer
Ctrl+C puis docker compose down
Arrêt propre
docker-compose
❌ Ancienne version (v1)
Installation séparée (pip ou binaire)
Plus maintenu
docker compose
✅ Version moderne (v2)
Intégré à Docker Engine
Plugin officiel
📌 Toujours utiliser docker compose (avec un espace, pas de tiret)
❌ Script de démarrage backend
console.log("Connexion à PostgreSQL...");
const client = new Client({ host: 'db' });
await client.connect(); // 💥 Crash !
// Error: connect ECONNREFUSED
Le conteneur db existe... mais PostgreSQL n'est pas encore prêt !
✅ Solution
# Dans docker-compose.yml
backend:
depends_on:
db:
condition: service_healthy
➕ healthcheck sur db avec pg_isready
❌ L'erreur classique
// Dans le backend
host: 'localhost'
port: 5432
// 💥 Connection refused !
Chaque conteneur a son propre localhost !
✅ La bonne pratique
// Dans le backend
host: 'db'
port: 5432
// ✅ Docker résout le nom du service
Les services communiquent par leur nom de service !
📌 La DATABASE_URL devient : postgres://user:pass@db:5432/mydb
❌ À ne PAS faire
db:
environment:
POSTGRES_PASSWORD=supersecret
Mot de passe en clair, commité dans Git !
✅ La bonne pratique
# docker-compose.yml
db:
env_file:
- .env
# .env (dans .gitignore)
POSTGRES_PASSWORD=supersecret
Le .env est local, .env.example est versionné
📄 Docker Compose remplace les longues commandes docker run
Fichier déclaratif versionnable, reproductible
⚠️ depends_on seul ne suffit pas
Toujours ajouter healthcheck + condition: service_healthy
🔗 Communication par nom de service
Les services se résolvent par leur nom, pas par localhost
🔐 Jamais de secrets en dur
Utiliser env_file et .env dans .gitignore
docker compose up -d
Lance toute la stack
docker compose down
Arrêt + nettoyage
depends_on
+ health check obligatoire
.env
Variables externalisées
📌 docker compose (sans tiret, v2 intégrée)
Commencez par copier un docker-compose.yml existant !
1. Créer un docker-compose.yml pour une app Node + MongoDB
Avec health check sur MongoDB (mongo --eval "db.adminCommand('ping')")
2. Ajouter un service Redis avec dépendance conditionnelle
depends_on + condition: service_healthy
3. Externaliser les variables dans .env
Créer .env et .env.example, utiliser env_file dans le YAML
4. Tester le workflow complet
docker compose up -d, logs -f, ps, exec, down
Docker Compose simplifie radicalement vos stacks multi-conteneurs
Pratiquez sur vos projets existants !
Remplacer vos scripts shell par un docker-compose.yml