Orchestration multi-services avec Docker Compose : démarrage en un clic de l'environnement local

Le premier après-midi de mon onboarding, j’ai fixé l’écran devant la cinquième fenêtre d’erreur — port MySQL occupé, version Redis incompatible, RabbitMQ qui refuse toute connexion. Un collègue senior a jeté un œil à mon écran et a soupiré : « Tu es là depuis combien de temps ? »
« Depuis 9 h ce matin. »
Quatre heures. Trois bases de données à installer, quatre heures. Et ce n’était que le début : ElasticSearch et MongoDB suivaient.
C’est là que j’ai compris pour la première fois : l’environnement de développement local, c’est un piège. Un gouffre sans fond.
Plus tard, l’équipe est passée à Docker Compose. Un nouveau clone le dépôt, docker-compose up -d, cinq minutes, tous les services tournent. MySQL, Redis, RabbitMQ, API, Web — une commande, propre et net. Changer de projet ? Un autre répertoire, un autre fichier compose. Nettoyer ? docker-compose down -v, volumes inclus, aucune trace.
Cet article partage cette transformation : comment orchestrer plusieurs services avec Docker Compose pour que l’environnement local passe du « cauchemar » à « une seule commande ».
Pourquoi l’orchestration multi-services
Franchement, il y a dix ans, avec une application monolithique, la configuration était simple — installer un JDK, renseigner une chaîne de connexion à la base, et c’était parti. Plus maintenant.
La plupart des projets sont en microservices, ou au minimum en séparation front/back. Un environnement local typique exige au minimum : un front Web, une API backend, MySQL pour les données métier, Redis pour le cache et les sessions, RabbitMQ pour les messages asynchrones. Certains ajoutent ElasticSearch pour la recherche, MongoDB pour les logs.
Et là, les problèmes commencent.
La douleur de l’installation manuelle
Chaque machine installe tout à la main. MySQL : version 5.7 ou 8.0 ? La mauvaise version, et les requêtes SQL ne passent plus. Redis : quel port — 6379 par défaut, mais s’il est déjà pris ? RabbitMQ exige Erlang… J’ai passé une demi-heure sur le tutoriel d’installation de RabbitMQ seul.
Après l’installation, les conflits de versions. Un ancien projet a laissé des données MySQL ; conflit de ports, échec au démarrage. On creuse, et c’est un processus zombie qui traîne.
Le pire, c’est le changement de projet. Vous finissez le projet A, vous passez au B. Le MySQL de A occupe le 3306, B veut aussi le 3306. Modifier la config ou arrêter A. Deux jours plus tard, retour sur A, il faut tout remettre.
Aller-retour sans fin.
Le cauchemar en équipe
« Chez moi ça marche. »
Toute équipe l’a entendu. Le nouveau clone le code, installe, ça ne démarre pas. Pourquoi ? L’ancien projet était en MySQL 5.7, le nouveau a installé la 8.0 ; l’ancien Redis sans mot de passe, le local du nouveau en a un. On modifie la config à plusieurs endroits, toujours bloqué.
Un senior intervient, une demi-journée à deux pour faire tourner l’environnement.
Une journée perdue.
L’approche Compose
L’idée de Docker Compose est simple : empaqueter tous les services en conteneurs, les gérer avec un seul fichier de configuration.
Vous n’avez plus à vous soucier de l’installation, des versions ou des ports. Tout est dans le fichier Compose ; un clic, les services démarrent selon la config. Changer de projet ? Un autre répertoire, un autre compose. Nettoyer ? Une commande supprime conteneurs et volumes.
C’est passer du « monter son PC pièce par pièce » au « acheter un PC clé en main ». Plus besoin de savoir insérer la RAM ou brancher la carte graphique — vous allumez et vous codez.
Configuration centrale de docker-compose.yml
Voici un fichier complet. Supposons quatre services : front Web, API backend, MySQL, Redis.
# docker-compose.yml
version: "3.8" # Version du fichier Compose, 3.8 couvre la plupart des options
services:
# Service Web frontend
web:
build: ./frontend # Construire l'image depuis le répertoire frontend
ports:
- "3000:3000" # Hôte 3000 -> conteneur 3000
depends_on:
- api # Dépend de api, api démarre en premier
environment:
- API_URL=http://api:8080 # URL du backend pour le frontend
# Service API backend
api:
build: ./backend # Construire l'image depuis le répertoire backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis # Dépend de la base et du cache
environment:
- DB_HOST=mysql # Adresse de la base (nom du conteneur)
- DB_PORT=3306
- DB_USER=root
- DB_PASSWORD=dev123 # Mot de passe dev ; en prod, utiliser un fichier .env
- REDIS_HOST=redis
- REDIS_PORT=6379
# Base MySQL
mysql:
image: mysql:8.0 # Image officielle, sans build local
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=dev123
- MYSQL_DATABASE=myapp # Crée la base automatiquement
volumes:
- mysql_data:/var/lib/mysql # Persistance des données
# Cache Redis
redis:
image: redis:7-alpine # Version alpine, plus légère
ports:
- "6379:6379"
volumes:
mysql_data: # Volume pour persister les données MySQL
Champs essentiels
services liste tous les services. Chaque service peut provenir de : build (construction locale), image (image officielle), ou les deux combinés.
ports mappe les ports. Format : "port_hôte:port_conteneur". Web sur 3000, API sur 8080, MySQL sur 3306, Redis sur 6379. Port local occupé ? Changez le port hôte, par exemple "13006:3306", puis connectez-vous via localhost:13006.
depends_on contrôle l’ordre de démarrage. MySQL et Redis d’abord, puis l’API (qui en dépend), enfin le Web (qui dépend de l’API). Attention au piège ci-dessous.
environment définit les variables d’environnement — mots de passe, hôtes, ports. En production, n’écrivez pas les secrets ici ; utilisez .env ou l’injection de variables.
volumes persiste les données. MySQL stocke dans le volume mysql_data : supprimer le conteneur ne supprime pas les données.
Un piège fréquent
Le nom du conteneur est le nom du service. Dans la config ci-dessus, l’API se connecte avec DB_HOST=mysql, pas localhost. Pourquoi ?
Chaque conteneur a son propre réseau. localhost dans le conteneur API pointe vers lui-même, pas vers l’hôte ni vers MySQL. Compose crée un réseau interne ; les services s’atteignent par leur nom. mysql est l’adresse du conteneur MySQL sur ce réseau.
La première fois que j’ai écrit un fichier Compose, j’ai mis localhost:3306 — impossible de joindre la base. Il fallait le nom du conteneur.
Dépendances et ordre de démarrage
depends_on paraît simple : MySQL d’abord, API ensuite. Mais il y a une subtilité.
depends_on ne garantit que l’ordre de démarrage des conteneurs, pas que le service est prêt. Le conteneur MySQL peut être up alors que le service initialise encore — création de la base, chargement de la config, écoute du port. L’API tente de se connecter trop tôt et échoue.
Je l’ai vécu : après docker-compose up, l’API signale immédiatement une erreur de connexion. Dix secondes plus tard, ça passe. MySQL était lancé, mais pas encore prêt.
Solution 1 : healthcheck
Compose permet d’ajouter un healthcheck. Les services dépendants ne démarrent qu’une fois le check réussi.
services:
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s # Vérification toutes les 5 secondes
timeout: 3s # Délai d'expiration
retries: 10 # 10 échecs avant unhealthy
# ... autres options
api:
depends_on:
mysql:
condition: service_healthy # Attendre que MySQL soit healthy
MySQL exécute mysqladmin ping pour vérifier qu’il accepte les connexions. Toutes les 5 secondes, jusqu’à 10 tentatives (50 secondes). L’API ne démarre qu’ensuite.
Efficace, mais chaque service demande sa config de healthcheck. Certaines images officielles (Redis par exemple) n’offrent pas de commande pratique — il faut improviser.
Solution 2 : retry côté application
Plus simple : retry dans le code. Échec de connexion, attendre quelques secondes, réessayer. MySQL met du temps à démarrer, on patiente.
Le backoff exponentiel est courant : 1 s, puis 2 s, puis 4 s… La plupart des bases sont prêtes en moins de 30 secondes.
En Node.js, avec le pool de connexions mysql2 :
const pool = mysql.createPool({
host: 'mysql',
port: 3306,
user: 'root',
password: 'dev123',
database: 'myapp',
waitForConnections: true, // Attendre une connexion disponible
connectionLimit: 10,
queueLimit: 0,
});
En Python, avec la bibliothèque tenacity :
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=2, max=10))
def connect_db():
return mysql.connector.connect(host='mysql', ...)
Quelle approche choisir
Le healthcheck est plus précis — l’API ne démarre que quand la base est vraiment prête. Mais la configuration est plus lourde, une commande par base.
Le retry applicatif est plus léger — quelques lignes de code, sans toucher au compose. Inconvénient : l’API peut afficher des erreurs de connexion au démarrage (sans impact final).
Je préfère le retry applicatif, plus rapide à mettre en place et suffisant dans la majorité des cas. Le healthcheck reste utile pour les services très lents à démarrer.
Stratégie multi-environnements
Dev local, test, production : les configs diffèrent. Exemple : en dev, exposer le port de la base pour le débogage ; en prod, accès uniquement via le réseau interne.
Tout mettre dans un seul fichier oblige à modifier à chaque changement d’environnement — fastidieux et source d’erreurs.
Compose propose : fichier de base + fichiers d’override.
Fichier de base : configuration commune
docker-compose.yml contient ce qui est partagé — définition des services, versions d’images, réseau interne, volumes.
# docker-compose.yml (configuration de base)
version: "3.8"
services:
web:
build: ./frontend
# ports omis, complété par l'override
api:
build: ./backend
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
# ports omis
mysql:
image: mysql:8.0
volumes:
- mysql_data:/var/lib/mysql
# ports omis, inutile en prod
redis:
image: redis:7-alpine
volumes:
mysql_data:
Les ports ne sont pas définis ici : ils varient selon l’environnement.
Fichier d’override développement
docker-compose.override.yml ajoute la config dev — mapping de ports, mots de passe simples, variables de debug.
# docker-compose.override.yml (environnement de développement)
version: "3.8"
services:
web:
ports:
- "3000:3000" # Port exposé pour accès local
api:
ports:
- "8080:8080"
environment:
- DEBUG=true # Mode debug en dev
mysql:
ports:
- "3306:3306" # Base exposée pour connexion locale
environment:
- MYSQL_ROOT_PASSWORD=dev123 # Mot de passe simple en dev
redis:
ports:
- "6379:6379"
Comportement par défaut : docker-compose up fusionne docker-compose.yml et docker-compose.override.yml. Les valeurs de l’override remplacent celles de la base.
En local, un simple docker-compose up suffit pour la config complète de développement.
Fichier d’override production
docker-compose.prod.yml ajoute la config prod — pas de ports exposés, secrets de production, services externes.
# docker-compose.prod.yml (environnement de production)
version: "3.8"
services:
web:
# Pas de port exposé, accès via reverse proxy (nginx)
api:
environment:
- DB_HOST={{DB_HOST}} # Lu depuis les variables d'environnement
- DB_PASSWORD={{DB_PASSWORD}}
mysql:
# Pas de port exposé, accès externe impossible
environment:
- MYSQL_ROOT_PASSWORD={{MYSQL_ROOT_PASSWORD}}
En production, spécifiez les fichiers avec -f :
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Les deux fichiers se superposent ; la config prod remplace la base. Ports de base non exposés, mots de passe injectés par variables.
Injection de variables d’environnement
Les secrets de production ne doivent pas figurer dans les fichiers. Compose lit .env ou les variables système.
# Fichier .env (ne pas committer dans git)
DB_HOST=prod-mysql.internal
DB_PASSWORD=super_secret_password_123
MYSQL_ROOT_PASSWORD=another_secret
Dans la config, syntaxe {{VAR:-default}} :
environment:
- DB_HOST={{DB_HOST:-localhost}} # localhost si DB_HOST absent
- DB_PASSWORD={{DB_PASSWORD:-dev123}}
Ne commitez pas .env ; fournissez .env.example avec des valeurs d’exemple que chacun copie et adapte.
Commandes essentielles en pratique
Configuration prête : démarrage, arrêt, débogage. Ces commandes couvrent l’essentiel du quotidien.
Démarrer tous les services
docker-compose up -d
up lance tous les services. -d = mode détaché (arrière-plan), le terminal reste libre. Sans -d, les logs s’affichent dans le terminal ; Ctrl+C arrête.
Compose télécharge les images (image), construit (build), crée et démarre les conteneurs. Le premier lancement est lent ; les suivants réutilisent les images locales.
Voir l’état des services
docker-compose ps
Liste l’état de chaque conteneur. Exemple de sortie :
NAME COMMAND SERVICE STATUS PORTS
myapp-web-1 "npm start" web running 0.0.0.0:3000->3000/tcp
myapp-api-1 "node index.js" api running 0.0.0.0:8080->8080/tcp
myapp-mysql-1 "mysqld" mysql running 0.0.0.0:3306->3306/tcp
myapp-redis-1 "redis-server" redis running 0.0.0.0:6379->6379/tcp
STATUS à running = normal. exited ou error = échec au démarrage.
Consulter les logs
docker-compose logs -f api
Logs du service API. -f = suivi en continu. Sans -f, affichage des logs existants uniquement.
Sans nom de service, docker-compose logs -f affiche tout — vite illisible si le volume est élevé.
Arrêter et nettoyer
docker-compose down
Arrête les conteneurs, supprime conteneurs et réseau. Les volumes restent — les données MySQL aussi.
Pour un nettoyage complet incluant les volumes :
docker-compose down -v
-v supprime les volumes. Au prochain démarrage, MySQL réinitialise tout. En debug, on l’utilise souvent : données incohérentes, on repart de zéro.
Reconstruire les images
Code modifié, image à reconstruire :
docker-compose build api
Construit uniquement l’image API. Puis redémarrer :
docker-compose up -d api
Ou en une étape, build + redémarrage :
docker-compose up -d --build api
--build force la reconstruction même si l’image existe déjà.
Aide-mémoire des commandes
| Commande | Rôle |
|---|---|
docker-compose up -d | Démarrer tous les services en arrière-plan |
docker-compose ps | Afficher l’état d’exécution |
docker-compose logs -f api | Suivre les logs de l’API |
docker-compose down | Arrêter et supprimer les conteneurs |
docker-compose down -v | Arrêter et supprimer conteneurs et volumes |
docker-compose restart api | Redémarrer le service API |
docker-compose build api | Reconstruire l’image API |
Ces commandes couvrent environ 90 % des opérations quotidiennes. Pour le reste (exec, cp, top), consultez la doc au besoin.
Conclusion
Comparons l’efficacité des deux approches :
| Opération | Approche traditionnelle | Approche Compose |
|---|---|---|
| Onboarding nouveau membre | 4 à 8 heures | 5 minutes (clone + up) |
| Changement de projet | Modifier config, arrêter services, redémarrer | Changer de répertoire, lancer un autre compose |
| Nettoyage | Désinstallation manuelle, processus résiduels | Une commande pour conteneurs et volumes |
| Cohérence d’équipe | Chaque machine peut différer | Config unifiée, environnements identiques |
L’écart est net.
Si vous installez encore les bases à la main, modifiez les configs et chassez les conflits de ports, essayez Docker Compose. Commencez simple — une API + un MySQL. Un docker-compose.yml, lancez. Puis ajoutez Redis, RabbitMQ, le multi-environnement.
Pour généraliser en équipe, commitez docker-compose.yml et docker-compose.override.yml avec un README décrivant le démarrage. Nouveau membre : clone, une commande, environnement prêt.
C’est plus fiable qu’un « document de configuration d’environnement ». La doc vieillit ; le fichier de config, non.
Orchestration multi-services Docker Compose en pratique
Orchestrer Web, API, MySQL et Redis avec Docker Compose pour un démarrage en un clic de l'environnement local
⏱️ Estimated time: 15 min
- 1
Step 1: Créer le fichier docker-compose.yml
Créez le fichier de configuration à la racine du projet :
```yaml
version: "3.8"
services:
web:
build: ./frontend
ports: ["3000:3000"]
depends_on: [api]
api:
build: ./backend
ports: ["8080:8080"]
depends_on: [mysql, redis]
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: dev123
MYSQL_DATABASE: myapp
redis:
image: redis:7-alpine
```
Note : la communication inter-conteneurs utilise le nom du service (ex. DB_HOST=mysql), pas localhost - 2
Step 2: Démarrer tous les services
Dans le répertoire contenant docker-compose.yml, exécutez :
```bash
docker-compose up -d
```
• Au premier lancement, le téléchargement des images prend du temps
• Les lancements suivants réutilisent les images existantes, en quelques secondes
• Sans -d, le terminal affiche les logs en continu - 3
Step 3: Vérifier l'état des services
Contrôlez que tous les conteneurs ont démarré correctement :
```bash
docker-compose ps
```
• STATUS affichant running indique un fonctionnement normal
• En cas d'exited ou error, inspectez avec logs :
```bash
docker-compose logs api
``` - 4
Step 4: Configurer le multi-environnement (optionnel)
Créez docker-compose.override.yml (environnement de développement) :
```yaml
version: "3.8"
services:
mysql:
ports: ["3306:3306"]
```
• Compose fusionne automatiquement le fichier override
• En production, spécifiez avec -f :
```bash
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
``` - 5
Step 5: Nettoyer l'environnement
Arrêter et supprimer tous les conteneurs :
```bash
docker-compose down # conserve les volumes
docker-compose down -v # supprime les volumes (données effacées)
```
• Utilisez down lors d'un changement de projet
• Données corrompues ? Relancez avec down -v
FAQ
Quelle est la différence entre docker-compose.yml et Dockerfile ?
depends_on garantit-il que le service est prêt ?
Comment accéder aux services de l'hôte depuis un conteneur ?
Où sont stockées les données ? Les supprimer avec le conteneur les efface-t-il ?
Comment gérer les configurations multi-environnements (dev/test/prod) ?
Que faire si un port est déjà occupé ?
11 min de lecture · Publié le: 9 avr. 2026 · Mis à jour le: 27 juil. 2026
Guide pratique Docker
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Accélérer les builds Docker : guide pratique du cache pour des builds 10× plus rapides
Maîtrisez le cache par couches Docker, la configuration .dockerignore et l'optimisation du Dockerfile pour passer de 10 minutes à 30 secondes. Exemples complets et montage de cache BuildKit.
Partie 7 sur 38
Suivant
Dépendances Docker Compose : healthcheck pour l'ordre de démarrage des bases de données
Guide détaillé de depends_on et healthcheck dans Docker Compose, avec exemples pratiques pour résoudre les échecs de démarrage dus à une base non prête — modèles complets PostgreSQL et MySQL.
Partie 9 sur 38



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire