Optimisation et configuration avancée avec Docker Compose

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.

Étiquettes: docker-compose orchestration-conteneurs reseau-docker volumes-persistants configuration-yaml

Publié le 21 septembre à 20h18