Intro
Le Docker Compose « avancé » ne consiste pas à lancer plus de conteneurs, mais à maîtriser la façon dont les services sont construits, démarrés, découverts, configurés, sécurisés et observés comme une unité unique et prévisible. Dans ce guide, vous allez :
- Comprendre comment Compose groupe les ressources et résout la configuration pour prédire le comportement.
- Appliquer les profils, les healthchecks avec dépendances conditionnelles, les secrets et configs, les ancres, et les overrides multi‑fichiers.
- Durcir les conteneurs (système de fichiers en lecture seule, no‑new‑privileges, ulimits) et éviter les erreurs courantes.
- Lancer un pilote local ciblé qui prouve la valeur sans risquer la production.
Vue d’ensemble du workflow
Suivez ce flux de déploiement sûr :
- Concevoir le graphe de services
- Définissez les services, leurs frontières de données et les ports publiés. Utilisez des volumes nommés pour l’état.
- Conservez le réseau de projet par défaut pour la découverte DNS ; évitez les bridges personnalisés sauf nécessité.
- Construire
- Utilisez un contexte de build réduit, des cibles multi‑étapes et des build args. Activez BuildKit. Favorisez le cache de couches et un
.dockerignorerobuste.
- Exécuter
- Démarrez avec un nom de projet explicite, activez des outils optionnels via des profils et ajoutez des healthchecks. Utilisez
depends_onavec des conditions pour piloter la « prêtitude », pas seulement l’ordre de démarrage.
- Observer
- Streamez les logs, inspectez l’état de santé et testez les noms DNS sur le réseau du projet.
- Durcir
- Ajoutez des systèmes de fichiers en lecture seule quand c’est possible, définissez des ulimits et montez les secrets comme fichiers. Évitez
deploy.*sauf si vous utilisez Swarm.
Les mécanismes internes de Compose qui comptent
- Nom de projet
- Compose groupe tout par un nom de projet. Définissez‑le avec le champ de niveau supérieur
name:ou l’option-p. Les réseaux et volumes reçoivent ce préfixe, isolant les projets sur le même hôte.
- Réseau par défaut
- Compose crée un réseau bridge défini par l’utilisateur pour chaque projet. Chaque nom de service est un hostname DNS sur ce réseau (par exemple,
dbrésout vers le conteneur Postgres). Vous n’avez pas besoin delinks.
- Fusion de fichiers
- Plusieurs fichiers passés avec
-ffusionnent de haut en bas. Les fichiers ultérieurs surchargent ou étendent les précédents. Conservez un fichier de base stable et superposez les changements spécifiques à l’environnement.
- Résolution des variables d’environnement
- Compose prend en charge
${VAR}et${VAR:-default}dans le YAML. Il lit un.envdans le répertoire du projet et votre environnement shell ; les valeurs du shell priment sur.env.env_filesous un service injecte des variables dans le conteneur uniquement et n’affecte pas la substitution au niveau Compose.
- Ordonnancement vs « prêtitude »
depends_onsans conditions n’ordonne que le démarrage. Pour la prêtitude, définissez unhealthchecket utilisezdepends_on: condition: service_healthy.
- État de santé
- Les services avec healthchecks rapportent
starting,healthyouunhealthy. Interrogez avecdocker compose psetdocker compose logs.
- Clés spécifiques à Swarm
- Les clés sous
deploy.*sont destinées au mode Swarm. Docker Compose (hôte unique) en ignore la plupart. Utilisez les options au niveau service (environment,volumes,ulimits,read_only,mem_limit,cpus, etc.) hors Swarm.
- Champ de version
- Le Compose moderne ne requiert pas
version:. Omettez‑le et fiez‑vous à la spécification Compose prise en charge par votre CLI.
Concepts avancés avec un exemple proche de la production
Un fichier de base concis montrant des patterns concrets : profils, dépendances conditionnées par l’état de santé, secrets/configs, ancres et paramètres sûrs par défaut.
# compose.yml
name: myapp
# Champs d’extension et ancres pour la réutilisation
x-service-defaults: &service_defaults
restart: unless-stopped
networks:
- appnet
env_file:
- ./.env
init: true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
mem_limit: 512m
cpus: "0.50"
x-healthchecks:
postgres: &pg_health
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
interval: 3s
timeout: 3s
retries: 10
start_period: 10s
redis: &redis_health
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 3s
retries: 10
start_period: 5s
web: &web_health
test: ["CMD", "wget", "-qO-", "http://localhost/healthz"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
services:
db:
<<: *service_defaults
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck: *pg_health
secrets:
- db_password
ulimits:
nofile: 65535
cache:
<<: *service_defaults
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redisdata:/data
healthcheck: *redis_health
web:
<<: *service_defaults
build:
context: ./web
dockerfile: Dockerfile
target: runtime
args:
APP_ENV: ${APP_ENV:-dev}
ssh:
- default
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
ports:
- "127.0.0.1:8080:80" # lier en local pour éviter une exposition sur le LAN
healthcheck: *web_health
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
configs:
- source: nginx_conf
target: /etc/nginx/conf.d/app.conf
adminer:
<<: *service_defaults
image: adminer:4
profiles: ["tools"]
ports:
- "127.0.0.1:8081:8080"
configs:
nginx_conf:
file: ./deploy/nginx/app.conf
secrets:
db_password:
file: ./secrets/db_password.txt
networks:
appnet: {}
volumes:
pgdata: {}
redisdata: {}
Points clés :
name: myappdéfinit un préfixe de projet stable pour réseaux et volumes.- Les ancres réduisent les duplications et préviennent la dérive de configuration.
- Les healthchecks avec
depends_on: condition: service_healthyimposent que web attende la prêtitude de Postgres et Redis. - Les
secretsetconfigsmontent des fichiers dans les conteneurs. Protégez leurs sources dans votre répertoire de travail. read_onlyetno-new-privilegesdurcissent le conteneur web. Untmpfspour/tmpévite les écritures dans l’image.- Liezen les ports à
127.0.0.1en dev local pour éviter toute exposition involontaire.
Overrides locaux pour itération rapide
Utilisez un override pour bind‑monter le code source et activer des flags de debug sans toucher au fichier de base. compose.override.yml est auto‑chargé par docker compose.
# compose.override.yml
services:
web:
volumes:
- ./web/src:/var/www/src:rw,cached
environment:
DEBUG: "1"
Pour des superpositions explicites et propres à l’environnement, créez un fichier dédié et passez‑le avec -f.
# compose.prod.yml
services:
web:
environment:
APP_ENV: prod
ports:
- "80:80" # publier sur le port standard en prod
adminer:
profiles: ["never"] # s’assurer que les outils restent inactifs en prod
Exécutez avec :
# Base + overlay prod explicite
docker compose -f compose.yml -f compose.prod.yml up -d
Variables d’environnement et .env
Créez un .env dans le répertoire du projet pour la substitution au niveau Compose et les valeurs communes :
# .env
APP_ENV=dev
POSTGRES_PASSWORD=unused_when_using_secret
# Le fichier secrets/db_password.txt reste requis pour le secret
Notes :
- Les variables du shell priment sur
.envpour la substitution :APP_ENV=staging docker compose uputiliserastaging. env_fileinjecte des variables uniquement dans le conteneur ciblé et n’influence pas l’interpolation de Compose.- Préférez les secrets pour les valeurs sensibles ; évitez de les committer au VCS.
Builds multi‑étapes avec BuildKit et SSH
Utilisez des builds multi‑étapes pour des images runtime compactes. Avec BuildKit, vous pouvez transférer votre agent SSH en toute sécurité pour récupérer des dépendances privées.
Exemple : une image compacte pour un service Go.
# api/Dockerfile
FROM golang:1.22 AS build
WORKDIR /src
# optionnel : utiliser le SSH de BuildKit pour récupérer des deps privées
# RUN --mount=type=ssh go env -w GOPRIVATE=github.com/yourorg/*
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags='-s -w' -o /out/api ./cmd/api
FROM gcr.io/distroless/base-debian12 AS runtime
COPY --from=build /out/api /usr/local/bin/api
USER 65532:65532
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/api"]
Activez BuildKit et construisez avec transfert SSH :
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
ssh-add -l >/dev/null 2>&1 || ssh-add
# Si le service api est dans votre fichier Compose, vous pouvez le construire ainsi :
docker compose build --ssh default
Plan de pilote local
Démarrez par un pilote étroit et mesurable qui exerce les fonctionnalités les plus utiles sans complexifier.
Objectifs du pilote :
- Prouver que le démarrage conditionné par l’état de santé fonctionne (web attend db et cache).
- Valider le montage des secrets et des configs.
- Confirmer le périmètre du projet et la découverte de service via le réseau de l’appli.
Préparation :
# Créer les répertoires et fichiers
mkdir -p deploy/nginx secrets web/src
printf 'server {\n listen 80;\n location /healthz { return 200 "ok"; }\n location / { return 200 "hello"; }\n}\n' > deploy/nginx/app.conf
umask 077 && printf 'supersecretpassword\n' > secrets/db_password.txt
# Optionnel : ajouter .env avec APP_ENV=dev
Lancer le pilote :
# Démarrer la pile de base
docker compose up -d --build
# Ou inclure le profil d’outils optionnel
docker compose --profile tools up -d --build
Validation :
# Vérifier l’état des services et la santé
docker compose ps
# Attendre que tous les services soient healthy (versions récentes)
docker compose up --wait
# Atteindre l’endpoint web
curl -sS http://localhost:8080/healthz
# Confirmer la découverte DNS de web vers db
docker compose exec web getent hosts db
# Vérifier que les secrets sont montés dans db
docker compose exec db ls -l /run/secrets
Nettoyage propre une fois terminé :
docker compose down --volumes
Erreurs de configuration courantes et correctifs rapides
- Utiliser le tag
latestpartout - Correctif : épinglez les images (par ex.
postgres:16,redis:7) pour éviter les mises à jour surprises.
- Publier les ports au monde par défaut
- Correctif : liez à localhost en dev (
127.0.0.1:8080:80). Ne publiez les ports publics qu’en production via des overlays.
- Penser que
depends_onattend la prêtitude - Correctif : ajoutez un
healthchecketdepends_on: condition: service_healthy. Assurez‑vous que l’endpoint de santé est léger et fiable.
- Confondre
.envavecenv_file - Correctif : utilisez
.envpour l’interpolation Compose etenv_filepar service pour l’environnement du conteneur. Rappelez‑vous que l’environnement du shell prime sur.env.
- Monter les données de base de données avec des bind mounts
- Correctif : utilisez des volumes nommés pour les bases (
pgdata,redisdata). C’est plus portable et plus sûr.
- Ajouter des options
deploy.*dans un Compose hors Swarm - Correctif : retirez
deploy.*. En local, utilisezmem_limit,cpus,ulimits,read_only, etc.
- Forcer
container_name - Correctif : évitez
container_name. Cela casse le scaling et l’unicité des noms. Laissez Compose générer des noms avec le préfixe de projet.
- Healthchecks instables
- Correctif : ajustez
start_period,intervaletretries. PréférezCMD-SHELLquand vous avez besoin du shell ; sinon, utilisez des tableauxCMD.
- Builds volumineux et lents
- Correctif : réduisez le contexte de build, utilisez
.dockerignore, des builds multi‑étapes et les caches BuildKit.
Conseils pratiques et paramètres sûrs par défaut
- Utilisez des volumes nommés pour les bases de données ; évitez de bind‑monter leurs répertoires de données.
- Conservez le réseau de projet par défaut et fiez‑vous aux noms de service pour le DNS. Ne publiez que les ports réellement nécessaires.
- Préférez les healthchecks avec
depends_onconditionnel aux scripts d’attente ad hoc. - Utilisez des profils pour laisser les outils optionnels inactifs par défaut.
- Durcissez avec
read_only,no-new-privileges,ulimitset des utilisateurs non‑root quand c’est possible. - Traitez les secrets locaux comme sensibles : restreignez les permissions de
./secretset ne les commitez jamais. - Gardez vos fichiers Compose concis et factorisez les overrides par environnement.
Conclusion
Vous disposez désormais d’un modèle pratique et à jour sur la façon dont Docker Compose groupe les services, câble le réseau et la découverte, résout la configuration et impose la prêtitude. Commencez par le pilote local pour valider le démarrage piloté par l’état de santé, les secrets et les configs. Ensuite, superposez des fichiers spécifiques à l’environnement, durcissez les conteneurs avec des systèmes de fichiers en lecture seule et no-new-privileges, épinglez les images et maintenez les outils optionnels derrière des profils. Cette approche progressive et disciplinée réduit les risques tout en libérant Compose pour des opérations quotidiennes rapides et fiables.