Cours NestJS · Module 12: Conteneurs et CI/CD
Docker Compose - réseau multi-services et volumes
Dans cette leçon6
Ton application n'est plus un seul processus. Il y a l'API, il y a MongoDB, il y a Redis - et pour lancer l'ensemble en local, il faut appeler docker run trois fois avec une longue liste d'options, dans le bon ordre, sans oublier le réseau ni le fait que la base a besoin d'un instant pour démarrer. Chaque nouvel arrivant dans l'équipe reçoit ces instructions de vive voix, et un peu différemment à chaque fois.
Une légion ne partait pas en campagne sur des accords verbaux. Elle avait un ordre du jour : la liste écrite des unités, leur ordre, et ce que chacune emporte. Docker Compose est un tel ordre - il sert à gérer plusieurs conteneurs à la fois.
Ne le confonds pas avec ses voisins : construire des images depuis les sources, c'est le rôle de docker build et du Dockerfile ; tester, celui de ton pipeline ; surveiller la consommation de ressources, celui des outils rencontrés en observabilité.
L'ordre du jour
Toute la configuration tient dans un fichier docker-compose.yml :
1services:
2 mongodb:
3 image: mongo:7
4 volumes:
5 - mongo-data:/data/db
6 healthcheck:
7 test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
8 interval: 10s
9 retries: 5
10
11 redis:
12 image: redis:7-alpine
13
14 api:
15 build: .
16 ports:
17 - "3000:3000"
18 environment:
19 DATABASE_URL: mongodb://mongodb:27017/legions
20 REDIS_HOST: redis
21 depends_on:
22 mongodb:
23 condition: service_healthy
24 redis:
25 condition: service_started
26
27volumes:
28 mongo-data:Chaque entrée sous services est un conteneur. image prend une image toute faite, build: . demande d'en construire une depuis le Dockerfile de ce répertoire.
L'ordre de démarrage
Le champ depends_on assure l'ordre de démarrage des services. Nos trois services se lèvent donc ainsi : d'abord MongoDB, puis Redis, et enfin l'API, qui attend les deux.
Mais voici le piège sur lequel butent la plupart des premières configurations : depends_on seul attend uniquement le démarrage du conteneur, non sa disponibilité. MongoDB a besoin de quelques secondes pour charger ses données - le conteneur tourne déjà que la base refuse encore les connexions. L'API démarre, tente de se connecter et meurt.
C'est pourquoi la base porte un healthcheck et depends_on porte la condition condition: service_healthy. Compose attend alors que la commande du healthcheck commence à réussir. Redis se lève vite et service_started lui suffit - le comportement par défaut, signifiant « le conteneur tourne ».
Retiens cette différence : démarré n'est pas prêt. La grande majorité des erreurs « connection refused » au lancement d'un environnement vient exactement de là.
Un réseau sans configuration
Note l'adresse de la base : mongodb://mongodb:27017/legions. Il n'y a là ni localhost ni adresse IP - il y a un nom de service.
Compose crée un réseau commun à tous les services et y enregistre chacun sous son nom. Le conteneur api se connecte à la base en écrivant simplement mongodb - le reste se fait tout seul. Nul besoin d'adresses IP ni de configuration.
Note aussi ce qui manque dans notre fichier : MongoDB et Redis n'ont pas de section ports. C'est délibéré. Sans elle, un service n'est visible qu'à l'intérieur du réseau Compose - l'API peut l'atteindre, personne de l'extérieur ne le peut. Nous n'exposons un port que là où c'est vraiment nécessaire, c'est-à-dire pour l'API.
Les volumes - des données qui survivent au conteneur
Un conteneur est par nature éphémère : tu le supprimes et tout ce qu'il a écrit disparaît. Pour une base de données, c'est un désastre ; aussi les volumes servent à conserver des données durables hors du conteneur.
L'entrée mongo-data:/data/db dit : ce que MongoDB écrit dans le répertoire /data/db, garde-le dans un volume nommé mongo-data, vivant indépendamment du conteneur. Supprimer et recréer le conteneur ne touchera pas ces données.
Les volumes ne limitent pas l'usage du processeur, ne chiffrent pas les communications et ne mettent rien à l'échelle - ils conservent des données, et c'est tout.
Le lancement
Tout l'ordre s'exécute par une seule commande :
1docker compose up --build -dLes deux éléments après up méritent d'être distingués. --build reconstruit les images avant le démarrage - sans lui, Compose utilise celle construite précédemment, quoi que tu aies changé dans le code. -d (pour detached) envoie le tout en arrière-plan et te rend le terminal.
L'arrêt a deux variantes, et la différence entre elles peut coûter cher. docker compose down supprime les conteneurs et le réseau, en laissant les volumes. docker compose down -v supprime aussi les volumes - c'est-à-dire les données de la base. Tu fais la première tous les jours ; la seconde seulement quand tu veux vraiment repartir d'une base vide.
Récapitulatif
L'ordre du jour est écrit et les unités partent dans l'ordre :
- Docker Compose sert à gérer plusieurs conteneurs à la fois - il ne construit pas d'images depuis les sources, ne teste pas et ne surveille pas la consommation,
- la configuration vit dans
docker-compose.yml, et chaque entrée sousservicesest un conteneur, depends_onassure l'ordre de démarrage des services,- démarré n'est pas prêt -
depends_onattend le démarrage du conteneur, non la disponibilité du service, healthcheckaveccondition: service_healthyattend la disponibilité réelle ;service_startedsuffit aux services rapides,- les conteneurs se connectent par nom de service -
mongodb://mongodb:27017, sans adresses IP ni configuration réseau, - l'absence de section
portssignifie que le service n'est visible qu'à l'intérieur du réseau Compose, - les volumes conservent des données durables hors du conteneur - ils ne limitent pas le CPU, ne chiffrent pas et ne mettent rien à l'échelle,
- lancement :
docker compose up --build -d, où--buildreconstruit les images et-dpasse en arrière-plan, downlaisse les volumes,down -vles supprime avec les données.
Dans la prochaine leçon, nous nous occuperons des secrets - ces mots de passe et clés que nous avons encore écrits en clair dans ce fichier. En attendant, retiens ceci : Compose est un ordre du jour - un seul fichier remplaçant les consignes orales que chacun retenait autrement.
Code de cette leçon : src/docker-compose-demo.ts
1// Docker Compose - Réseau routier de l'Empire
2console.log("=== DOCKER COMPOSE ===\n");
3
4interface ComposeService {
5 name: string;
6 image: string;
7 port: number;
8 role: string;
9 dependsOn?: string[];
10}
11
12const services: ComposeService[] = [
13 {
14 name: 'api',
15 image: 'Dockerfile (build)',
16 port: 4000,
17 role: 'Forteresse principale (Praetorium)',
18 dependsOn: ['mongodb', 'redis'],
19 },
20 {
21 name: 'mongodb',
22 image: 'mongo:7.0',
23 port: 27017,
24 role: 'Archives de l\'Empire (base de données)',
25 },
26 {
27 name: 'redis',
28 image: 'redis:7-alpine',
29 port: 6379,
30 role: 'Courrier rapide (cache)',
31 },
32];
33
34console.log("Services dans docker-compose.yml :\n");
35services.forEach(s => {
36 console.log(` ${s.name} :`);
37 console.log(` Image : ${s.image}`);
38 console.log(` Port : ${s.port}`);
39 console.log(` Rôle : ${s.role}`);
40 if (s.dependsOn) {
41 console.log(` depends_on : ${s.dependsOn.join(', ')}`);
42 }
43 console.log();
44});
45
46// Commandes clés
47const commands = [
48 { cmd: 'docker compose up -d', desc: 'Tout lancer en arrière-plan' },
49 { cmd: 'docker compose up --build -d', desc: 'Construire et lancer' },
50 { cmd: 'docker compose down', desc: 'Tout arrêter' },
51 { cmd: 'docker compose logs -f api', desc: 'Logs du service api' },
52 { cmd: 'docker compose ps', desc: 'État des services' },
53];
54
55console.log("Commandes Docker Compose :\n");
56commands.forEach(c => {
57 console.log(` $ ${c.cmd}`);
58 console.log(` ${c.desc}\n`);
59});
60Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. À quoi sert Docker Compose ?
2. Que garantit le champ « depends_on » dans docker-compose.yml ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Éditeur de code
Définissez trois services avec les images, ports, variables d'environnement et dépendances appropriés
- Tri vertical
Ordonnez les services Docker Compose selon leur ordre de démarrage (du premier) :
- Clique dans l'ordre
Ordonnez les éléments de la commande pour lancer Docker Compose en mode détaché avec reconstruction :
- Éditeur de code
Définissez les variables NODE_ENV, PORT, DATABASE, JWT_SECRET, REDIS_HOST, LOG_LEVEL, ALLOWED_ORIGINS pour les deux environnements