Agrégation et priorisation des fichiers de configuration
L'outil charge automatiquement le descripteur docker-compose.yml situé à la racine du projet. Il est néanmoins possible de chaîner plusieurs fichiers via l'indicateur -f. Lors de l'exécution, le moteur fusionne les déclarations en respectant l'ordre de déclaration : les propriétés définies dans les fichiers ultérieurs écrasent les valeurs identiques rencontrées précédemment.
Créons un fichier de base nommé infra-base.yml :
version: '3.8'
services:
api_backend:
build: ./src
cache_layer:
image: "memcached:1.6"
Ajoutons un fichier spécifique au développement, env-dev.yml :
version: '3.8'
services:
api_backend:
ports:
- "8080:3000"
L'exécution de la commande suivante permet de visualiser la configuration résultante sans lancer les conteneurs :
$ docker-compose -f infra-base.yml -f env-dev.yml config
Le résultat affiche un seul fichier unifié où la section ports a été injectée au service api_backend.
Pour illustrer l'écrasement de paramètres, définissons env-prod.yml :
version: '3.8'
services:
api_backend:
ports:
- "443:3000"
cache_layer:
image: "memcached:1.6-alpine"
La fusion infra-base.yml + env-prod.yml remplacera l'image Redis/Memcached et modifiera l'exposition du port sur 443.
Une convention native existe pour simplifier ce mécanisme : le moteur lit implicitement docker-compose.yml puis docker-compose.override.yml. Cette approche évite la répétition de l'indicateur -f, bien qu'elle limite la flexibilité lors de la gestion de multiples environnements.
Ségrégation réseau et isolation des services
La couche réseau de Docker permet de segmenter la communication entre les conteneurs. En attribuant explicitement des interfaces virtuelles, on restreint les flux aux seuls services qui en ont strictement besoin.
version: '3.8'
services:
gateway_svc:
image: caddy:latest
ports:
- "80:80"
networks:
- public_zone
app_worker:
build: ./application
networks:
- public_zone
- private_zone
db_primary:
image: postgres:14
networks:
- private_zone
networks:
public_zone:
private_zone:
Dans cette topologie, gateway_svc et app_worker partagent public_zone. Le service app_worker sert de pont en étant également attaché à private_zone avec db_primary. Un conteneur situé sur le réseau public ne pourra pas initier de connexion directe vers la base de données, garantissant ainsi une barrière de sécurité logique. Le lancement via docker-compose -p demo-reseau up -d créera les deux réseaux et les trois conteneurs. Une tentative de ping depuis db_primary vers gateway_svc échouera, confirmant l'isolation.
Orchestration de la séquence de démarrage
Par défaut, les services sont initialisés de manière asynchrone. Lorsqu'une dépendance fonctionnelle existe (par exemple, une application nécessitant une file de messages active), l'instruction depends_on permet de forcer un ordre de lancement hiérarchique.
version: '3.8'
services:
lb_frontend:
image: traefik:v2.9
ports:
- "80:80"
depends_on:
- core_service
- queue_manager
core_service:
build: ./app
depends_on:
- queue_manager
queue_manager:
image: rabbitmq:3-management
À chaque exécution de la commande d'initialisation, le moteur respectera cette chaîne : queue_manager → core_service → lb_frontend. Il est crucial de noter que depends_on attend uniquement que le conteneur soit créé et que son processus principal soit lancé, sans vérifier si le service est effectivement prêt à accepter des requêtes. Pour gérer ce cas, l'intégration d'un script de vérification (type wait-for-it) ou l'utilisation de healthchecks reste nécessaire.
Gestion du stockage persistant
Les volumes constituent la méthode recommandée pour conserver les données au-delà du cycle de vie d'un conteneur. La syntaxe moderne distingue clairement les volumes nommés des montages liés (bind mounts).
version: '3.8'
services:
frontend_ui:
image: node:18-alpine
volumes:
- type: volume
source: app_shared
target: /mnt/shared
- type: bind
source: ./logs/runtime
target: /var/log/nodeapp
backend_api:
image: python:3.10-slim
volumes:
- app_shared:/mnt/shared
- user_uploads:/data/media
volumes:
app_shared:
user_uploads:
Dans cette configuration, app_shared et user_uploads sont des volumes nommés gérés par le moteur Docker. Le premier est monté dans les deux services, permettant un partage synchrone des fichiers. Le montage lié bind mappe diretcement le répertoire local ./logs/runtime vers le conteneur, ce qui est idéal pour déboguer ou centraliser les traces. La création des volumes doit être explicitée dans le nœud racine volumes dès qu'un partage inter-services est requis.
Configuration des drivers de journalisation
Le nœud logging permet de rediriger et de formater les flux standards sortants. On peut y appliquer des politiques de rétention pour éviter la saturation du disque hôte.
version: '3.8'
services:
api_backend:
build: ./src
ports:
- "8080:3000"
logging:
driver: "local"
options:
max-size: "50m"
max-file: "3"
cache_layer:
image: "memcached:1.6"
Le driver local stocke les journaux dans un format compressé sur le système hôte. Les options configurées limitent chaque fichier à 50 mégaoctets et conservent un maximum de trois archives, purgeant automatiquement les plus anciens.
Modularité via les champs d'extension YAML
À partir de la version 3.4, les fichiers Compose acceptent les champs d'extension préfixés par x-. Couplés aux ancres YAML (& et *), ils permettent de définir des blocs réutilisables et de centraliser la maintenance des paramètres communs.
version: '3.8'
x-service-config: &defaults
restart: on-failure
logging:
driver: local
options:
max-size: 10m
services:
api_backend:
build: ./src
<<: *defaults
ports:
- "8080:3000"
cache_layer:
image: "memcached:1.6"
<<: *defaults
L'exécution de docker-compose config révèle que les ancres sont résolues dynamiquement, injectant les politiques de redémarrage et les options de journalisation dans chaque service concerné. Cette pratique réduit considérablement la duplication de code et facilite les mises à jour globales de l'infrastructure.