Retour au cours

infra / docker-compose

Premier docker-compose.yml : services, image, ports

Leçon 21 exercice

Explication

Ce que vous allez apprendre

  • Déclarer un service Compose avec une image, des ports et des variables d'environnement
  • Comprendre le mapping de ports host:container et pourquoi les deux nombres peuvent différer
  • Distinguer docker compose up (service durable) de docker compose run (commande ponctuelle)
  • Vérifier la configuration résolue avant de démarrer avec docker compose config
  • Éviter le piège classique des ports mal quotés dans un fichier YAML

Dans quel contexte ?

Un développeur reprend le projet boutique-api : il doit exposer l'API sur le port 3000 de sa machine, la base PostgreSQL sur 5432 et un cache Redis sur 6379, sans qu'aucun de ces ports n'entre en conflit avec d'autres projets déjà lancés sur son poste. Cette leçon construit exactement ce type de fichier, service par service.

Repartir de zéro : qu'est-ce qu'un service ?

D'abord, il faut comprendre ce qu'est un "service" dans le vocabulaire de Compose. C'est simplement l'équivalent d'une commande docker run : une image à utiliser, un point d'entrée, des ports, des variables. La différence, c'est que cette configuration est écrite une fois dans un fichier, au lieu d'être retapée à chaque fois dans le terminal.

Le problème avec docker run tout seul

Avec docker run, la configuration existe seulement dans l'historique de ton terminal. Difficile à partager avec un collègue, difficile à relire, impossible à versionner proprement dans Git. Il fallait un moyen de figer cette configuration quelque part.

La solution : décrire plutôt qu'exécuter

C'est là qu'intervient l'idée déclarative de Compose. Au lieu d'exécuter des étapes, on décrit un état voulu : quelle image, quel port exposé, quelles variables. Compose se charge ensuite lui-même d'atteindre cet état, exactement comme un fichier de configuration d'infrastructure classique.

Zoom sur les ports, une notion à bien comprendre

Une fois que le service est déclaré, il reste souvent une question : comment y accéder depuis ta machine ? C'est le rôle du mapping "host:container". Par exemple 3000:3000 relie le port 3000 de ta machine au port 3000 à l'intérieur du conteneur.

Les deux nombres peuvent être différents

Il faut bien réaliser que ces deux nombres n'ont pas besoin d'être identiques. On pourrait très bien écrire 8080:3000 : ta machine écoute alors sur le port 8080, mais redirige vers le port 3000 du conteneur. C'est utile quand plusieurs services internes utilisent le même port par défaut.

Clé du serviceRôleExemple
imageImage à tirer depuis un registrepostgres:16
portsMapping host:container"3000:3000"
environmentVariables injectées dans le conteneurNODE_ENV: production
working_dirDossier de travail dans le conteneur/app
commandCommande exécutée au démarrage["node", "server.js"]

Piège fréquent

Écrire ports: - 8080:80 sans guillemets peut, selon le parseur YAML, être interprété comme une valeur numérique inattendue plutôt qu'un texte "host:container". Prends le réflexe systématique de toujours entourer un mapping de ports par des guillemets doubles, comme "8080:80".

Une commande pour vérifier avant de se tromper

Une fois ton fichier écrit, il reste une étape importante avant de foncer : vérifier ce que Compose va réellement faire. La commande docker compose config affiche la configuration finale résolue, avec toutes les valeurs interpolées — un bon réflexe avant chaque déploiement.

Les pièges à connaître

Premier piège classique : oublier les guillemets autour des ports. Écrire 8080:80 sans guillemets peut être mal interprété par le parseur YAML, qui le lit parfois comme un nombre au lieu d'un texte. Deuxième piège : confondre up, qui démarre un service durable, avec run, qui exécute une commande ponctuelle et jetable, hors de la stack normale.

Maintenant que tu sais déclarer un service avec une image existante, la leçon suivante va plus loin : comment construire sa propre image à partir de son code, au lieu d'utiliser uniquement des images toutes faites.

Commandes & code

Premier docker-compose.yml

Un fichier Compose déclare une liste de services, chacun étant l'équivalent d'un docker run.

yaml
services:
  api:
    image: node:20-alpine
    working_dir: /app
    command: ["node", "server.js"]
    ports:
      - "3000:3000"       # host:container
    environment:
      NODE_ENV: production

  db:
    image: postgres:16
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: changeme
      POSTGRES_DB: app_db

  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
bash
# Démarrer un seul service et ses dépendances
docker compose up -d db

# Exécuter une commande ponctuelle dans un conteneur du projet
docker compose exec db psql -U postgres -d app_db

# Lancer un conteneur "jetable" (pas dans la stack persistante)
docker compose run --rm api node -v

# Voir la configuration résolue (interpolation, defaults, merges)
docker compose config
yaml
# Convention "short syntax" vs "long syntax" pour les ports
services:
  web:
    ports:
      - "8080:80"                 # short syntax
      - target: 80                # long syntax (plus explicite)
        published: 8080
        protocol: tcp
        mode: host

Résumé

  • Chaque clé sous services: est un conteneur nommé.
  • ports: "host:container" publie un port sur la machine hôte.
  • docker compose config valide et affiche le YAML final résolu.
  • run --rm sert aux commandes ponctuelles, up -d aux services durables.

Exercices pratiques

1 disponible
1

Mission : republier boutique-api sur un port libre sans casser le déploiement

Objectif : Corriger un fichier Compose mal quoté et distinguer un service durable d'une commande ponctuelle.

Contexte

Sur le projet boutique-api, un développeur a écrit ports: - 8080:80 sans guillemets dans son docker-compose.yml, et un collègue affirme avoir "démarré l'API" avec docker compose run --rm api node -v, avant de s'étonner que plus rien ne tourne quelques secondes plus tard. Le port 3000, déjà utilisé par un autre projet sur sa machine, doit en plus être remplacé par le port hôte 8080.

Corrige ces problèmes et explique-les, sans copier une réponse toute faite.

Résoudre l’exercice →