Docker Compose : l’orchestrateur incontournable
Docker Compose facilite grandement le déploiement de différents types de piles d'applications en permettant de définir et de gérer des applications
conteneurisées multi-services à l'aide d'un simple fichier YAML (`docker-compose.yml`).
Un conteneur unique suffit rarement — votre API réclame une base de données, un système de cache, un reverse-proxy, etc.
Compose orchestre tout cela de manière explicite et traçable
1. Pourquoi Docker Compose ?
- Une vision d’ensemble maîtrisée : Un unique fichier source de vérité (docker-compose.yml), versionné dans Git, décrit la stack complète.
Résultat : les environnements de développement, d’intégration continue et de préproduction deviennent reproductibles.
- Déploiement et reproduction instantanés Grâce à ce fichier unique, il suffit d’un docker compose up pour recréer l’environnement décrit — fidèlement, partout.
- Productivité maximale, friction minimale : Fini le « ça marche sur ma machine ». Vos collègues, votre pipeline CI ou un poste fraîchement provisionné peuvent relancer l’intégralité de la stack en quelques dizaines de secondes.
Moins d’attente, moins d’erreurs, plus d’itérations.
1. Les fondamentaux du fichier docker-compose.yml
services:
web:
build: .
ports: ["8000:8000"]
environment:
- DATABASE_URL=postgres://pg:pg@db:5432/app
depends_on: [db]
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
retries: 5
volumes:
pgdata:
- services : Un service dans Docker Compose représente un conteneur logique pour exécuter une application.
- build / image : Vous pouvez soit construire l'image, soit utiliser une image pré-construite.
- Ports : Le mapping entre les ports de l'hôte et ceux du conteneur.
- environment / env_file : Gestion des variables d'environnement, secrets et configurations.
- volumes : Ils garantissent la persistance des données.
- depends_on / healthcheck : Garantir l'ordre d’exécution et vérifier la robustesse des services.
- networks (optionnel) : Séparation du front et du back, ainsi que l’isolation pour le testing.
2. Commandes clés — à connaître par cœur
Récapitulatif : ENTRYPOINT vs CMD
Commandes usuelles
| Action |
Commande V2 |
Ce que ça fait |
| Démarrer la stack |
docker compose up -d |
Build (si besoin) + run en background |
| Arrêt + suppression |
docker compose down -v |
Coupe tout et détruit volumes/réseaux |
| Logs stream |
docker compose logs -f --tail=100 |
Suivre les logs en continu |
| Rebuild ciblé |
docker compose up -d --build web |
Rebuild uniquement le service web |
| Exécuter une commande d'un service |
docker compose exec db psql -U postgres |
Executer la commande psql du service db |
| Lancer un shell |
docker compose exec web /bin/bash |
Ouvre un shell dans un conteneur déjà démarré. |
| Lancer un conteneur éphémère |
docker compose run --rm <service> <cmd> |
Lancer une tâche ad hoc dans un conteneur éphémère (migrations, tests) |
| Charger un container |
docker compose pull |
Forcer la récupération des images distantes |
| Lister les container en fonctionnement |
docker compose ps |
Voir l’état des services/containeurs |
| Redémarre, arrête et demarre le(s) conteneur(s) |
docker compose restart|stop|start <service> |
Il est possible de cibler plusieurs services : docker compose stop web db. |
| Mise à l’échelle |
docker compose up -d --scale web=3 |
3 instances du service web |
Note : `docker compose` (avec un espace) = Compose V2, intégré au binaire Docker. `docker-compose` (avec tiret) = l’ancien binaire Python, encore présent mais voué à disparaître.
3. Bonnes pratiques qui font la différence
-
.env à la racine : pour paramétrer ports, versions d’image, etc.
- Fichiers d’override : (`docker-compose.override.yml`, `docker-compose.prod.yml`) : mêmes définitions, options différentes (debug vs production).
- Profiles : activer/masquer certains services (`docker compose --profile tests up`).
- Santé d’abord : des `healthcheck` explicites évitent que votre app démarre avant Postgres.
- Pas de mot de passe en clair : secrets Docker ou variables d’environnement injectées par votre CI/CD.
- Gardez le build simple : un `Dockerfile` par service, pas de scripts bash géants dans le compose.
4. Limites à connaître (et comment anticiper)
- Pas de haute disponibilité multi-nœuds : Compose orchestre sur une seule machine ; pour le cluster, passez à Docker Swarm ou, plus souvent aujourd’hui, à Kubernetes (Kompose convertit un compose.yml en manifestes K8s pour démarrer la migration).
- Mise à l’échelle basique : le `--scale` lance plusieurs conteneurs, mais sans load-balancer externe ni rolling update fin — OK pour du dev/staging, pas pour 1 000 req/s en prod.
En résumé :
Docker Compose est le tremplin entre le conteneur unique et la vraie architecture micro-services.
Maîtrisez-le dès aujourd’hui : vous gagnerez en fiabilité, en rapidité de mise en route et vous préparerez tranquillement la montée en charge vers le cloud ou Kubernetes.
Explication détaillée des points d'un Docker Compose
Zoom sur chaque brique clé d’un `docker-compose.yml`
1. services – l’unité logique de l'architecture
Chaque composant d'une pile (serveur web, base de données, cache, etc.) est défini comme un service distinct.
Un service peut être construit à partir d'une image Docker existante (par exemple, mysql:5.7 ou wordpress:latest) ou à partir d'un Dockerfile personnalisé.
Une fois le fichier YAML configuré, l'ensemble de l'environnement, qu'il soit composé de deux conteneurs ou plus,
peut être lancé avec une seule commande : docker-compose up.
- Nom clair = contrat clair (`web`, `db`, `redis-cache`, pas `service1`).
- Les conteneurs d’un service partagent l’image , les variables d’environnement, les volumes… et peuvent être scalés (`docker compose up --scale web=3`).
- Isolation contrôlée : par défaut, chaque service reçoit un hostname interne (
<service>) et communique via le réseau virtuel créé par Compose.
2.build / image – construire ou télécharger
3.ports – exposer le bon service, au bon endroit
Syntaxes:
4. `environment` / `env_file` – paramètres, secrets, config
Il est facile de définir des variables d'environnement pour configurer le comportement de chaque service (par exemple, les mots de passe de la base de données ou l'environnement de développement/production), soit directement dans le fichier docker-compose.yml, soit via un fichier .env.
environment:
- APP_ENV=prod
- DATABASE_URL=${DATABASE_URL}
env_file:
- ./.env
- Ordre de priorité : CLI › `environment:` › variables du shell qui lance `docker compose`.
- Substitution `${VAR}` : Compose lit automatiquement un fichier `.env` à la racine si présent.
- Secrets sensibles :
- En dev : variables d’environnement + `.env` non commité (ajouté au `.gitignore`).
- En prod : utilisez les secrets Docker ou un gestionnaire externe (Vault, AWS Secrets Manager) ; Compose v3+ supporte `secrets:`.
- Config immuable : pour des blobs non sensibles (fichiers YAML, nginx.conf), préférez `configs:` (Swarm/K8s) ou un volume read-only.
5. volumes – persistence et partage
La directive volumes permet de garantir la persistance des données (comme les fichiers de base de données ou le code source) au-delà du cycle de vie des conteneurs, ce qui est crucial pour les applications web.
Les volumes peuvent être nommés ou mappés vers des répertoires locaux sur l'hôte.
Types de volumes :
1. Només – gérés par Docker :
volumes:
pgdata: # déclaré en bas du fichier
services:
db:
volumes:
- pgdata:/var/lib/postgresql/data
2. Bind mounts – chemin hôte explicite :
L'indication ./data correspond au nom du dossier local et /data-dans-le-conteneur au nom dossier dans le conteneur.
Ces deux seront liés (mappés).
- ./uploads:/app/uploads:ro
3. Volumes éphémères (`tmpfs:`) – pour le cache, la session, etc.
6. depends_on / healthcheck – ordre de démarrage et robustesse
La directive depends_on permet de s'assurer que certains services (comme la base de données) sont démarrés avant ceux qui en dépendent (comme l'application web), garantissant ainsi un ordre de démarrage correct de la pile.
services:
web:
depends_on:
db:
condition: service_healthy
db:
image: postgres
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 10s
retries: 5
- depends_on v2 accepte désormais condition: service_healthy : le conteneur web attend que Postgres réponde réellement, pas juste qu’il soit lancé.
- healthcheck est exécuté à l’intérieur du conteneur ; retour 0 = healthy, >0 = unhealthy. Compose relance ou marque le service en échec selon la politique restart: (always, on-failure, etc.).
- Limites : depends_on ne gère pas les dépendances cycliques ; pour le service discovery, préférez des solutions comme wait-for-it, ou mieux, laissez votre application gérer la reconnexion.
7. networks – isolation, sécurité, scalabilité
Docker Compose crée automatiquement un réseau par défaut ou peut utiliser des réseaux personnalisés, permettant aux services de communiquer entre eux
de manière sécurisée sans configuration réseau manuelle complexe.
Par exemple, dans une application web utilisant une base de données, le conteneur de l'application
peut se connecter au conteneur de la base de données en utilisant son nom de service (ex : `db`) comme nom d'hôte.
version: "3.8"
services:
app:
build: ./app
depends_on:
- db
environment:
- DATABASE_URL=postgresql://myuser:mypassword@db:5432/mydb
ports:
- "8080:8080" # expose uniquement l'API web
db:
image: postgres:15
environment:
- POSTGRES_USER=myuser
- POSTGRES_PASSWORD=mypassword
- POSTGRES_DB=mydb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
- Par défaut Compose crée un réseau bridge unique. Déclarer plusieurs réseaux permet :
- Isolation (front ↔ back) : la BDD n’est pas accessible depuis l’extérieur du back-end.
- Tests parallèles : lancez plusieurs stacks qui ne se « voient » pas.
networks:
front:
back:
services:
web:
networks: [front, back]
db:
networks: [back]
Alias & DNS: chaque service reçoit un alias = son nom + réseau (db.front, db.back). Vous pouvez en ajouter d’autres :
networks:
back:
aliases: [postgresql]
Comment utiliser les variables d'environnement dans Docker ?
Pour plus de flexibilité et de sécurité, il est préférable d’utiliser des variables d'environnement pour stocker les informations sensibles.
Ainsi, elles ne seront pas présentes dans le fichier compose.yml de Docker.
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
Ensuite, dans un fichier .env, ajoutez le nom de la variable ainsi que son contenu : le mot de passe.
POSTGRES_PASSWORD=supersecret
Ajoutez .env à votre .gitignore pour éviter les fuites d’infos sensibles.
Réutiliser des blocs grâce aux ancres
Les ancres permettent de réutiliser des blocs que l’on répète plusieurs fois dans le fichier compose.yml.
services:
api:
image: nginx:alpine
ports:
- "8080:80"
volumes: *code
worker:
image: alpine:3.20
command: sh -c "ls -la /app && sleep 3600"
volumes: *code
x-volumes:
code: &code
- ./app:/app
- &code crée l’ancre (la “liste de volumes”).
- *code la réutilise dans chaque service.
- ./app:/app est le volume (un bind mount) partagé par api et worker.
Tu peux lancer :
docker compose up
Vérifier la configuration du fichier compose.yml
Docker compose config affiche la configuration “finale” que Docker Compose va réellement utiliser, après avoir :
- fusionné tes fichiers (docker-compose.yml + docker-compose.override.yml + -f docker-compose.prod.yml, etc.)
- appliqué les overrides
- interpolé les variables d’environnement (${VAR}) sauf si tu lui demandes de ne pas le faire
- normalisé/résolu certains chemins/valeurs
C’est l’outil n°1 pour débugger : “Pourquoi ce port est ouvert ? Pourquoi ce volume est monté ? Quelle commande est réellement lancée ?” (Docker Documentation)
Nous allons créer un compose.yml pour l'exemple :
x-volumes:
common: &common_volumes
# 1) Code local monté dans les conteneurs (dev / hot reload)
- type: bind
source: ./api
target: /app
# 2) Cache pip partagé entre services (accélère les rebuilds)
- type: volume
source: pip-cache
target: /root/.cache/pip
# 3) Logs partagés (pratique pour centraliser)
- type: volume
source: app-logs
target: /var/log/myapp
services:
api:
build: ./api
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
ports:
- "8000:8000"
volumes: *common_volumes
environment:
- APP_ENV=dev
worker:
build: ./api
command: python -m worker
volumes: *common_volumes
environment:
- APP_ENV=dev
volumes:
pip-cache:
app-logs:
'docker compose config' donne
services:
api:
build:
context: /home/user-ubuntu/Public/test/api
dockerfile: Dockerfile
command:
- uvicorn
- main:app
- --host
- 0.0.0.0
- --port
- "8000"
- --reload
environment:
APP_ENV: dev
networks:
default: null
ports:
- mode: ingress
target: 8000
published: "8000"
protocol: tcp
volumes:
- type: bind
source: /home/user-ubuntu/Public/test/api
target: /app
- type: volume
source: pip-cache
target: /root/.cache/pip
- type: volume
source: app-logs
target: /var/log/myapp
worker:
build:
context: /home/user-ubuntu/Public/test/api
dockerfile: Dockerfile
command:
- python
- -m
- worker
environment:
APP_ENV: dev
networks:
default: null
volumes:
- type: bind
source: /home/user-ubuntu/Public/test/api
target: /app
- type: volume
source: pip-cache
target: /root/.cache/pip
- type: volume
source: app-logs
target: /var/log/myapp
networks:
default:
name: test_default
volumes:
app-logs:
name: test_app-logs
pip-cache:
name: test_pip-cache
x-volumes:
common:
- source: ./api
target: /app
type: bind
- source: pip-cache
target: /root/.cache/pip
type: volume
- source: app-logs
target: /var/log/myapp
type: volume
Utiliser les profils pour gérer des services conditionnels
x-volumes:
common: &common_volumes
# 1) Code local monté dans les conteneurs (dev / hot reload)
- type: bind
source: ./api
target: /app
# 2) Cache pip partagé entre services (accélère les rebuilds)
- type: volume
source: pip-cache
target: /root/.cache/pip
# 3) Logs partagés (pratique pour centraliser)
- type: volume
source: app-logs
target: /var/log/myapp
services:
api:
build: ./api
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
ports:
- "8000:8000"
volumes: *common_volumes
environment:
- APP_ENV=dev
worker:
build: ./api
command: python -m worker
volumes: *common_volumes
environment:
- APP_ENV=dev
tests:
build: ./api
profiles: ["tests"]
command: pytest -q
volumes: *common_volumes
volumes:
pip-cache:
app-logs:
Sans profile :
docker compose config n’inclut pas le service tests (parce qu’il a profiles: ["tests"]).
Avec --profile tests :
docker compose config inclut le service tests dans la sortie YAML finale.
Les services api et worker restent présents (eux n’ont pas de profile), donc ils restent dans la config.
Donc le résultat attendu
api ✅ présent
worker ✅ présent
tests ✅ présent uniquement avec --profile tests
Une fois corrigé :
docker compose up → tests absent
docker compose --profile tests up → tests présent
Utiliser plusieurs configurations
docker-compose.yml (base commune)
services:
api:
build: ./api
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/app
depends_on:
- db
ports:
- "8000:8000"
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=app
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
docker-compose.override.yml (DEV)
Chargé automatiquement quand on fait docker compose up (si le fichier existe).
services:
api:
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
volumes:
- ./api:/app
environment:
- APP_ENV=dev
lancer en dev :
docker compose up --build
docker-compose.prod.yml (PROD : gunicorn, durci, restart)
services:
api:
image: my-api:1.0.0
command: gunicorn -k uvicorn.workers.UvicornWorker -w 2 -b 0.0.0.0:8000 main:app
environment:
- APP_ENV=prod
restart: unless-stopped
read_only: true
tmpfs:
- /tmp
Lancer en prod (en empilant les fichiers) :
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
La règle importante :
Quand on fait :
docker compose -f A -f B config
➡️ B écrase A quand il y a conflit (ex: command, environment, ports, etc.).
Exemples de Piles d'applications
Docker Compose est utilisé pour déployer une vaste gamme de piles d'applications, allant des environnements de développement classiques aux systèmes plus complexes d'ingénierie des données.
Les sources fournissent de nombreux exemples de piles d'applications web et de services intégrés facilités par Docker Compose, notamment :
| Type de Pile d'Application Web |
Technologies impliquées |
| Piles Web Classiques |
Flask (Python) + MySQL : Application web simple multi-conteneurs. |
|
PHP + MySQL : Serveur web Apache/PHP lié à un conteneur MySQL. |
|
Node.js + MySQL : Application Node.js avec base de données MySQL. |
|
NGINX + Flask + MongoDB/MySQL : Proxy Nginx avec un backend Flask et différentes bases de données. |
|
NGINX + Node.js + Redis : Serveur Node.js avec proxy Nginx et Redis. |
| Applications Complètes |
WordPress + MySQL : Lancement d'un blog WordPress complet avec sa base de données. |
|
ASP.NET + MS-SQL : Application ASP.NET Core avec un serveur MS SQL. |
| Environnements de Développement |
Jupyter Notebook : Création d'un environnement de science des données reproductible. |
|
FastAPI/API de Science des Données : Déploiement d'une API de prédiction basée sur un modèle d'apprentissage automatique. |
| Pipes de Données et DevOps |
Apache Airflow + PostgreSQL : Configuration d'un environnement d'automatisation de pipelines de données. |
|
Prometheus + Grafana : Configuration de base pour la surveillance. |
Exemples de service discovery avec Docker Compose
Service discovery = mécanisme qui permet à un conteneur de trouver un autre conteneur par nom (plutôt que par IP fixe).
Docker Compose crée un réseau interne pour ton projet et fournit un DNS embarqué : quand un conteneur fait nslookup db, Docker résout db en l’adresse IP du conteneur (ou des conteneurs) du service db.
Autrement dit : les services se découvrent par leur service name.
version: '3.9'
services:
db:
image: postgres:16
environment:
POSTGRES_USER: pg
POSTGRES_PASSWORD: pg
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U pg -d app"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
web:
image: curlimages/curl:8
depends_on:
db:
condition: service_healthy
command: sh -c "
echo '🔍 Résolution DNS :' &&
nslookup db &&
echo '\n💬 Ping DB :' &&
ping -c 3 db
"
docker compose up
pg_isready est un petit utilitaire fourni avec PostgreSQL qui teste rapidement si un serveur PostgreSQL est prêt à accepter des connexions TCP/UNIX.
Il ne lance pas une session SQL complète : il vérifie juste la possibilité d’établir une connexion.
healthcheck permet à Docker de marquer db comme healthy quand Postgres est prêt.
depends_on avec service_healthy attend cette santé avant de lancer web.
Scaling + Round-Robin : plusieurs IP pour un nom de service
services:
api:
image: hashicorp/http-echo # tiny server qui renvoie “hello”
command: ["-text", "hello from $(hostname)"]
expose: ["5678"] # pas besoin de publier en dehors du réseau Compose
inspector:
image: busybox:uclibc
command: sh -c "
echo '\n DNS Round-Robin :' &&
for i in $(seq 1 6); do
dig +short api | head -n1
sleep 1
done
"
depends_on:
api:
condition: service_started
docker compose up -d --scale api=3 # ← 3 instances « api »
docker compose logs -f inspector
- docker compose up -d --scale api=3 lance trois conteneurs distincts api_1, api_2, api_3 sur le même réseau de projet.
Chacun reçoit une IP unique sur ce réseau interne.
- Le DNS interne de Docker (accessible depuis les conteneurs) résout le nom de service api en plusieurs enregistrements A — une IP par instance en cours d’exécution.
- Ce n’est pas un load-balancer applicatif. Docker renvoie plusieurs IPs (ou tourne l’ordre), mais le client décide laquelle il contactera.
- Beaucoup de clients résolvent le nom une seule fois puis réutilisent l’IP (caching, keep-alive, résolveurs système).
Dans ces cas, tu n’auras pas de vraie distribution de charge entre les instances.
“Voting App” : le Hello World incontournable du micro-service Docker Compose
La Voting App est un mini-système distribué imaginé par l’équipe Docker pour illustrer, en moins de 200 lignes de YAML et de code Python/Node, tout le cycle d’une appli micro-services :
Explication Voting App
| Rôle |
Tech par défaut |
Mission |
| vote (front) |
Python + Flask (ou Node/React selon variantes) |
Formulaire vote “Cats vs Dogs” |
| redis (queue) |
Redis |
Stocke chaque vote en mémoire (latence < 1 ms) |
| worker |
.NET, Python ou Java |
Défile la queue Redis → écrit en base SQL |
| db |
PostgreSQL |
Persistance définitive des votes |
| result (back-office) |
Node + Express + Socket.io |
Graphique temps réel des scores |
ervices:
vote:
image: dockersamples/examplevotingapp_vote:latest
depends_on:
redis:
condition: service_healthy
ports: ["5000:80"]
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
retries: 3
worker:
image: dockersamples/examplevotingapp_worker:latest
depends_on:
redis:
condition: service_healthy
db:
condition: service_healthy
db:
image: postgres:16
environment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
volumes:
- votes-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
retries: 5
result:
image: dockersamples/examplevotingapp_result:latest
depends_on:
db:
condition: service_healthy
ports: ["5001:80"]
volumes:
votes-data:
Explication du Docker Compose - Voting App
Cette application de vote est un exemple classique d'architecture microservices.
Voici comment elle fonctionne :
Architecture générale
L'application permet aux utilisateurs de voter entre deux options, et de voir les résultats en temps réel.
Elle est composée de 5 services qui communiquent entre eux.
Les services
- Vote (Interface de vote)
Conteneur qui expose l'interface web pour voter
Accessible sur `http://localhost:5000`
Dépend de Redis pour stocker les votes temporairement
Attend que Redis soit "healthy" avant de démarrer
- Redis (Cache/File de messages)
Base de données en mémoire qui sert de file d'attente
Stocke temporairement les votes avant qu'ils soient traités
Le healthcheck vérifie toutes les 5 secondes que Redis répond au ping
Utilise l'image Alpine (version légère)
- Worker (Traitement des votes)
Service en arrière-plan qui traite les votes
Récupère les votes depuis Redis
Les enregistre définitivement dans PostgreSQL
Dépend à la fois de Redis et PostgreSQL (attend que les deux soient healthy)
- DB (Base de données PostgreSQL)
Stocke les résultats finaux des votes de manière persistante
Les données sont sauvegardées dans un volume Docker `votes-data`
Le healthcheck vérifie que PostgreSQL est prêt à accepter des connexions
Configuré avec les identifiants postgres/postgres
- Result (Interface des résultats)
Conteneur qui affiche les résultats en temps réel
Accessible sur `http://localhost:5001`
Lit directement les données depuis PostgreSQL
Attend que la base de données soit healthy
Flux de données
- L'utilisateur vote via l'interface **vote** (port 5000)
- Le vote est stocké temporairement dans **Redis**
- Le **worker** récupère le vote depuis Redis
- Le **worker** l'enregistre dans la base **PostgreSQL**
- L'interface **result** (port 5001) affiche les résultats depuis PostgreSQL
Points techniques importants
C'est une architecture découplée typique où chaque composant a une responsabilité unique et peut être scalé indépendamment.
- Service Discovery natif :
- vote poste son JSON sur redis:6379 sans connaître son IP.
- worker lit redis, écrit sur db:5432.
- Tout repose sur la résolution DNS interne fournie par Compose.
- depends_on avec condition: Les services attendent que leurs dépendances soient vraiment opérationnelles (pas juste démarrées)
- healthchecks: Permettent de vérifier qu'un service est fonctionnel avant que d'autres services ne tentent de s'y connecter
- Volume nommé: `votes-data` persiste les données même si les conteneurs sont supprimés
- Ports mappés: 5000 et 5001 sur l'hôte vers le port 80 dans les conteneurs
# lancement
docker compose up -d # <5 s et c'est en ligne
# logs agrégés
docker compose logs -f --tail=50
# ouvrir les apps
xdg-open http://localhost:5000 # front “Vote”
xdg-open http://localhost:5001 # dashboard “Result”
# simuler une montée en charge
docker compose up -d --scale vote=4 --scale worker=3
# inspection DNS round-robin
docker compose exec vote getent hosts worker
Chaque résolution du nom worker renvoie tour à tour les 3 IP des workers – round-robin automatique, sans LB externe.