Changer le thème

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

Easton editorial illustration: route-map drafting table

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

CommandeRôle
docker-compose up -dDémarrer tous les services en arrière-plan
docker-compose psAfficher l’état d’exécution
docker-compose logs -f apiSuivre les logs de l’API
docker-compose downArrêter et supprimer les conteneurs
docker-compose down -vArrêter et supprimer conteneurs et volumes
docker-compose restart apiRedémarrer le service API
docker-compose build apiReconstruire 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érationApproche traditionnelleApproche Compose
Onboarding nouveau membre4 à 8 heures5 minutes (clone + up)
Changement de projetModifier config, arrêter services, redémarrerChanger de répertoire, lancer un autre compose
NettoyageDésinstallation manuelle, processus résiduelsUne commande pour conteneurs et volumes
Cohérence d’équipeChaque machine peut différerConfig 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. 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. 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. 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. 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. 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 ?
Le Dockerfile définit les étapes de construction d'une image unique ; docker-compose.yml définit comment plusieurs conteneurs collaborent. En bref : Dockerfile construit l'image, Compose gère les conteneurs. Un projet a généralement un Dockerfile (ou plusieurs, un par service) et un docker-compose.yml.
depends_on garantit-il que le service est prêt ?
Non. depends_on ne garantit que l'ordre de démarrage des conteneurs, pas que le service est opérationnel. Le conteneur MySQL peut être lancé alors que le service initialise encore. Deux solutions : ajouter un healthcheck dans docker-compose.yml, ou implémenter une logique de retry dans le code applicatif. Le retry côté application est plus simple et fiable.
Comment accéder aux services de l'hôte depuis un conteneur ?
Utilisez host.docker.internal (Docker Desktop) ou l'IP de l'hôte (Linux). Par exemple, si Redis tourne sur l'hôte, connectez-vous via host.docker.internal:6379 depuis le conteneur. Note : sur Linux, host.docker.internal nécessite une configuration supplémentaire ; une alternative est le mode --network host (au prix de l'isolation réseau).
Où sont stockées les données ? Les supprimer avec le conteneur les efface-t-il ?
Les données configurées via volumes sont stockées dans des volumes gérés par Docker ; supprimer le conteneur ne les efface pas. docker-compose down ne supprime que conteneurs et réseaux, pas les volumes. Pour tout effacer, utilisez docker-compose down -v. En développement, cette commande est fréquente : données incohérentes, on repart de zéro.
Comment gérer les configurations multi-environnements (dev/test/prod) ?
Adoptez la stratégie fichier de base + fichiers d'override. docker-compose.yml contient la configuration commune ; docker-compose.override.yml l'environnement de développement (chargé automatiquement par Compose) ; docker-compose.prod.yml la production. Au démarrage, spécifiez avec -f : docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d.
Que faire si un port est déjà occupé ?
Modifiez le mapping de port côté hôte. Par exemple, MySQL mappe par défaut 3306:3306 ; si le 3306 local est pris, passez à 13006:3306 et connectez-vous via localhost:13006. Ou supprimez le mapping ports pour n'accéder au service que via le réseau interne des conteneurs — plus sécurisé.

11 min de lecture · Publié le: 9 avr. 2026 · Mis à jour le: 27 juil. 2026

Commentaires

Connectez-vous avec GitHub pour laisser un commentaire

Easton BlogEaston Blog