Docker Compose est un outil qui permet de définir et lancer plusieurs conteneurs Docker comme une seule application. Il utilise un fichier YAML pour décrire les services, réseaux et volumes, simplifiant l’orchestration. C’est une approche déclarative typique de la philosophie IaC (infrastructure as code).
En d’autres mots, au lieu d’exécuter plusieurs lignes de commande docker run avec des options complexes, vous décrivez votre architecture dans un fichier YAML et lancez tout avec Docker compose up.
Docker Compose automatise le démarrage, l’arrêt et la coordination des conteneurs, rendant le développement multiservice plus rapide, reproductible et facile à gérer.
Malgré la domination de Kubernetes dans les déploiements à grande échelle, Docker Compose demeure l’outil de prédilection pour du déploiement local.
Cela est utile pour faire des tests, apprendre comment monter des environnements, monter des architectures 3-tiers ou reproduire un environnement de prod sur votre poste.
On peut aussi appeler un Dockerfile à partir d’un Docker Compose
Pourquoi Docker compose ?
Sans Docker Compose, vous devriez :
Lancer chaque conteneur manuellement avec des commandes docker run séparées
Configurer les réseaux pour que les conteneurs puissent communiquer entre eux
Créer et attacher les volumes pour la persistance des données
Gérer les dépendances (par exemple, s’assurer que la base de données démarre avant l’application)
Répéter toutes ces étapes à chaque fois que vous voulez démarrer votre application
Installation de Docker Compose sur Linux (si nécessaire, Docker Compose est déjà inclus dans Docker Desktop sur Windows)
# 1. Mise à jour des paquets et installation des prérequissudo apt update
sudo apt install -y ca-certificates curl gnupg
# 2. Ajout de la clé GPG officielle de Dockersudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 3. Ajout du dépôt officiel Docker aux sources APTecho \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 4. Installation de Docker Engine et du plugin Docker Composesudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 5. Configuration des permissions (exécution sans sudo)sudo usermod -aG docker $USER
newgrp docker
# 6. Vérification de l'installationdocker compose version
concepts fondamentaux
Avant d’écrire votre premier fichier, comprenez ces trois concepts fondamentaux.
Service
Un service dans Docker Compose représente un conteneur logique.
Il décrit l’image à utiliser, les ports, les variables d’environnement, les volumes et les dépendances.
Chaque service correspond à une partie autonome de l’application, orchestrée par Compose.
Chaque service peut définir :
L’image Docker à utiliser
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 doivent démarrer en premier)
Les ressources (limites CPU, mémoire) et les politiques de redémarrage
Exemple d’utilisation : Dans une architecture 3‑tiers, nous définissons trois services dans notre fichier Docker Compose :
# ==============================================================================# EXEMPLE : ARCHITECTURE MULTI-CONTENEURS AVEC DOCKER COMPOSE# ==============================================================================version: "3.8"services:
# ----------------------------------------------------------------------------# 1. LE SERVICE DE BASE DE DONNÉES (Couche persistance - MySQL)# ----------------------------------------------------------------------------db:
# Toujours cibler une version spécifique (ici une version LTS) en # développement et production. Éviter ':latest' qui peut briser le projet du jour au lendemain.image: mysql:8.4# Nommer le conteneur évite que Docker génère un nom aléatoire.# Utile pour vos commandes CLI : 'docker logs cegep-mysql-db' ou 'docker exec -it ...'container_name: cegep-mysql-db # Résilience. 'always' force le conteneur à redémarrer s'il plante ou si # l'ordinateur/serveur redémarre.restart: always environment:
# Sécurité et flexibilité. La syntaxe ${VAR:-defaut} tente de lire une # variable dans un fichier '.env' local. Si elle n'existe pas, elle prend la valeur après le ':-'.# C'est la bonne pratique pour ne JAMAIS pousser de vrais mots de passe sur GitHub.MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-passwordRoot}MYSQL_DATABASE: ${MYSQL_DB_NAME:-database_Name}MYSQL_USER: ${MYSQL_USER:-userMysql}MYSQL_PASSWORD: ${MYSQL_PASSWORD:-password_Mysql}volumes:
# Persistance des données. Un conteneur est éphémère (si on le supprime, on perd tout).# On mappe un "Volume nommé" (géré par Docker) vers le dossier interne de MySQL. - db_data:/var/lib/mysql# CONCEPT (Astuce de prof) : Si vous décommentez la ligne sous ce commentaire, tout les scripts # .sql placés dans votre dossier local './scripts_init' seront exécutés AUTOMATIQUEMENT # au tout premier démarrage de la base de données. Très utile pour injecter un schéma et générer une bd !# - ./scripts_init:/docker-entrypoint-initdb.dnetworks:
# Sécurité par isolation. La base de données est placée UNIQUEMENT dans # le réseau arrière-plan (backend). Elle est invisible depuis l'extérieur ou depuis le serveur Web. - backend-network healthcheck:
# Validation d'état (Crucial !). Par défaut, Docker considère qu'un conteneur # est prêt dès que son processus démarre. Mais MySQL prend souvent 10-15 secondes pour s'initialiser.# Ce test exécute un 'ping' interne pour confirmer que MySQL est RÉELLEMENT prêt à répondre.test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$MYSQL_ROOT_PASSWORD"]
interval: 10s # Temps entre chaque vérificationtimeout: 5s # Temps max accordé à la commande pour répondreretries: 5# Nombre d'échecs consécutifs avant de déclarer le conteneur "unhealthy"# ----------------------------------------------------------------------------# 2. L'APPLICATION NODE.JS (Couche Logique / API Backend)# ----------------------------------------------------------------------------app:
# Les images '-alpine' sont basées sur une distribution Linux ultra-légère (~5 Mo).# Cela permet de télécharger et déployer les conteneurs beaucoup plus rapidement au laboratoire.image: node:20-alpine container_name: cegep-node-apprestart: unless-stopped # Redémarre sauf si l'utilisateur l'a arrêté manuellement (ex: docker compose down)# Définit le dossier de travail PAR DÉFAUT à l'intérieur du conteneur.working_dir: /usr/src/appvolumes:
# Bind Mount (Montage lié) pour le développement. # On lie le dossier './backend' de votre ordinateur au dossier '/usr/src/app' du conteneur.# Ainsi, dès qu'un étudiant modifie son code dans VS Code, le conteneur voit le changement # instantanément (permet le Live Reload / Hot Reload avec nodemon). - ./backend:/usr/src/app - /usr/src/app/node_modulesenvironment:
NODE_ENV: development# Le DNS Interne de Docker. Pour se connecter à la base de données, on n'utilise PAS # une adresse IP (ex: 172.18.0.2) car elle change tout le temps. On utilise simplement le NOM # du service Docker, soit 'db'. Docker résout le nom automatiquement !DB_HOST: db DB_USER: ${MYSQL_USER:-userMysql}DB_PASS: ${MYSQL_PASSWORD:-password_Mysql}DB_NAME: ${MYSQL_DB_NAME:-database_Name}ports:
# Mappage de ports (HÔTE:CONTENEUR). Le port 3000 de votre machine physique # est redirigé vers le port 3000 interne du conteneur. Utile pour tester l'API directement # avec Postman ou l'extension Thunder Client. - "3000:3000"# Remplace l'instruction CMD du Dockerfile d'origine.command: sh -c "npm install && npm run dev"# Installe automatiquement mysql2 au démarrage networks:
# Le pont (Bridge). Ce conteneur a les pieds dans DEUX réseaux.# Il peut parler à la base de données (via backend-network) ET recevoir les requêtes de Nginx # (via frontend-network). C'est le seul intermédiaire. - backend-network - frontend-networkdepends_on:
db:
condition: service_healthy# Attend que MySQL soit 100% prêt# ---------------------------------------------------------------------------# 3. LE SERVEUR WEB / REVERSE PROXY (Couche Présentation - Nginx)# ---------------------------------------------------------------------------web:
image: nginx:alpinecontainer_name: cegep-nginx-webrestart: alwaysports:
# Exposition publique. On mappe le port standard HTTP (80) de la machine hôte. # C'est la seule porte d'entrée officielle pour l'utilisateur final (ex: http://localhost). - "80:80"volumes:
# Injection de configuration et sécurité. On remplace la configuration par défaut # de Nginx par la nôtre. L'ajout du drapeau ':ro' (Read-Only) est une bonne pratique de sécurité : # le conteneur peut lire le fichier, mais ne pourra jamais le modifier ou le corrompre. - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro # CONCEPT OPTIONNEL : Si les étudiants ont un projet Frontend (React, Vue, HTML statique), # Nginx peut servir les fichiers compilés directement sans passer par Node.js, ce qui est beaucoup plus performant.# - ./frontend/dist:/usr/share/nginx/html:ronetworks:
# Sécurité (Principe du moindre privilège). Nginx reçoit les requêtes du Web, il est # le plus vulnérable. En l'isolant dans le 'frontend-network', s'il se fait pirater, l'attaquant # n'a aucun chemin réseau pour atteindre directement la base de données. - frontend-network depends_on:
# Nginx a besoin que l'application Node tourne pour pouvoir lui relayer les requêtes API. - app# ==============================================================================# DÉCLARATION DES INFRASTRUCTURES PARTAGÉES (Réseaux et Volumes)# ==============================================================================networks:
# Par défaut, Docker utilise le pilote (driver) 'bridge' (un commutateur/switch virtuel) # pour créer des sous-réseaux isolés sur la machine hôte.frontend-network:
driver: bridgebackend-network:
driver: bridgevolumes:
# Les volumes nommés sont persistants et gérés par le moteur Docker. Même si l'étudiant # fait un 'docker compose down', les données de la base de données restent en sécurité sur son volume.db_data:
# Assigner un nom explicite permet à l'étudiant de repérer immédiatement son volume # lorsqu'il exécute la commande 'docker volume ls'.name: cegep_mysql_data
Vérifier que tous les conteneurs sont démarrés
docker compose ps
NAME IMAGE COMMAND SERVICE STATUS PORTS
cegep-mysql-db mysql:8.4 "docker-entrypoint.s…" db running (healthy) 3306/tcp, 33060/tcp
cegep-node-app node:20-alpine "docker-entrypoint.s…" app running 0.0.0.0:3000->3000/tcp
cegep-nginx-web nginx:alpine "/docker-entrypoint.…" web running 0.0.0.0:80->80/tcp
# reste qu'à tester avec le navigateur http://localhost
Commandes Docker Compose (CLI)
1. Démarrer et arrêter une stack
docker compose up
Lance tous les services définis dans le fichier.
Options utiles :
-d : mode détaché (démarrer les services en arrière-plan et récupérer votre terminal)
--build : force la reconstruction des images
--pull : force le téléchargement des images
Exemples :
docker compose up
docker compose up -d
docker compose up --build
docker compose down
Arrête et supprime les conteneurs.
Ne supprime pas les volumes.
Options :
--volumes : supprime aussi les volumes nommés
--remove-orphans : supprime les conteneurs non définis dans le compose
Exemples :
docker compose down
docker compose down --volumes
2. Gestion des conteneurs
docker compose ps
Affiche les conteneurs de la stack, leurs ports et leur état.
docker compose ps
docker compose restart
Redémarre un service ou toute la stack.