Retour au cours

infra / docker-compose

Configuration multi-environnements

Leçon 121 exercice

Explication

Ce que vous allez apprendre

  • Structurer un projet avec un fichier de base commun et des overlays par environnement
  • Charger un fichier .env spécifique à chaque environnement avec --env-file
  • Volontairement empêcher un déploiement si une variable critique n'a pas de valeur
  • Combiner plusieurs fichiers Compose dans le bon ordre pour staging et production
  • Encapsuler des commandes longues dans un Makefile pour réduire les erreurs humaines

Dans quel contexte ?

L'équipe de boutique-api déploie la même application sur trois environnements : son poste local, un serveur de staging pour les tests avant mise en production, et le serveur de production lui-même. Chacun a son propre nombre de replicas, ses propres limites mémoire et son propre tag d'image (API_TAG), construit à partir du SHA du commit Git. Cette leçon organise cette configuration sans jamais dupliquer le fichier de base.

Le problème : une application, plusieurs destinations

D'abord, un rappel : une application ne vit jamais dans un seul endroit. Elle est développée en local, testée en staging, puis déployée en production, et chacun de ces environnements a ses propres contraintes : ressources, réplicas, secrets, niveaux de log.

Le piège à éviter : dupliquer toute la config

Le premier réflexe serait de dupliquer l'intégralité de la configuration pour chaque environnement. Mais cela finit inévitablement par diverger, et crée des bugs du type "ça marchait en staging, pas en prod".

La stratégie : base commune + fichiers spécifiques

La bonne approche reprend tout ce qui a été vu dans les leçons précédentes : un fichier de base contient ce qui est partagé par tous les environnements, et des fichiers comme .staging.yml ou .prod.yml ajoutent ou modifient uniquement ce qui change réellement.

Ajouter un fichier .env par environnement

Une fois cette structure en place, il reste à gérer les valeurs sensibles ou spécifiques, comme les tags d'image ou les mots de passe. Chaque environnement reçoit son propre fichier .env pour cela, chargé explicitement avec --env-file.

Une règle de sécurité à ne jamais oublier

En production, certaines variables ne doivent JAMAIS avoir de valeur par défaut silencieuse. Un tag d'image sans valeur de secours force par exemple Compose à échouer explicitement si la variable n'est pas fournie, plutôt que de déployer accidentellement une mauvaise version.

EnvironnementFichiers Compose combinésFichier .env
Local (dev)docker-compose.yml + override.yml (auto).env
Stagingdocker-compose.yml + docker-compose.staging.yml.env.staging
Productiondocker-compose.yml + docker-compose.prod.yml.env.production

Piège fréquent

Écrire image: "myorg/api:${API_TAG:-latest}" en production semble pratique, mais si API_TAG n'est pas défini par erreur dans le pipeline CI, la stack redéploie silencieusement l'image latest au lieu de la version attendue. En production, préfère image: "myorg/api:${API_TAG}" sans valeur par défaut : Compose refuse alors de démarrer plutôt que de se tromper de version.

Pourquoi cette rigidité volontaire est une bonne chose

Cela peut sembler contraignant, mais c'est voulu : mieux vaut un déploiement qui échoue immédiatement avec une erreur claire, qu'un déploiement silencieux avec la mauvaise image en production.

Un dernier conseil pratique

Enfin, évite de multiplier les commandes -f fichier1 -f fichier2 --env-file ... tapées à la main : les encapsuler dans un Makefile ou un script réduit fortement le risque d'erreur humaine au moment d'un déploiement, surtout sous pression.

Après cette organisation multi-environnements, la dernière étape de ce parcours "essentiel" consiste à durcir cette configuration pour qu'elle soit réellement prête pour la production, avec des bonnes pratiques de sécurité et de résilience.

Commandes & code

Configuration multi-environnements

Sur un projet réel, on gère souvent : local, staging, production, chacun avec ses propres images, secrets et ressources.

bash
project/
├── docker-compose.yml            # base commune (services, réseaux)
├── docker-compose.override.yml   # dev local (auto-chargé)
├── docker-compose.staging.yml    # config staging
├── docker-compose.prod.yml       # config prod
├── .env                          # défauts partagés (non sensibles)
├── .env.staging
└── .env.production
yaml
# docker-compose.yml - base
services:
  api:
    image: "myorg/api:${API_TAG:-latest}"
    environment:
      NODE_ENV: "${NODE_ENV:-development}"
    networks:
      - app_net

networks:
  app_net:
yaml
# docker-compose.prod.yml
services:
  api:
    image: "myorg/api:${API_TAG}"      # pas de défaut -> échoue si non défini, volontairement
    restart: always
    deploy:
      replicas: 3
      resources:
        limits:
          memory: 512M
    logging:
      driver: json-file
      options:
        max-size: "20m"
bash
# Staging : combinaison base + staging, avec son propre fichier .env
docker compose --env-file .env.staging \
  -f docker-compose.yml -f docker-compose.staging.yml \
  up -d

# Production : API_TAG doit être défini (ex: le SHA du commit buildé en CI)
API_TAG=$(git rev-parse --short HEAD) \
  docker compose --env-file .env.production \
    -f docker-compose.yml -f docker-compose.prod.yml \
    up -d --remove-orphans
makefile
# Makefile - encapsule les commandes longues pour l'équipe
.PHONY: dev staging prod

dev:
	docker compose up -d

staging:
	docker compose --env-file .env.staging -f docker-compose.yml -f docker-compose.staging.yml up -d

prod:
	docker compose --env-file .env.production -f docker-compose.yml -f docker-compose.prod.yml up -d --remove-orphans

Résumé

  • Un fichier de base + des overlays par environnement évite la duplication.
  • --env-file sélectionne les variables d'interpolation propres à chaque environnement.
  • Ne jamais donner de défaut à une variable sensible/critique en production (API_TAG sans :-).
  • Encapsuler les commandes longues (Makefile, scripts) réduit les erreurs humaines.

Exercices pratiques

1 disponible
1

Mission : la mauvaise version déployée en silence

Objectif : Corriger une valeur par défaut dangereuse sur une variable critique de production.

Contexte

En production, image: "myorg/api:${API_TAG:-latest}" a redéployé silencieusement l'image latest au lieu de la version attendue, car la variable API_TAG n'était pas définie par le pipeline CI ce jour-là. Personne ne s'en est rendu compte avant plusieurs heures.

Corrige cette configuration pour qu'un tel incident devienne impossible.

Résoudre l’exercice →