Aller au contenu
Conteneurs & Orchestration high

Docker Compose : orchestrer vos conteneurs multi-services

27 min de lecture

Ce guide vous apprend à déployer une application multi-conteneurs avec Docker Compose en 15 minutes. Vous saurez écrire un fichier docker-compose.yml, faire communiquer vos services (application web + base de données), persister vos données avec des volumes et gérer vos secrets. Prérequis : Docker installé. Résultat : une stack WordPress + MySQL fonctionnelle que vous pourrez adapter à vos projets.

  • Architecture services/réseaux/volumes : comprendre les 3 concepts fondamentaux de Docker Compose et comment ils interagissent
  • Écrire un docker-compose.yml multi-services : créer une stack WordPress + MySQL complète avec dépendances, volumes et variables
  • Orchestrer avec les commandes CLI : maîtriser up, down, logs, exec, config et scale pour gérer vos environnements
  • Gérer les configurations multi-environnements : utiliser fichiers .env, overrides et profils pour dev/staging/prod
  • Résoudre les problèmes courants : diagnostiquer erreurs de connexion, ports occupés, volumes perdus et dépendances non prêtes
  • Connaître les limites vs orchestrateurs : identifier quand migrer vers Docker Swarm ou Kubernetes pour la production

Docker Compose est un outil qui permet de définir et lancer plusieurs conteneurs Docker en une seule commande. Au lieu d’exécuter plusieurs docker run avec des options complexes, vous décrivez votre architecture dans un fichier YAML et lancez tout avec docker compose up.

Analogie : Imaginez un chef d’orchestre. Chaque musicien (conteneur) joue sa partition, mais c’est le chef (Compose) qui coordonne l’ensemble : qui démarre quand, qui communique avec qui, où sont stockées les partitions (données).

Pour mieux comprendre l’intérêt de Docker Compose, comparons deux approches pour déployer la même application (WordPress + MySQL).

Sans Docker Compose, vous devez exécuter manuellement chaque commande, dans le bon ordre, en vous souvenant de toutes les options. Si vous faites une erreur, vous devez tout recommencer. Et si un collègue veut reproduire votre environnement, vous devez lui transmettre toutes ces commandes.

Avec Docker Compose, tout est décrit dans un fichier versionnable. N’importe qui peut cloner votre repo et lancer docker compose up pour obtenir exactement le même environnement.

Sans ComposeAvec Compose
docker network create mynetUn seul fichier docker-compose.yml
docker run -d --name db --network mynet -e MYSQL_ROOT_PASSWORD=secret mysqlUne seule commande docker compose up
docker run -d --name wp --network mynet -e WORDPRESS_DB_HOST=db -p 8080:80 wordpressTout est versionné et reproductible
3 commandes à retenir et exécuter dans l’ordreArchitecture documentée dans le code

Docker Compose excelle dans les environnements où vous contrôlez une seule machine et où la haute disponibilité n’est pas critique. C’est l’outil idéal pour les développeurs qui veulent reproduire un environnement de production localement, ou pour les équipes QA qui ont besoin d’environnements de test isolés.

En revanche, dès que vous avez besoin de répartir la charge sur plusieurs serveurs, de gérer des pannes automatiquement, ou de déployer en production avec zéro downtime, Compose montre ses limites. Dans ces cas, tournez-vous vers Kubernetes ou Docker Swarm.

✅ Utilisez Compose pour❌ N’utilisez pas Compose pour
Développement local multi-servicesProduction haute disponibilité
Tests d’intégrationDéploiement sur plusieurs serveurs
Démos et POCScaling automatique
CI/CD (environnements de test)Orchestration complexe → utilisez Kubernetes

Vérifiez que Docker est installé et que Compose est disponible :

Fenêtre de terminal
docker --version
docker compose version

Résultat attendu : deux numéros de version s’affichent. Si docker compose ne fonctionne pas, vous avez peut-être une ancienne version de Docker.

Si docker compose version échoue, installez le plugin :

Fenêtre de terminal
# Ubuntu/Debian
sudo apt update
sudo apt install docker-compose-plugin
# Vérification
docker compose version

Validation : la commande doit afficher quelque chose comme Docker Compose version v2.x.x.

Avant d’écrire votre premier fichier, comprenez ces trois concepts fondamentaux :

Un service représente un conteneur avec toute sa configuration. C’est l’unité de base dans Docker Compose. Dans notre exemple, nous aurons deux services : wordpress (l’application web) et mysql (la base de données).

Pensez à un service comme une « recette » pour créer un conteneur. Cette recette spécifie quelle image utiliser, quelles variables d’environnement injecter, quels ports ouvrir, etc. Quand vous lancez docker compose up, Compose lit ces recettes et crée les conteneurs correspondants.

Chaque service peut définir :

  • L’image Docker à utiliser (depuis Docker Hub ou un registre privé)
  • Les ports à exposer vers l’extérieur ou entre services
  • Les variables d’environnement pour configurer l’application
  • Les volumes à monter pour persister ou partager des données
  • Les dépendances vers d’autres services (qui doit démarrer en premier)
  • Les ressources (limites CPU, mémoire) et les politiques de redémarrage

Un réseau Docker est un espace de communication isolé où les conteneurs peuvent s’échanger des données. C’est comme un réseau local virtuel : les conteneurs connectés au même réseau peuvent se « voir », tandis que ceux sur des réseaux différents sont isolés.

La magie de Docker Compose, c’est la résolution DNS automatique. Dans un réseau Compose, chaque service est accessible par son nom. Le service wordpress peut contacter la base de données simplement en utilisant mysql comme nom d’hôte — pas besoin de connaître son adresse IP.

Docker Compose crée automatiquement un réseau par défaut nommé <dossier>_default. Tous les services de votre fichier y sont connectés, ce qui signifie qu’ils peuvent communiquer entre eux sans configuration supplémentaire. Vous pouvez aussi créer des réseaux personnalisés pour isoler certains services (par exemple, séparer le frontend du backend).

Les conteneurs sont éphémères par nature : quand vous supprimez un conteneur, tout ce qu’il contenait disparaît. C’est problématique pour une base de données — vous ne voulez pas perdre vos données à chaque redémarrage !

Un volume résout ce problème en stockant les données en dehors du conteneur, sur le système de fichiers de l’hôte. Même si le conteneur est supprimé et recréé, les données du volume sont préservées.

Docker propose trois types de stockage, chacun adapté à un usage différent :

TypeUsageExemple
Volume nomméDonnées persistantes (BDD, fichiers)db_data:/var/lib/mysql
Bind mountCode source en développement./src:/app
tmpfsDonnées temporaires en RAMtmpfs:/tmp

Pour illustrer ces concepts, nous allons déployer une stack simple : **WordPress

  • MySQL**. Suivez ces étapes pour créer votre fichier docker-compose.yml et lancer les conteneurs.

Architecture Docker Compose : WordPress et MySQL connectés via un réseau interne, avec un volume pour la persistance des données

  1. Créez un dossier pour votre projet

    Fenêtre de terminal
    mkdir ~/mon-wordpress && cd ~/mon-wordpress
  2. Créez le fichier docker-compose.yml

    Fenêtre de terminal
    touch docker-compose.yml
  3. Ajoutez la configuration suivante

    services:
    wordpress:
    image: wordpress:latest
    ports:
    - "8080:80"
    environment:
    WORDPRESS_DB_HOST: mysql
    WORDPRESS_DB_USER: wordpress
    WORDPRESS_DB_PASSWORD: wordpress_password
    WORDPRESS_DB_NAME: wordpress
    depends_on:
    - mysql
    restart: unless-stopped
    mysql:
    image: mysql:8.0
    environment:
    MYSQL_DATABASE: wordpress
    MYSQL_USER: wordpress
    MYSQL_PASSWORD: wordpress_password
    MYSQL_ROOT_PASSWORD: root_secret_password
    volumes:
    - db_data:/var/lib/mysql
    restart: unless-stopped
    volumes:
    db_data:

    Explications ligne par ligne :

    • services: — définit les conteneurs à créer
    • image: wordpress:latest — utilise l’image officielle WordPress
    • ports: "8080:80" — expose le port 80 du conteneur sur le port 8080 de votre machine
    • environment: — variables d’environnement passées au conteneur
    • WORDPRESS_DB_HOST: mysql — WordPress contacte MySQL via son nom de service
    • depends_on: — WordPress attend que MySQL démarre (mais pas qu’il soit prêt !)
    • volumes: db_data:/var/lib/mysql — les données MySQL sont persistées
    • restart: unless-stopped — redémarre automatiquement sauf arrêt manuel

Lorsque vous exécutez docker compose up, Docker Compose orchestre l’ensemble du processus de création de l’infrastructure. Voici ce qui se passe en coulisses :

Workflow Docker Compose : étapes d'exécution de docker compose up

  1. Lancez la stack

    Fenêtre de terminal
    docker compose up -d

    L’option -d (detached) lance les conteneurs en arrière-plan.

    Résultat attendu :

    [+] Running 3/3
    ✔ Network mon-wordpress_default Created
    ✔ Container mon-wordpress-mysql-1 Started
    ✔ Container mon-wordpress-wordpress-1 Started
  2. Vérifiez que les conteneurs tournent

    Fenêtre de terminal
    docker compose ps

    Résultat attendu :

    NAME STATUS PORTS
    mon-wordpress-mysql-1 Up 30 seconds 3306/tcp, 33060/tcp
    mon-wordpress-wordpress-1 Up 30 seconds 0.0.0.0:8080->80/tcp
  3. Testez l’application

    Ouvrez votre navigateur à l’adresse : http://localhost:8080

    Vous devriez voir l’écran d’installation de WordPress.

  4. Consultez les logs si quelque chose ne fonctionne pas

    Fenêtre de terminal
    docker compose logs
    # Ou pour un service spécifique :
    docker compose logs mysql

Mettre des mots de passe en clair dans docker-compose.yml pose deux problèmes : d’abord, c’est un risque de sécurité si vous versionnez ce fichier (les secrets se retrouvent dans l’historique Git). Ensuite, c’est peu pratique si vous avez des environnements différents (dev, staging, prod) avec des credentials différents.

La solution : externaliser vos variables dans un fichier .env. Docker Compose lit automatiquement ce fichier s’il existe dans le même dossier que votre docker-compose.yml, et remplace les variables ${...} par leurs valeurs.

Fenêtre de terminal
# Fichier .env (même dossier que docker-compose.yml)
MYSQL_ROOT_PASSWORD=super_secret_root
MYSQL_PASSWORD=wordpress_password
WORDPRESS_DB_PASSWORD=wordpress_password

Puis référencez-les dans votre Compose :

services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}

Les fichiers .env sont pratiques, mais ils ont une limite : les variables d’environnement sont visibles dans les logs, les processus, et via docker inspect. Pour des secrets vraiment sensibles (mots de passe de production, clés API), Docker propose un mécanisme plus sécurisé : les secrets.

Avec les secrets, le contenu sensible est stocké dans un fichier séparé et monté en lecture seule dans le conteneur, à l’emplacement /run/secrets/<nom>. L’application lit le fichier au lieu d’une variable d’environnement. C’est plus sûr car le secret n’apparaît jamais dans les variables d’environnement ni dans les logs.

services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
secrets:
- mysql_root_password
secrets:
mysql_root_password:
file: ./secrets/mysql_root_password.txt

Le mot de passe est lu depuis le fichier, jamais exposé dans les variables d’environnement ou les logs.

Vous avez peut-être remarqué le depends_on dans notre configuration WordPress. Cette directive garantit que MySQL démarre avant WordPress. Mais attention : « démarré » ne signifie pas « prêt » !

Quand Docker lance le conteneur MySQL, il considère le service comme « démarré » dès que le processus MySQL est lancé. Mais MySQL a besoin de quelques secondes pour initialiser ses bases de données et ouvrir son port réseau. Si WordPress tente de se connecter pendant cette initialisation, il échouera avec « Error establishing database connection ».

La solution : les healthchecks. Un healthcheck est une commande que Docker exécute régulièrement pour vérifier si le service est vraiment opérationnel. Avec condition: service_healthy, le service dépendant attend que le healthcheck soit positif avant de démarrer.

services:
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# ... reste de la config
wordpress:
image: wordpress:latest
depends_on:
mysql:
condition: service_healthy
# ... reste de la config

Avec condition: service_healthy, WordPress attendra que MySQL réponde au ping avant de démarrer.

Jusqu’ici, nous avons utilisé des images publiques (wordpress:latest, mysql:8.0). Mais dans un projet réel, vous aurez souvent votre propre code à conteneuriser. Plutôt que de builder l’image manuellement avec docker build, puis de la référencer dans Compose, vous pouvez demander à Compose de la construire automatiquement.

La directive build remplace image et indique où trouver le Dockerfile :

services:
api:
build:
context: ./backend # Dossier contenant le code et le Dockerfile
dockerfile: Dockerfile # Nom du Dockerfile (optionnel si c'est le nom par défaut)
ports:
- "3000:3000"

Quand vous lancez docker compose up --build, Compose construit d’abord l’image à partir du Dockerfile, puis lance le conteneur. C’est particulièrement pratique en développement : modifiez votre code, relancez avec --build, et vous avez une nouvelle image.

L’un des principaux avantages de Docker Compose est sa capacité à scaler horizontalement un service en créant plusieurs réplicas (copies identiques) du même conteneur. C’est utile pour répartir la charge, tester la montée en charge, ou simuler un environnement distribué.

Fenêtre de terminal
# Lancer 3 instances du service web
docker compose up -d --scale web=3
# Vérifier les conteneurs créés
docker compose ps

Résultat attendu :

NAME STATUS PORTS
mon-projet-web-1 Up 10 seconds 0.0.0.0:8080->80/tcp
mon-projet-web-2 Up 10 seconds 0.0.0.0:8081->80/tcp
mon-projet-web-3 Up 10 seconds 0.0.0.0:8082->80/tcp

Vous pouvez aussi définir le nombre de réplicas directement dans le fichier Compose (syntaxe Swarm, compatible avec Compose v3+) :

services:
web:
image: nginx:latest
deploy:
replicas: 3

Limitations du scaling avec Compose :

  • Pas de vrai load balancer intégré (il faut un reverse proxy)
  • Pas de répartition intelligente de la charge
  • Pas de health-based scaling (augmenter/réduire selon la charge CPU)

Pour ces besoins avancés, tournez-vous vers Docker Swarm ou Kubernetes.

Les restart policies définissent le comportement d’un conteneur en cas d’arrêt (crash, arrêt manuel, reboot serveur). C’est crucial pour la résilience de vos applications.

Docker Compose propose 4 politiques, chacune adaptée à un cas d’usage spécifique :

PolitiqueComportementUsage recommandé
noNe jamais redémarrer automatiquement (par défaut)Services à usage ponctuel, jobs batch
alwaysRedémarre toujours, même après arrêt manuel ou rebootServices critiques (BDD, API)
unless-stoppedRedémarre sauf si arrêté manuellement (docker compose stop)Services persistants en production
on-failureRedémarre uniquement si le conteneur a crashé (exit code ≠ 0)Services instables, diagnostics

Exemple de configuration :

services:
db:
image: postgres:15
restart: always # BDD toujours disponible
api:
image: mon-api:latest
restart: unless-stopped # Ne redémarre pas si arrêté volontairement
worker:
image: worker:latest
restart: on-failure # Redémarre uniquement en cas de crash
batch:
image: batch-job:latest
restart: no # Job unique, pas de redémarrage

Vérifier l’historique de redémarrage :

Fenêtre de terminal
docker inspect <container_id> | grep -A 5 RestartCount

Les profils permettent de définir des services optionnels qui ne démarrent que si explicitement demandés. C’est particulièrement utile pour :

  • Les outils de debug (phpmyadmin, pgadmin, adminer)
  • Les services de développement (hot-reload, linters)
  • Les environnements de test (mocks, stubs)

Exemple avec profils debug :

services:
db:
image: postgres:15
# Toujours démarré (pas de profil)
api:
image: mon-api:latest
# Toujours démarré
pgadmin:
image: dpage/pgadmin4
profiles: ["debug"] # Démarré uniquement avec --profile debug
ports:
- "5050:80"
prometheus:
image: prom/prometheus
profiles: ["monitoring"] # Démarré uniquement avec --profile monitoring

Utilisation :

Fenêtre de terminal
# Démarrage normal : db + api uniquement
docker compose up -d
# Démarrage avec pgadmin pour debug
docker compose --profile debug up -d
# Démarrage avec monitoring
docker compose --profile monitoring up -d
# Combiner plusieurs profils
docker compose --profile debug --profile monitoring up -d

Cas d’usage réels :

ProfilServicesUtilisation
debugpgadmin, phpmyadmin, redis-commanderExploration BDD en dev
monitoringprometheus, grafanaMétriques et dashboards
testwiremock, mock-apiTests d’intégration
docsswagger-ui, redocDocumentation API

Dans un projet réel, vous avez souvent besoin de configurations différentes selon l’environnement : développement (avec hot-reload), staging (avec logs verbeux), production (avec limites ressources strictes).

Docker Compose supporte plusieurs fichiers YAML qui se fusionnent automatiquement. C’est plus maintenable que de dupliquer toute la configuration dans plusieurs fichiers séparés.

Structure recommandée :

mon-projet/
├── docker-compose.yml # Configuration de base (commune à tous les envs)
├── docker-compose.override.yml # Overrides pour dev (auto-chargé)
├── docker-compose.prod.yml # Overrides pour production
├── docker-compose.test.yml # Overrides pour tests
├── .env.dev # Variables dev
├── .env.prod # Variables prod
└── .env.test # Variables test

docker-compose.yml (base commune) :

services:
api:
image: mon-api:${VERSION:-latest}
environment:
DB_HOST: db
DB_NAME: ${DB_NAME}

docker-compose.override.yml (dev — chargé automatiquement) :

services:
api:
build: ./api # Build local au lieu d'utiliser l'image
volumes:
- ./api:/app # Hot reload
environment:
DEBUG: "true"
ports:
- "3000:3000" # Expose pour accès direct

docker-compose.prod.yml (production) :

services:
api:
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M
restart: unless-stopped
# Pas de ports exposés (via reverse proxy)

Utilisation selon l’environnement :

Fenêtre de terminal
# Développement (charge base + override automatiquement)
docker compose up -d
# Production (base + prod, ignore override)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Test (base + test)
docker compose -f docker-compose.yml -f docker-compose.test.yml up -d

Différences entre développement et production :

Comparaison configurations développement vs production avec Docker Compose

Fusion des propriétés :

Type de propriétéComportement
Scalaires (image, restart)Écrasement : dernier fichier gagne
Listes (ports, volumes)Fusion : combinaison des deux listes
Maps (environment, labels)Fusion : clés dupliquées écrasées

Exemple de fusion :

# Fichier 1
environment:
DEBUG: "false"
API_KEY: "dev-key"
# Fichier 2
environment:
DEBUG: "true"
LOG_LEVEL: "info"
# Résultat fusionné
environment:
DEBUG: "true" # Écrasé
API_KEY: "dev-key" # Conservé
LOG_LEVEL: "info" # Ajouté

Voici les commandes que vous utiliserez quotidiennement. La plupart s’exécutent depuis le dossier contenant votre docker-compose.yml. Si vous êtes ailleurs, utilisez l’option -f pour spécifier le chemin du fichier.

CommandeDescription
docker compose up -dDémarre tous les services en arrière-plan
docker compose downArrête et supprime les conteneurs et réseaux
docker compose down -vIdem + supprime les volumes (⚠️ perte de données)
docker compose psListe les conteneurs du projet
docker compose logs -fAffiche les logs en temps réel
docker compose exec mysql bashOuvre un shell dans le conteneur MySQL
docker compose restartRedémarre tous les services
docker compose pullMet à jour les images
docker compose configValide et affiche la configuration finale

Même avec un fichier Compose bien configuré, des problèmes peuvent survenir. Voici les erreurs les plus fréquentes et comment les résoudre. Dans tous les cas, votre premier réflexe devrait être de consulter les logs avec docker compose logs — ils contiennent souvent la réponse.

SymptômeCause probableSolution
port is already allocatedUn autre service utilise déjà le port 8080Changez le port dans ports: ou arrêtez l’autre service
WordPress affiche “Error establishing database connection”MySQL n’est pas encore prêt ou mauvais credentialsVérifiez les variables d’environnement, ajoutez un healthcheck
Les données disparaissent après docker compose downVous avez utilisé down -v qui supprime les volumesUtilisez down sans -v pour garder les données
network not foundLe réseau a été supprimé manuellementRelancez docker compose up pour le recréer
Conteneur en restart loopL’application plante au démarrageConsultez docker compose logs <service>
Changements de config non appliquésCompose utilise l’ancienne image/configLancez docker compose up -d --build --force-recreate

Docker Compose est parfait pour le développement local, mais ce n’est pas un orchestrateur de production. Pourquoi ? Parce qu’il ne gère qu’une seule machine.

Imaginons que votre serveur tombe en panne. Avec Compose, vos conteneurs s’arrêtent et ne redémarrent pas — il n’y a pas de serveur de secours. Imaginons que votre application ait besoin de plus de ressources. Avec Compose, vous ne pouvez pas répartir la charge sur plusieurs serveurs.

Pour ces besoins, vous avez besoin d’un véritable orchestrateur : Docker Swarm (plus simple) ou Kubernetes (plus puissant). Voici une comparaison pour vous aider à choisir :

FonctionnalitéDocker ComposeDocker SwarmKubernetes
Orchestration multi-serveurs
Load balancing automatique
Redémarrage sur panne de serveur
Scaling automatique (HPA)
Rolling updates
RBAC et sécurité avancéeBasique
ComplexitéFaibleMoyenneÉlevée
Cas d’usageDev/test/CIPetit clusterProduction

Règle simple : utilisez Compose en développement. Pour la production, passez à Kubernetes (ou au minimum Docker Swarm).

  1. Docker Compose orchestre plusieurs conteneurs avec un seul fichier YAML et une seule commande.

  2. Trois concepts : les services (conteneurs), les réseaux (communication) et les volumes (persistance).

  3. docker compose up -d démarre tout, docker compose down arrête tout.

  4. Externalisez vos secrets dans un fichier .env (jamais en clair dans le YAML versionné).

  5. Utilisez des healthchecks pour que les dépendances attendent que les services soient vraiment prêts.

  6. Compose n’est pas pour la production : pas de haute disponibilité, pas de scaling multi-serveurs.

  • Docker Compose installé (docker compose version fonctionne)
  • Fichier docker-compose.yml créé et validé (docker compose config)
  • Services démarrés (docker compose up -d)
  • Communication inter-services fonctionnelle (WordPress accède à MySQL)
  • Données persistées (volume db_data créé)
  • Secrets externalisés (fichier .env ou secrets Docker)
  • Fichier .env dans .gitignore