Introduction
Docker Compose est l'outil de facto pour définir et exécuter des applications multi-conteneurs sur un hôte Docker unique. Si de nombreux développeurs savent écrire un fichier docker-compose.yml de base, moins nombreux sont ceux qui comprennent l'architecture sous-jacente : comment l'interface en ligne de commande (CLI) de Compose traduit un YAML déclaratif en commandes d'exécution de conteneurs, comment les services se découvrent mutuellement, comment les volumes et les réseaux sont gérés, et où les défaillances sont les plus susceptibles de se produire.
Ce guide explique l'architecture de Docker Compose d'un point de vue opérationnel. Vous apprendrez les composants essentiels, le flux de données et de contrôle pendant les commandes up, down et build, ainsi que les compromis pratiques qui affectent la fiabilité et la maintenabilité. Nous examinerons un exemple concret : une application web avec une API dorsale, une base de données et un proxy inverse. À la fin, vous serez en mesure de diagnostiquer les problèmes courants de Compose, de revenir en arrière en toute sécurité et d'exploiter des environnements multi-services avec confiance.
Une architecture Docker Compose bien structurée sépare les préoccupations entre la CLI, le modèle de fichier Compose et le moteur d'exécution de conteneurs sous-jacent. Comprendre ces composants réduit les retouches opérationnelles et rend explicites la configuration, la mise en réseau et le stockage.
Inventaire des versions et de l'environnement
Avant de modifier une configuration Compose, notez les versions et la topologie de votre environnement. Cet inventaire constitue votre référence pour le dépannage et la restauration.
Versions de Docker et de Compose
Docker Compose existe sous deux formes : le binaire autonome docker-compose (v1) et le plugin intégré docker compose (v2). Depuis 2024, la v1 est obsolète et la v2 est la version par défaut dans Docker Desktop et les installations de Docker Engine. Vérifiez vos versions avec :
docker --version
docker compose version
Sortie attendue (exemple) :
Docker version 24.0.7, build afdd53b
Docker Compose version v2.23.0-desktop.1
Seule la v2 prend en charge des fonctionnalités comme docker compose watch, --profile et les nouvelles conditions depends_on. Si vous utilisez encore la v1, effectuez une mise à niveau avant de continuer.
Topologie actuelle
Documentez les services existants et leurs relations. Pour un projet Compose existant, exécutez :
docker compose ps
Exemple de sortie :
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
myapp_web_1 myapp_web:latest "nginx -g 'daemon of..." web 2 hours ago Up 2 hours 0.0.0.0:80->80/tcp
myapp_api_1 myapp_api:latest "uvicorn app.main: ap..." api 2 hours ago Up 2 hours 8000/tcp
myapp_db_1 postgres:15 "docker-entrypoint.s..." db 2 hours ago Up 2 hours 5432/tcp
Cela montre les noms des conteneurs, les images, les commandes et les mappages de ports. Notez que dans un réseau Compose par défaut, les services peuvent s'atteindre par leur nom de service (api, db), et non par le nom du conteneur.
Prérequis
- Docker Engine 20.10+ avec le plugin Compose v2.
- Au moins 4 Go de RAM et 10 Go d'espace disque pour une pile typique à trois services.
- Un répertoire pour le projet, par exemple ~/myapp, avec des autorisations en écriture.
- Une familiarité de base avec les Dockerfiles et Docker en ligne de commande.
Chemin de configuration sûr
Cette section construit une configuration Compose minimale mais réaliste, étape par étape. Nous allons créer un frontend web (nginx), un service API (Python/FastAPI) et une base de données PostgreSQL. Les choix de configuration sont délimités pour être faciles à inspecter et à restaurer.
Structure des répertoires et fichiers initiaux
Créez le répertoire du projet :
mkdir -p ~/myapp/nginx ~/myapp/api
cd ~/myapp
Créez api/app.py :
from fastapi import FastAPI
import psycopg2
import os
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/db")
def read_db():
conn = psycopg2.connect(
host=os.environ.get("DB_HOST", "db"),
database=os.environ.get("DB_NAME", "mydb"),
user=os.environ.get("DB_USER", "user"),
password=os.environ.get("DB_PASSWORD", "password")
)
cur = conn.cursor()
cur.execute("SELECT version();")
version = cur.fetchone()[0]
cur.close()
conn.close()
return {"db_version": version}
Créez api/requirements.txt :
fastapi
uvicorn
psycopg2-binary
Créez api/Dockerfile :
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
Créez nginx/nginx.conf :
events {}
http {
upstream api {
server api:8000;
}
server {
listen 80;
location / {
proxy_pass http://api;
}
}
}
Créez nginx/Dockerfile :
FROM nginx:1.25-alpine
COPY nginx.conf /etc/nginx/nginx.conf
Fichier Compose : version initiale
Créez docker-compose.yml :
version: "3.8"
services:
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- db-data:/var/lib/postgresql/data
api:
build: ./api
environment:
DB_HOST: db
DB_NAME: mydb
DB_USER: user
DB_PASSWORD: password
depends_on:
- db
web:
build: ./nginx
ports:
- "80:80"
depends_on:
- api
volumes:
db-data:
Cette configuration définit trois services, un volume nommé pour la persistance de la base de données et un réseau implicite par défaut. La clé version est facultative dans Compose v2, mais souvent incluse pour la compatibilité.
Explication des composants de l'architecture
- Modèle de fichier Compose : Le fichier YAML est analysé en un modèle de projet. Les services correspondent à des conteneurs ; les réseaux et les volumes sont des ressources de première classe.
- Nom du projet : Par défaut, le nom du projet est le nom du répertoire (myapp). Toutes les ressources sont préfixées par ce nom pour éviter les collisions.
- Réseaux : Compose crée un réseau par défaut unique (par exemple, myapp_default) et y attache tous les services. Chaque service reçoit une entrée DNS correspondant à son nom de service. Cela permet la découverte de services sans coder en dur des adresses IP.
- Volumes : Les volumes nommés (db-data) persistent les données indépendamment du cycle de vie des conteneurs.
- Dépendances : depends_on contrôle l'ordre de démarrage, mais ne garantit pas la disponibilité. Nous y reviendrons plus tard.
Construction et démarrage
Exécutez au premier plan pour une première inspection :
docker compose up --build
Sortie attendue (tronquée) :
[+] Building 12.3s (12/12) FINISHED
...
[+] Running 3/3
✔ Container myapp-db-1 Created
✔ Container myapp-api-1 Created
✔ Container myapp-web-1 Created
Attaching to myapp-db-1, myapp-api-1, myapp-web-1
...
Appuyez sur Ctrl+C pour arrêter. Ensuite, démarrez en mode détaché :
docker compose up -d
Choix d'implémentation délimités
- Pas de réseaux personnalisés : L'utilisation du réseau par défaut est suffisante pour une configuration à hôte unique. Des réseaux personnalisés deviennent nécessaires pour l'isolation ou des sous-réseaux personnalisés.
- Pas encore de contrôles de santé : Nous les ajouterons plus tard après avoir vérifié le flux de base.
- Secrets en texte clair : Pour le développement local uniquement. En production, utilisez les secrets Docker ou des fichiers de variables d'environnement avec des autorisations restreintes.
- Volume persistant pour db : Garantit que les données survivent à docker compose down (sans -v).
Vérification et diagnostic
Après avoir démarré la pile, vérifiez chaque composant et le flux de données global.
État des services
docker compose ps
Tous les services doivent afficher l'état Up et aucune boucle de redémarrage.
Connectivité réseau
Vérifiez que le conteneur web peut atteindre l'API par nom de service :
docker compose exec web wget -qO- http://api:8000/
Sortie attendue :
{"Hello":"World"}
Si cela échoue, vérifiez les journaux du conteneur API :
docker compose logs api
Connectivité de la base de données
Vérifiez que l'API peut interroger la base de données :
docker compose exec api python -c "import psycopg2; conn = psycopg2.connect(host='db', database='mydb', user='user', password='password'); cur = conn.cursor(); cur.execute('SELECT version()'); print(cur.fetchone()[0]); conn.close()"
Sortie attendue (tronquée) :
PostgreSQL 15.4 ...
HTTP de bout en bout
Accédez au service web depuis l'hôte :
curl http://localhost/
Sortie attendue :
{"Hello":"World"}
Et le point de terminaison de la base de données :
curl http://localhost/db
Sortie attendue (tronquée) :
{"db_version":"PostgreSQL 15.4 ..."}
Inspecter le réseau
Listez les réseaux et inspectez le réseau par défaut :
docker network ls
Trouvez myapp_default. Inspectez-le :
docker network inspect myapp_default
Cela montre les conteneurs attachés avec leurs adresses IP et leurs alias. Chaque service a un alias correspondant à son nom de service.
Vérifier le mappage du volume
Inspectez le volume :
docker volume inspect myapp_db-data
Confirmez que le point de montage existe et appartient au conteneur postgres.
Contrôles de santé et disponibilité
Actuellement, depends_on n'attend que le démarrage du conteneur, pas la disponibilité de l'application. L'API peut échouer à se connecter à la base de données si celle-ci est encore en cours d'initialisation. Ajoutez des contrôles de santé au fichier Compose :
services:
db:
image: postgres:15
# ... configuration existante
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 5s
timeout: 5s
retries: 5
api:
# ... configuration existante
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/"]
interval: 10s
timeout: 5s
retries: 3
web:
# ... configuration existante
depends_on:
api:
condition: service_healthy
Appliquez les modifications :
docker compose up -d
Maintenant, Compose attend que les dépendances soient saines avant de démarrer les services dépendants. Vérifiez avec docker compose ps - la colonne STATUS doit afficher (healthy) pour db et api.
Liste de vérification
| Vérification | Commande | Résultat attendu |
|---|---|---|
| État des services | docker compose ps | Tous les services Up (healthy) |
| Connectivité API depuis web | docker compose exec web wget -qO- http://api:8000/ | Réponse JSON |
| Connectivité DB depuis api | docker compose exec api python ... | Chaîne de version PostgreSQL |
| HTTP de bout en bout | curl http://localhost/ | Réponse JSON |
| Point de terminaison DB via proxy | curl http://localhost/db | JSON avec db_version |
| Attachement réseau | docker network inspect myapp_default | Tous les services listés avec alias |
| Présence du volume | docker volume inspect myapp_db-data | Point de montage existe |
Modes de défaillance et récupération
Comprendre les modes de défaillance courants permet une récupération et une restauration rapides.
1. Le service ne démarre pas en raison d'une dépendance manquante
Symptôme : Le conteneur API redémarre à plusieurs reprises avec l'erreur could not connect to server: Connection refused. Cause : La base de données n'est pas prête lorsque l'API tente de se connecter, ou les informations d'identification de la base de données ne correspondent pas. Diagnostic : Vérifiez les journaux :
docker compose logs api
Récupération : Assurez-vous que les contrôles de santé sont définis. Si les informations d'identification ne correspondent pas, corrigez les variables d'environnement et recréez les services :
docker compose up -d --force-recreate
2. Port déjà utilisé sur l'hôte
Symptôme : Le service web échoue avec Bind for 0.0.0.0:80 failed: port is already allocated. Cause : Un autre processus utilise le port 80. Diagnostic : Trouvez le processus :
sudo lsof -i :80
Récupération : Modifiez le port hôte dans le fichier Compose, par exemple 8080:80, puis exécutez docker compose up -d.
3. Corruption des données du volume ou suppression involontaire
Symptôme : La base de données perd des données après docker compose down -v. Cause : L'indicateur -v supprime les volumes nommés. Récupération : Restaurez à partir d'une sauvegarde si disponible. Pour éviter cela, évitez -v sauf si vous souhaitez effacer intentionnellement. Sauvegardez périodiquement le volume :
docker run --rm -v myapp_db-data:/data -v $(pwd):/backup alpine tar czf /backup/db-backup.tar.gz -C /data .
4. Un changement de configuration entraîne une défaillance du service
Symptôme : Après modification de docker-compose.yml ou d'un Dockerfile, les services échouent. Récupération : Revenez à la configuration de travail précédente :
- Conservez les fichiers Compose dans un contrôle de version.
- Annulez la modification du fichier et exécutez docker compose up -d --build.
- Compose ne recréera que les services modifiés.
5. Partition réseau ou défaillance DNS dans le réseau Compose
Symptôme : Les services ne peuvent pas résoudre les noms des autres. Diagnostic : Exécutez une commande dans un conteneur et envoyez un ping à un autre service :
docker compose exec web ping api
Si le ping échoue, vérifiez le réseau :
docker network inspect myapp_default
Assurez-vous que tous les conteneurs sont attachés. Sinon, recréez le réseau :
docker compose down
# Supprimez le réseau manuellement si nécessaire
docker network rm myapp_default
docker compose up -d
6. Épuisement de l'espace disque dû aux images et aux volumes
Symptôme : La construction échoue avec no space left on device. Récupération : Nettoyez les ressources inutilisées :
docker system prune -a --volumes
À utiliser avec prudence ; cela supprime tous les conteneurs, réseaux, images et volumes inutilisés.
7. Sortie inattendue du conteneur
Symptôme : Le conteneur se termine avec le code 137 ou 143. Cause : Tueur de mémoire insuffisante ou signal d'arrêt gracieux. Diagnostic : Vérifiez le code de sortie du conteneur :
docker inspect myapp-api-1 --format='{{.State.ExitCode}}'
Récupération : Augmentez les limites de mémoire dans Compose (mem_limit) ou ajustez l'utilisation de la mémoire de l'application.
Modes de défaillance courants et actions de récupération
| Mode de défaillance | Symptôme | Récupération |
|---|---|---|
| Dépendance non prête | Erreurs de connexion refusée | Ajouter des contrôles de santé, corriger l'environnement, recréer |
| Conflit de port hôte | Erreur de liaison au démarrage | Modifier le mappage du port hôte |
| Perte de données du volume | Données manquantes après down -v | Restaurer à partir d'une sauvegarde, éviter -v |
| Changement de configuration cassé | Les services échouent après modification | Annuler le fichier, reconstruire |
| Échec de résolution DNS | Le ping échoue entre les services | Inspecter le réseau, recréer |
| Disque plein | La construction ou l'exécution échoue avec no space | Élaguer les ressources inutilisées |
| Conteneur OOM | Code de sortie 137 | Augmenter les limites de mémoire |
Liste de contrôle opérationnelle
Utilisez cette liste de contrôle pour l'exploitation et la révision de routine d'un environnement Docker Compose.
Opération quotidienne
- Exécutez docker compose ps pour vérifier que tous les services sont sains.
- Vérifiez les journaux pour les erreurs : docker compose logs --tail=100.
- Surveillez l'utilisation du disque : docker system df.
- Vérifiez la fonctionnalité de bout en bout : curl http://localhost/.
Maintenance hebdomadaire
- Sauvegardez le volume de la base de données (ou automatisez via cron).
- Examinez l'utilisation des ressources : docker stats --no-stream.
- Vérifiez les mises à jour des images : docker compose pull (puis testez avant d'appliquer).
- Inspectez le réseau pour détecter des attachements inattendus.
Avant les changements de configuration
- Validez les fichiers Compose de travail actuels dans le contrôle de version.
- Documentez le changement et l'impact attendu.
- Testez les changements dans un environnement de staging si possible.
Après les changements de configuration
- Exécutez docker compose config pour valider la syntaxe YAML et les variables résolues.
- Appliquez les changements avec docker compose up -d.
- Exécutez la liste de vérification (Section 4).
- Surveillez les journaux pendant 10 minutes pour détecter des erreurs inattendues.
Plan de restauration
- Conservez la version précédente du fichier Compose dans le contrôle de version.
- Si une restauration est nécessaire, extrayez le fichier précédent et exécutez docker compose up -d --build.
- Si le schéma du volume a changé, assurez-vous que la sauvegarde est restaurable.
Revue de sécurité (trimestrielle)
- Examinez les ports exposés ; minimisez les liaisons de ports hôte.
- Faites tourner les mots de passe de la base de données et mettez à jour les variables d'environnement.
- Analysez les images pour les vulnérabilités : docker scan ou des outils tiers.
- Assurez-vous que les fichiers Compose ne contiennent pas de secrets en texte clair ; utilisez des fichiers .env ou les secrets Docker.
Conclusion
L'architecture de Docker Compose est plus qu'une couche de commodité ; c'est un modèle structuré pour les applications multi-conteneurs. En comprenant les composants - CLI, modèle de projet, réseaux, volumes et gestion du cycle de vie - vous pouvez concevoir des déploiements fiables locaux et sur hôte unique.
Dans ce guide, vous avez appris :
- Les composants essentiels : fichier Compose, services, réseaux, volumes et nom de projet.
- Comment construire une configuration sûre avec des dépendances explicites et des contrôles de santé.
- Comment vérifier la pile avec des commandes de diagnostic concrètes et des sorties attendues.
- Les modes de défaillance courants et leurs procédures de récupération.
- Une liste de contrôle opérationnelle pour les tâches quotidiennes, hebdomadaires et de gestion des changements.
Pour un premier pilote pratique, commencez par une configuration multi-services étroite et mesurable qui peut être inspectée localement avant de l'étendre. Enregistrez les versions et la topologie, construisez progressivement, vérifiez chaque couche et ayez toujours un plan de restauration. Cette approche réduit les retouches opérationnelles et renforce la confiance pour passer à des environnements plus complexes.
Prochaines étapes :
- Implémentez la pile d'exemple sur votre machine.
- Ajoutez des contrôles de santé et pratiquez la récupération après défaillance.
- Sauvegardez le volume de la base de données et testez la restauration.
- Étendez l'architecture avec un réseau personnalisé ou un service supplémentaire.
- Intégrez la liste de contrôle opérationnelle dans le flux de travail de votre équipe.
Avec cette base, vous pouvez exploiter les environnements Docker Compose avec clarté et contrôle.