Docker-compose

Docker Compose : l’orchestrateur incontournable

Docker Compose facilite grandement le déploiement de différents types de piles d'applications en permettant de définir et de gérer des applications conteneurisées multi-services à l'aide d'un simple fichier YAML (`docker-compose.yml`).
Un conteneur unique suffit rarement — votre API réclame une base de données, un système de cache, un reverse-proxy, etc.
Compose orchestre tout cela de manière explicite et traçable


1. Pourquoi Docker Compose ?

  • Une vision d’ensemble maîtrisée : Un unique fichier source de vérité (docker-compose.yml), versionné dans Git, décrit la stack complète.
    Résultat : les environnements de développement, d’intégration continue et de préproduction deviennent reproductibles.
  • Déploiement et reproduction instantanés Grâce à ce fichier unique, il suffit d’un docker compose up pour recréer l’environnement décrit — fidèlement, partout.
  • Productivité maximale, friction minimale : Fini le « ça marche sur ma machine ». Vos collègues, votre pipeline CI ou un poste fraîchement provisionné peuvent relancer l’intégralité de la stack en quelques dizaines de secondes. Moins d’attente, moins d’erreurs, plus d’itérations.

1. Les fondamentaux du fichier docker-compose.yml

services:
  web:
    build: .
    ports: ["8000:8000"]
    environment:
      - DATABASE_URL=postgres://pg:pg@db:5432/app
    depends_on: [db]

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      retries: 5

volumes:
  pgdata:
  1. services : Un service dans Docker Compose représente un conteneur logique pour exécuter une application.
  2. build / image : Vous pouvez soit construire l'image, soit utiliser une image pré-construite.
  3. Ports : Le mapping entre les ports de l'hôte et ceux du conteneur.
  4. environment / env_file : Gestion des variables d'environnement, secrets et configurations.
  5. volumes : Ils garantissent la persistance des données.
  6. depends_on / healthcheck : Garantir l'ordre d’exécution et vérifier la robustesse des services.
  7. networks (optionnel) : Séparation du front et du back, ainsi que l’isolation pour le testing.

2. Commandes clés — à connaître par cœur

Récapitulatif : ENTRYPOINT vs CMD
Commandes usuelles
Action Commande V2 Ce que ça fait
Démarrer la stack docker compose up -d Build (si besoin) + run en background
Arrêt + suppression docker compose down -v Coupe tout et détruit volumes/réseaux
Logs stream docker compose logs -f --tail=100 Suivre les logs en continu
Rebuild ciblé docker compose up -d --build web Rebuild uniquement le service web
Exécuter une commande d'un service docker compose exec db psql -U postgres Executer la commande psql du service db
Lancer un shell docker compose exec web /bin/bash Ouvre un shell dans un conteneur déjà démarré.
Lancer un conteneur éphémère docker compose run --rm <service> <cmd> Lancer une tâche ad hoc dans un conteneur éphémère (migrations, tests)
Charger un container docker compose pull Forcer la récupération des images distantes
Lister les container en fonctionnement docker compose ps Voir l’état des services/containeurs
Redémarre, arrête et demarre le(s) conteneur(s) docker compose restart|stop|start <service> Il est possible de cibler plusieurs services : docker compose stop web db.
Mise à l’échelle docker compose up -d --scale web=3 3 instances du service web

Note : `docker compose` (avec un espace) = Compose V2, intégré au binaire Docker. `docker-compose` (avec tiret) = l’ancien binaire Python, encore présent mais voué à disparaître.

3. Bonnes pratiques qui font la différence

  1. .env à la racine : pour paramétrer ports, versions d’image, etc.
  2. Fichiers d’override : (`docker-compose.override.yml`, `docker-compose.prod.yml`) : mêmes définitions, options différentes (debug vs production).
  3. Profiles : activer/masquer certains services (`docker compose --profile tests up`).
  4. Santé d’abord : des `healthcheck` explicites évitent que votre app démarre avant Postgres.
  5. Pas de mot de passe en clair : secrets Docker ou variables d’environnement injectées par votre CI/CD.
  6. Gardez le build simple : un `Dockerfile` par service, pas de scripts bash géants dans le compose.

4. Limites à connaître (et comment anticiper)

  • Pas de haute disponibilité multi-nœuds : Compose orchestre sur une seule machine ; pour le cluster, passez à Docker Swarm ou, plus souvent aujourd’hui, à Kubernetes (Kompose convertit un compose.yml en manifestes K8s pour démarrer la migration).
  • Mise à l’échelle basique : le `--scale` lance plusieurs conteneurs, mais sans load-balancer externe ni rolling update fin — OK pour du dev/staging, pas pour 1 000 req/s en prod.

En résumé :
Docker Compose est le tremplin entre le conteneur unique et la vraie architecture micro-services.
Maîtrisez-le dès aujourd’hui : vous gagnerez en fiabilité, en rapidité de mise en route et vous préparerez tranquillement la montée en charge vers le cloud ou Kubernetes.


Explication détaillée des points d'un Docker Compose

Zoom sur chaque brique clé d’un `docker-compose.yml`

1. services – l’unité logique de l'architecture

Chaque composant d'une pile (serveur web, base de données, cache, etc.) est défini comme un service distinct.
Un service peut être construit à partir d'une image Docker existante (par exemple, mysql:5.7 ou wordpress:latest) ou à partir d'un Dockerfile personnalisé.
Une fois le fichier YAML configuré, l'ensemble de l'environnement, qu'il soit composé de deux conteneurs ou plus,
peut être lancé avec une seule commande : docker-compose up.

  • Nom clair = contrat clair (`web`, `db`, `redis-cache`, pas `service1`).
  • Les conteneurs d’un service partagent l’image , les variables d’environnement, les volumes… et peuvent être scalés (`docker compose up --scale web=3`).
  • Isolation contrôlée : par défaut, chaque service reçoit un hostname interne (<service>) et communique via le réseau virtuel créé par Compose.

2.build / image – construire ou télécharger

  • image ⇒ vous partez d’une image existante (ex. `postgres:16-alpine`).
  • build ⇒ Compose construit l’image à la volée.
    build:
        context: .
        dockerfile: Dockerfile.prod
        args:
          NODE_ENV: production
    • context : répertoire envoyé au daemon Docker.
    • args : variables disponibles uniquement au build (pas en runtime).
    • Cache automatique : en V2, Compose ré-utilise la build-cache locale ou distante.

3.ports – exposer le bon service, au bon endroit

Syntaxes:

  • ports:
      - "8080:80"        # hôte:conteneur
      - "127.0.0.1:2222:22"  # bind sur l’IP loopback seulement
      - "80"             # port auto-mappé (rare en prod)
    • Pensée sécurité : n’ouvrez que les ports nécessaires ; laissez le reste en réseau interne.
    • Performance : évitez de ré-exposer inutilement un service si un reverse-proxy interne peut faire le routage.
    • Multi-projets : changez le port hôte (`8080:80`) au lieu du port conteneur pour lancer plusieurs stacks côte à côte.

4. `environment` / `env_file` – paramètres, secrets, config

Il est facile de définir des variables d'environnement pour configurer le comportement de chaque service (par exemple, les mots de passe de la base de données ou l'environnement de développement/production), soit directement dans le fichier docker-compose.yml, soit via un fichier .env.

environment:
  - APP_ENV=prod
  - DATABASE_URL=${DATABASE_URL}
env_file:
  - ./.env
  • Ordre de priorité : CLI › `environment:` › variables du shell qui lance `docker compose`.
  • Substitution `${VAR}` : Compose lit automatiquement un fichier `.env` à la racine si présent.
  • Secrets sensibles :
    • En dev : variables d’environnement + `.env` non commité (ajouté au `.gitignore`).
    • En prod : utilisez les secrets Docker ou un gestionnaire externe (Vault, AWS Secrets Manager) ; Compose v3+ supporte `secrets:`.
  • Config immuable : pour des blobs non sensibles (fichiers YAML, nginx.conf), préférez `configs:` (Swarm/K8s) ou un volume read-only.

5. volumes – persistence et partage

La directive volumes permet de garantir la persistance des données (comme les fichiers de base de données ou le code source) au-delà du cycle de vie des conteneurs, ce qui est crucial pour les applications web.
Les volumes peuvent être nommés ou mappés vers des répertoires locaux sur l'hôte.

Types de volumes :

1. Només – gérés par Docker :
volumes:
  pgdata:      # déclaré en bas du fichier
services:
  db:
    volumes:
      - pgdata:/var/lib/postgresql/data
2. Bind mounts – chemin hôte explicite :

L'indication ./data correspond au nom du dossier local et /data-dans-le-conteneur au nom dossier dans le conteneur.
Ces deux seront liés (mappés).

 - ./uploads:/app/uploads:ro
3. Volumes éphémères (`tmpfs:`) – pour le cache, la session, etc.

6. depends_on / healthcheck – ordre de démarrage et robustesse

La directive depends_on permet de s'assurer que certains services (comme la base de données) sont démarrés avant ceux qui en dépendent (comme l'application web), garantissant ainsi un ordre de démarrage correct de la pile.

services:
  web:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 10s
      retries: 5		
		
  • depends_on v2 accepte désormais condition: service_healthy : le conteneur web attend que Postgres réponde réellement, pas juste qu’il soit lancé.
  • healthcheck est exécuté à l’intérieur du conteneur ; retour 0 = healthy, >0 = unhealthy. Compose relance ou marque le service en échec selon la politique restart: (always, on-failure, etc.).
  • Limites : depends_on ne gère pas les dépendances cycliques ; pour le service discovery, préférez des solutions comme wait-for-it, ou mieux, laissez votre application gérer la reconnexion.

7. networks – isolation, sécurité, scalabilité

Docker Compose crée automatiquement un réseau par défaut ou peut utiliser des réseaux personnalisés, permettant aux services de communiquer entre eux
de manière sécurisée sans configuration réseau manuelle complexe.
Par exemple, dans une application web utilisant une base de données, le conteneur de l'application peut se connecter au conteneur de la base de données en utilisant son nom de service (ex : `db`) comme nom d'hôte.

		version: "3.8"

services:
  app:
    build: ./app
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgresql://myuser:mypassword@db:5432/mydb
    ports:
      - "8080:8080"   # expose uniquement l'API web

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mypassword
      - POSTGRES_DB=mydb
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:
  • Par défaut Compose crée un réseau bridge unique. Déclarer plusieurs réseaux permet :
    • Isolation (front ↔ back) : la BDD n’est pas accessible depuis l’extérieur du back-end.
    • Tests parallèles : lancez plusieurs stacks qui ne se « voient » pas.
    networks:
      front:
      back:
    services:
      web:
        networks: [front, back]
      db:
        networks: [back]
    		
  • Alias & DNS: chaque service reçoit un alias = son nom + réseau (db.front, db.back). Vous pouvez en ajouter d’autres :
    networks:
        back:
          aliases: [postgresql]			
    			

Comment utiliser les variables d'environnement dans Docker ?

Pour plus de flexibilité et de sécurité, il est préférable d’utiliser des variables d'environnement pour stocker les informations sensibles.
Ainsi, elles ne seront pas présentes dans le fichier compose.yml de Docker.

environment:
  POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
 
Ensuite, dans un fichier .env, ajoutez le nom de la variable ainsi que son contenu : le mot de passe.
POSTGRES_PASSWORD=supersecret
Ajoutez .env à votre .gitignore pour éviter les fuites d’infos sensibles.

Réutiliser des blocs grâce aux ancres

Les ancres permettent de réutiliser des blocs que l’on répète plusieurs fois dans le fichier compose.yml.

services:
  api:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes: *code

  worker:
    image: alpine:3.20
    command: sh -c "ls -la /app && sleep 3600"
    volumes: *code

x-volumes:
  code: &code
    - ./app:/app

  • &code crée l’ancre (la “liste de volumes”).
  • *code la réutilise dans chaque service.
  • ./app:/app est le volume (un bind mount) partagé par api et worker.
Tu peux lancer :
docker compose up

Vérifier la configuration du fichier compose.yml

Docker compose config affiche la configuration “finale” que Docker Compose va réellement utiliser, après avoir :

  • fusionné tes fichiers (docker-compose.yml + docker-compose.override.yml + -f docker-compose.prod.yml, etc.)
  • appliqué les overrides
  • interpolé les variables d’environnement (${VAR}) sauf si tu lui demandes de ne pas le faire
  • normalisé/résolu certains chemins/valeurs
C’est l’outil n°1 pour débugger : “Pourquoi ce port est ouvert ? Pourquoi ce volume est monté ? Quelle commande est réellement lancée ?” (Docker Documentation)

Nous allons créer un compose.yml pour l'exemple :
x-volumes:
  common: &common_volumes
    # 1) Code local monté dans les conteneurs (dev / hot reload)
    - type: bind
      source: ./api
      target: /app

    # 2) Cache pip partagé entre services (accélère les rebuilds)
    - type: volume
      source: pip-cache
      target: /root/.cache/pip

    # 3) Logs partagés (pratique pour centraliser)
    - type: volume
      source: app-logs
      target: /var/log/myapp

services:
  api:
    build: ./api
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
    ports:
      - "8000:8000"
    volumes: *common_volumes
    environment:
      - APP_ENV=dev

  worker:
    build: ./api
    command: python -m worker
    volumes: *common_volumes
    environment:
      - APP_ENV=dev

volumes:
  pip-cache:
  app-logs:
'docker compose config' donne
services:
  api:
    build:
      context: /home/user-ubuntu/Public/test/api
      dockerfile: Dockerfile
    command:
      - uvicorn
      - main:app
      - --host
      - 0.0.0.0
      - --port
      - "8000"
      - --reload
    environment:
      APP_ENV: dev
    networks:
      default: null
    ports:
      - mode: ingress
        target: 8000
        published: "8000"
        protocol: tcp
    volumes:
      - type: bind
        source: /home/user-ubuntu/Public/test/api
        target: /app
      - type: volume
        source: pip-cache
        target: /root/.cache/pip
      - type: volume
        source: app-logs
        target: /var/log/myapp
  worker:
    build:
      context: /home/user-ubuntu/Public/test/api
      dockerfile: Dockerfile
    command:
      - python
      - -m
      - worker
    environment:
      APP_ENV: dev
    networks:
      default: null
    volumes:
      - type: bind
        source: /home/user-ubuntu/Public/test/api
        target: /app
      - type: volume
        source: pip-cache
        target: /root/.cache/pip
      - type: volume
        source: app-logs
        target: /var/log/myapp
networks:
  default:
    name: test_default
volumes:
  app-logs:
    name: test_app-logs
  pip-cache:
    name: test_pip-cache
x-volumes:
  common:
    - source: ./api
      target: /app
      type: bind
    - source: pip-cache
      target: /root/.cache/pip
      type: volume
    - source: app-logs
      target: /var/log/myapp
      type: volume

Utiliser les profils pour gérer des services conditionnels

x-volumes:
  common: &common_volumes
    # 1) Code local monté dans les conteneurs (dev / hot reload)
    - type: bind
      source: ./api
      target: /app

    # 2) Cache pip partagé entre services (accélère les rebuilds)
    - type: volume
      source: pip-cache
      target: /root/.cache/pip

    # 3) Logs partagés (pratique pour centraliser)
    - type: volume
      source: app-logs
      target: /var/log/myapp

services:
  api:
    build: ./api
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
    ports:
      - "8000:8000"
    volumes: *common_volumes
    environment:
      - APP_ENV=dev

  worker:
    build: ./api
    command: python -m worker
    volumes: *common_volumes
    environment:
      - APP_ENV=dev

  tests:
    build: ./api
    profiles: ["tests"]
    command: pytest -q
    volumes: *common_volumes


volumes:
  pip-cache:
  app-logs:

Sans profile :
docker compose config n’inclut pas le service tests (parce qu’il a profiles: ["tests"]).
Avec --profile tests :
docker compose config inclut le service tests dans la sortie YAML finale.
Les services api et worker restent présents (eux n’ont pas de profile), donc ils restent dans la config.
Donc le résultat attendu
api ✅ présent
worker ✅ présent
tests ✅ présent uniquement avec --profile tests
Une fois corrigé :
docker compose up → tests absent
docker compose --profile tests up → tests présent

Utiliser plusieurs configurations

docker-compose.yml (base commune)
services:
  api:
    build: ./api
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/app
    depends_on:
      - db
    ports:
      - "8000:8000"

  db:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=app
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

docker-compose.override.yml (DEV)
Chargé automatiquement quand on fait docker compose up (si le fichier existe).
services:
  api:
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
    volumes:
      - ./api:/app
    environment:
      - APP_ENV=dev

lancer en dev :
docker compose up --build
docker-compose.prod.yml (PROD : gunicorn, durci, restart)
services:
  api:
    image: my-api:1.0.0
    command: gunicorn -k uvicorn.workers.UvicornWorker -w 2 -b 0.0.0.0:8000 main:app
    environment:
      - APP_ENV=prod
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /tmp

Lancer en prod (en empilant les fichiers) :
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
La règle importante :
Quand on fait :
docker compose -f A -f B config
➡️ B écrase A quand il y a conflit (ex: command, environment, ports, etc.).

Exemples de Piles d'applications

Docker Compose est utilisé pour déployer une vaste gamme de piles d'applications, allant des environnements de développement classiques aux systèmes plus complexes d'ingénierie des données.
Les sources fournissent de nombreux exemples de piles d'applications web et de services intégrés facilités par Docker Compose, notamment :

Type de Pile d'Application Web Technologies impliquées
Piles Web Classiques Flask (Python) + MySQL : Application web simple multi-conteneurs.
PHP + MySQL : Serveur web Apache/PHP lié à un conteneur MySQL.
Node.js + MySQL : Application Node.js avec base de données MySQL.
NGINX + Flask + MongoDB/MySQL : Proxy Nginx avec un backend Flask et différentes bases de données.
NGINX + Node.js + Redis : Serveur Node.js avec proxy Nginx et Redis.
Applications Complètes WordPress + MySQL : Lancement d'un blog WordPress complet avec sa base de données.
ASP.NET + MS-SQL : Application ASP.NET Core avec un serveur MS SQL.
Environnements de Développement Jupyter Notebook : Création d'un environnement de science des données reproductible.
FastAPI/API de Science des Données : Déploiement d'une API de prédiction basée sur un modèle d'apprentissage automatique.
Pipes de Données et DevOps Apache Airflow + PostgreSQL : Configuration d'un environnement d'automatisation de pipelines de données.
Prometheus + Grafana : Configuration de base pour la surveillance.

Exemples de service discovery avec Docker Compose

Service discovery = mécanisme qui permet à un conteneur de trouver un autre conteneur par nom (plutôt que par IP fixe).
Docker Compose crée un réseau interne pour ton projet et fournit un DNS embarqué : quand un conteneur fait nslookup db, Docker résout db en l’adresse IP du conteneur (ou des conteneurs) du service db.
Autrement dit : les services se découvrent par leur service name.

version: '3.9'

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: pg
      POSTGRES_PASSWORD: pg
      POSTGRES_DB: app
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U pg -d app"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

  web:
    image: curlimages/curl:8
    depends_on:
      db:
        condition: service_healthy
    command: sh -c "
      echo '🔍 Résolution DNS :' &&
      nslookup db &&
      echo '\n💬  Ping DB :' &&
      ping -c 3 db
    "		
			
docker compose up		
		

pg_isready est un petit utilitaire fourni avec PostgreSQL qui teste rapidement si un serveur PostgreSQL est prêt à accepter des connexions TCP/UNIX.
Il ne lance pas une session SQL complète : il vérifie juste la possibilité d’établir une connexion.
healthcheck permet à Docker de marquer db comme healthy quand Postgres est prêt.
depends_on avec service_healthy attend cette santé avant de lancer web.

Scaling + Round-Robin : plusieurs IP pour un nom de service

services:
  api:
    image: hashicorp/http-echo       # tiny server qui renvoie “hello”
    command: ["-text", "hello from $(hostname)"]
    expose: ["5678"]                 # pas besoin de publier en dehors du réseau Compose

  inspector:
    image: busybox:uclibc
    command: sh -c "
      echo '\n DNS Round-Robin :' &&
      for i in $(seq 1 6); do
        dig +short api | head -n1
        sleep 1
      done
    "
    depends_on:
      api:
        condition: service_started
		
docker compose up -d --scale api=3      # ← 3 instances « api »
docker compose logs -f inspector		
		
  • docker compose up -d --scale api=3 lance trois conteneurs distincts api_1, api_2, api_3 sur le même réseau de projet.
    Chacun reçoit une IP unique sur ce réseau interne.
  • Le DNS interne de Docker (accessible depuis les conteneurs) résout le nom de service api en plusieurs enregistrements A — une IP par instance en cours d’exécution.
  • Ce n’est pas un load-balancer applicatif. Docker renvoie plusieurs IPs (ou tourne l’ordre), mais le client décide laquelle il contactera.
  • Beaucoup de clients résolvent le nom une seule fois puis réutilisent l’IP (caching, keep-alive, résolveurs système).
    Dans ces cas, tu n’auras pas de vraie distribution de charge entre les instances.

“Voting App” : le Hello World incontournable du micro-service Docker Compose

La Voting App est un mini-système distribué imaginé par l’équipe Docker pour illustrer, en moins de 200 lignes de YAML et de code Python/Node, tout le cycle d’une appli micro-services :

Explication Voting App
Rôle Tech par défaut Mission
vote (front) Python + Flask (ou Node/React selon variantes) Formulaire vote “Cats vs Dogs”
redis (queue) Redis Stocke chaque vote en mémoire (latence < 1 ms)
worker .NET, Python ou Java Défile la queue Redis → écrit en base SQL
db PostgreSQL Persistance définitive des votes
result (back-office) Node + Express + Socket.io Graphique temps réel des scores
ervices:
  vote:
    image: dockersamples/examplevotingapp_vote:latest
    depends_on:
      redis:
        condition: service_healthy
    ports: ["5000:80"]

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 3

  worker:
    image: dockersamples/examplevotingapp_worker:latest
    depends_on:
      redis:
        condition: service_healthy
      db:
        condition: service_healthy

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "postgres"
    volumes:
      - votes-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      retries: 5

  result:
    image: dockersamples/examplevotingapp_result:latest
    depends_on:
      db:
        condition: service_healthy
    ports: ["5001:80"]

volumes:
  votes-data:  
   

Explication du Docker Compose - Voting App

Cette application de vote est un exemple classique d'architecture microservices.
Voici comment elle fonctionne :

Architecture générale

L'application permet aux utilisateurs de voter entre deux options, et de voir les résultats en temps réel.
Elle est composée de 5 services qui communiquent entre eux.

Les services

  1. Vote (Interface de vote)
    • Conteneur qui expose l'interface web pour voter
      Accessible sur `http://localhost:5000`
      Dépend de Redis pour stocker les votes temporairement
      Attend que Redis soit "healthy" avant de démarrer
  2. Redis (Cache/File de messages)
    • Base de données en mémoire qui sert de file d'attente
      Stocke temporairement les votes avant qu'ils soient traités
      Le healthcheck vérifie toutes les 5 secondes que Redis répond au ping
      Utilise l'image Alpine (version légère)
  3. Worker (Traitement des votes)
    • Service en arrière-plan qui traite les votes
      Récupère les votes depuis Redis
      Les enregistre définitivement dans PostgreSQL
      Dépend à la fois de Redis et PostgreSQL (attend que les deux soient healthy)
  4. DB (Base de données PostgreSQL)
    • Stocke les résultats finaux des votes de manière persistante
      Les données sont sauvegardées dans un volume Docker `votes-data`
      Le healthcheck vérifie que PostgreSQL est prêt à accepter des connexions
      Configuré avec les identifiants postgres/postgres
  5. Result (Interface des résultats)
    • Conteneur qui affiche les résultats en temps réel
      Accessible sur `http://localhost:5001`
      Lit directement les données depuis PostgreSQL
      Attend que la base de données soit healthy

Flux de données

  1. L'utilisateur vote via l'interface **vote** (port 5000)
  2. Le vote est stocké temporairement dans **Redis**
  3. Le **worker** récupère le vote depuis Redis
  4. Le **worker** l'enregistre dans la base **PostgreSQL**
  5. L'interface **result** (port 5001) affiche les résultats depuis PostgreSQL

Points techniques importants

C'est une architecture découplée typique où chaque composant a une responsabilité unique et peut être scalé indépendamment.

  1. Service Discovery natif :
    • vote poste son JSON sur redis:6379 sans connaître son IP.
    • worker lit redis, écrit sur db:5432.
    • Tout repose sur la résolution DNS interne fournie par Compose.
  2. depends_on avec condition: Les services attendent que leurs dépendances soient vraiment opérationnelles (pas juste démarrées)
  3. healthchecks: Permettent de vérifier qu'un service est fonctionnel avant que d'autres services ne tentent de s'y connecter
  4. Volume nommé: `votes-data` persiste les données même si les conteneurs sont supprimés
  5. Ports mappés: 5000 et 5001 sur l'hôte vers le port 80 dans les conteneurs
# lancement
docker compose up -d           # <5 s et c'est en ligne

# logs agrégés
docker compose logs -f --tail=50

# ouvrir les apps
xdg-open http://localhost:5000      # front “Vote”
xdg-open http://localhost:5001      # dashboard “Result”

# simuler une montée en charge
docker compose up -d --scale vote=4 --scale worker=3

# inspection DNS round-robin
docker compose exec vote getent hosts worker	
	

Chaque résolution du nom worker renvoie tour à tour les 3 IP des workers – round-robin automatique, sans LB externe.