🐳 Module Docker — Semaine de consolidation

Consolider Docker avec Bootcode Hub

Backend · Frontend · Base de donnĂ©es — Dev & Prod

Utilisez les flĂšches, cliquez ou glissez pour naviguer

Objectifs de la leçon

1. ConnaĂźtre les patterns Docker de production

Restart, logging, resource limits

2. Utiliser les profiles Docker Compose

Séparer dev et prod avec --profile

3. Configurer docker-compose.override.yml

Surcharge automatique pour le développement

4. Organiser un projet Docker dev/prod

Configurations distinctes et workflow

5. Synthétiser toutes les connaissances Docker de la semaine

docker run → Dockerfile → Compose → Volumes/RĂ©seaux → Multi-stage → Production

Plan du cours

1

Récap rapide de la semaine

docker run → Dockerfile → Compose → Volumes/RĂ©seaux → Multi-stage

2

Patterns de production

Restart policies, logging, resource limits, labels

3

Docker Compose profiles

Séparer les services de dev (adminer, debug) et de prod

4

Dev vs Prod : deux configurations Compose

docker-compose.override.yml vs -f explicite

5

Mini-projet : Conteneuriser Bootcode Hub

Backend + Frontend + Base de données en dev ET prod

Module 1

Récap rapide de la semaine

docker run → Dockerfile → Compose → Volumes → Multi-stage

1

docker run

2

Dockerfile

3

Compose

4

Volumes & Réseaux

5

Multi-stage

1. docker run — Les bases

Lancer un conteneur Ă  partir d'une image

docker run -d --name mon-app -p 3000:3000 -e NODE_ENV=production mon-image

// -d = détaché, -p = port mapping, -e = variable d'env

📩 Commandes essentielles

  • docker ps — lister les conteneurs
  • docker logs -f — suivre les logs
  • docker exec -it — entrer dans un conteneur
  • docker stop / rm — arrĂȘter / supprimer

🔗 Images essentielles

  • node:20-alpine — 120 Mo
  • postgres:16-alpine — 160 Mo
  • nginx:alpine — 25 Mo

2. Dockerfile — Construire son image

Chaque instruction crée une layer (couche)

FROM node:20-alpine

WORKDIR /app

# Copier les dépendances d'abord

COPY package*.json ./

RUN npm ci

# Puis le code source

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

Bonnes pratiques :

  • ✅ .dockerignore — exclure node_modules
  • ✅ Copier d'abord package.json (cache)
  • ✅ Images Alpine = plus petites
  • ✅ Épingler les versions (node:20-alpine)

3. Docker Compose — Orchestrer

Un fichier YAML pour lancer tous les services en une commande

# docker-compose.yml

services:

api:

build: ./backend

ports: ["3000:3000"]

db:

image: postgres:16-alpine

environment:

POSTGRES_PASSWORD: ${DB_PASSWORD}

docker compose up -d

Tout lancer

docker compose logs -f

Suivre les logs

docker compose down

Tout arrĂȘter

4. Volumes & Réseaux

đŸ’Ÿ Persistance des donnĂ©es

services:

db:

volumes:

- pgdata:/var/lib/postgresql/data

volumes:

pgdata:

bind mount (dev) :

- ./src:/app/src

volume nommé (prod) :

- pgdata:/var/lib/postgresql/data

🌐 Communication entre services

services:

api:

networks:

- app-net

networks:

app-net:

Dans le code, on utilise le nom du service comme hostname :

postgresql://user:pass@db:5432/mydb

5. Multi-stage builds

Un seul Dockerfile, deux images : builder + runtime

✅ Image finale optimisĂ©e

FROM node:20-alpine AS builder

COPY package*.json ./

RUN npm ci

COPY . .

RUN npm run build

# --- seconde étape ---

FROM node:20-alpine

COPY --from=builder /app/dist ./dist

CMD ["node", "dist/server.js"]

📊 RĂ©sultat

Sans multi-stage : ~1.2 Go

Avec multi-stage : ~150 Mo

💡 Principe

Builder compile + installe les dĂ©pendances de dev → seule la sortie utile (dist/) va dans l'image finale

Module 2

Patterns de production

Restart · Logging · Resource limits · Labels

La différence entre un service qui tient la route et un service qui crashe sans prévenir

Restart policies

Que se passe-t-il quand le conteneur crashe ?

services:

api:

restart: unless-stopped # ← À utiliser en production

đŸš« Les 3 autres options

  • no — ne redĂ©marre pas (dĂ©faut)
  • on-failure[:max] — seulement si code != 0
  • always — toujours, mĂȘme aprĂšs docker stop

✅ La rùgle d'or

restart: unless-stopped

Redémarre aprÚs crash, mais respecte docker stop

L'admin arrĂȘte proprement → pas de restart. Crash → redĂ©marrage auto.

Logging en production

Les logs = la mémoire de votre application

services:

api:

logging:

driver: json-file

options:

max-size: "10m" # Rotation tous les 10 Mo

max-file: "3" # Garde 3 fichiers

📄 json-file

Défaut, rotation configurable

🌐 syslog / gelf

Centralisation externe

đŸš« local / none

Pas de logs. À Ă©viter en prod

⚠ Sans rotation, les logs peuvent remplir le disque et faire planter le serveur !

Resource limits

Un conteneur ne doit pas pouvoir tout consommer

services:

api:

deploy:

resources:

limits:

memory: 256M

cpus: "0.5"

reservations:

memory: 128M

⚠ Sans limites

Un bug mémoire peut saturer le CPU et faire tomber tous les autres services

✅ Bonnes pratiques

  • Toujours fixer memory
  • Adapter cpus selon le besoin
  • Utiliser reservations pour garantir

Labels — MĂ©tadonnĂ©es

Organiser et documenter ses services

services:

api:

labels:

- "app.name=bootcode-hub"

- "app.environment=production"

- "app.team=backend"

- "traefik.enable=true"

đŸ·ïž Organisation

Filtrer / trier les conteneurs

🔌 Reverse proxy

Traefik / Nginx utilisent les labels

📊 Monitoring

Prometheus, Grafana s'en servent

Module 3

Docker Compose Profiles

Un seul docker-compose.yml, plusieurs configurations

Lancer certains services seulement en dev, d'autres en prod

Comment ça marche ?

Un service peut appartenir Ă  un ou plusieurs profiles

services:

api:

image: bootcode-api

profiles: ["dev", "prod"]

adminer:

image: adminer

profiles: ["dev"] # ← Dev seulement !

docker compose --profile dev up

Lance tous les services du profile "dev"

docker compose --profile prod up

Lance seulement les services de prod

Services typiques en dev

Des outils qu'on ne veut PAS en production

đŸ—„ïž Adminer

Interface web pour PostgreSQL/MySQL

adminer:

image: adminer

profiles: ["dev"]

ports: ["8080:8080"]

📧 MailHog

Capture les emails en dev (SMTP + UI)

mailhog:

image: mailhog/mailhog

profiles: ["dev"]

🐞 Debug tools

Redis Commander, pgAdmin, etc.

redis-commander:

profiles: ["dev"]

🔄 Hot Reload

Bind mounts + nodemon/vite watch

api:

volumes:

- ./backend:/app

command: npx nodemon

Combiner les profiles

On peut activer plusieurs profiles Ă  la fois

# Lancer la stack complĂšte de dev

docker compose --profile dev up -d

# Lancer seulement la prod

docker compose --profile prod up -d

# Lancer les deux (utile en CI)

docker compose --profile dev --profile prod up -d

💡 Cas d'usage

En CI, on lance le profile "prod" pour builder et tester l'image finale. En local, on lance "dev" pour avoir tous les outils de debug.

Module 4

Dev vs Prod

Deux configurations Compose pour deux contextes

docker-compose.override.yml

Automatiquement chargĂ© par Docker Compose — parfait pour le dev !

📄 docker-compose.yml

services:

api:

build: ./backend

ports: ["3000:3000"]

env_file: .env

📄 docker-compose.override.yml

services:

api:

volumes:

- ./backend:/app

command: npx nodemon

⚠ L'override est chargĂ© automatiquement sans flag -f

Quand vous lancez docker compose up -d, Compose fusionne les deux fichiers

Production : docker-compose.prod.yml

Un fichier séparé, chargé explicitement avec -f

# docker-compose.prod.yml

services:

api:

build:

context: ./backend

target: production # ← multi-stage: Ă©tape prod

restart: unless-stopped

logging:

options:

max-size: "10m"

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

On ignore volontairement le fichier override

Dev vs Prod : Tableau comparatif

CritĂšre Dev Prod
Build Bind mount du code source Multi-stage build, image figée
Hot reload nodemon / vite Non (process manager type PM2)
Restart no (ou on-failure) unless-stopped
Logs Par défaut (stdout) Rotation configurée (10 Mo)
Limites Aucune 256M RAM / 0.5 CPU
.env .env.dev .env.prod
Outils Adminer, MailHog, debug Aucun outil superflu

Module 5 — Mini-projet

Conteneuriser Bootcode Hub

Backend + Frontend + Base de donnĂ©es — Dev ET Prod

👉 Objectif : Une stack complĂšte, prĂȘte pour le dĂ©veloppement ET le dĂ©ploiement

Architecture du projet

🌐 Frontend

React / Vite

Port 5173

→

⚙ Backend API

Node.js / Express

Port 3000

→

đŸ—„ïž PostgreSQL

Port 5432

📁 frontend/

  • Dockerfile (multi-stage)
  • Nginx pour servir le build

📁 backend/

  • Dockerfile (multi-stage)
  • Express API server

📁 docker/

  • docker-compose.yml
  • docker-compose.prod.yml
  • .env.dev / .env.prod

docker-compose.yml (base commune)

services:

backend:

build: ./backend

ports: ["3000:3000"]

depends_on:

db:

condition: service_healthy

env_file: .env

frontend:

build: ./frontend

ports: ["80:80"]

depends_on: [backend]

db:

image: postgres:16-alpine

volumes: [pgdata:/var/lib/postgresql/data]

healthcheck:

test: ["CMD-SHELL", "pg_isready"]

volumes:

pgdata:

docker-compose.override.yml (dev)

Automatiquement chargĂ© — bind mounts + hot reload + outils dev

services:

backend:

volumes:

- ./backend:/app

- /app/node_modules # ne pas écraser

command: npx nodemon src/server.js

env_file: .env.dev

frontend:

volumes:

- ./frontend:/app

command: npm run dev -- --host

ports: ["5173:5173"]

adminer:

image: adminer

ports: ["8080:8080"]

docker-compose.prod.yml

Explicitement chargé avec -f

services:

backend:

build:

context: ./backend

target: production

restart: unless-stopped

env_file: .env.prod

logging:

options:

max-size: "10m"

max-file: "3"

deploy:

resources:

limits:

memory: "256M"

cpus: "0.5"

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Pas d'override, pas de bind mounts, pas d'outils dev

Workflow développement

1ïžâƒŁ

Cloner le projet

git clone ... && cd bootcode-hub

2ïžâƒŁ

Copier .env.dev → .env

cp .env.dev .env && docker compose up -d

3ïžâƒŁ

Coder avec hot reload

Les changements sont détectés via les bind mounts

4ïžâƒŁ

Adminer sur http://localhost:8080

Pour inspecter la base de données

Workflow production

1ïžâƒŁ

Builder les images

docker compose -f docker-compose.prod.yml build

2ïžâƒŁ

Pousser vers le registry

docker tag bootcode-api registry.example.com/api:v1.0

docker push registry.example.com/api:v1.0

3ïžâƒŁ

Déployer sur le serveur

scp docker-compose.prod.yml .env.prod server:/app/

ssh server "docker compose -f docker-compose.prod.yml up -d"

⚠ Toujours builder AVANT de dĂ©ployer ! Les bind mounts de dev ne marchent pas en prod.

PiÚge n°1 : Pas de restart policy

❌ L'erreur

services:

api:

image: mon-api

# pas de restart policy

→ Le service crashe à 3h du matin

→ Personne ne le relance → downtime

✅ La solution

services:

api:

image: mon-api

restart: unless-stopped

→ RedĂ©marrage automatique aprĂšs crash

→ Et on peut toujours faire docker stop

PiĂšge n°2 : MĂȘmes credentials dev/prod

❌ L'erreur

# .env (dev ET prod)

DB_PASSWORD=password123

→ Dev utilise le mĂȘme mot de passe que prod

→ Faille de sĂ©curitĂ© monumentale

✅ La solution

# .env.dev

DB_PASSWORD=dev_password_local

# .env.prod (jamais commité!)

DB_PASSWORD=aB7!xK9#mP2$

# .gitignore

.env.prod

PiÚge n°3 : Oublier de builder avant de déployer

Le scénario catastrophe

  1. En dev, on utilise des bind mounts
  2. On pousse le docker-compose.yml sur le serveur
  3. On lance docker compose up -d
  4. Le serveur monte son dossier vide dans le conteneur → đŸ’„

✅ La solution

  • Utiliser docker compose build avant de dĂ©ployer
  • Ou utiliser une CI qui build l'image et la pousse dans un registry
  • Sur le serveur, utiliser docker compose pull

PiÚge n°4 : override.yml vs -f explicite

❌ La confusion

Les étudiants pensent que override et -f sont interchangeables

docker compose -f docker-compose.override.yml up

// N'inclut PAS docker-compose.yml !

✅ La rĂ©alitĂ©

docker compose up

→ Charge docker-compose.yml + override

docker compose -f docker-compose.prod.yml up

→ Charge SEULEMENT le fichier prod

docker compose -f docker-compose.yml -f docker-compose.prod.yml up

→ Charge les deux, PAS l'override

Points clés à retenir

🔁 En production, toujours restart: unless-stopped

Le service redĂ©marre aprĂšs un crash, mais pas aprĂšs un arrĂȘt volontaire

🎭 Les profiles isolent les services de dev (adminer, mailhog)

Avec --profile dev / --profile prod

📄 docker-compose.override.yml est chargĂ© automatiquement

Parfait pour les configs de dev sans avoir à spécifier de flag

đŸ—ïž Workflow rĂ©el : dev avec bind mounts → CI avec multi-stage → prod avec compose

Chaque environnement a sa configuration adaptée

Récapitulatif de la semaine Docker

docker run

Lancer des conteneurs

-d, -p, -e, --name

Dockerfile

Construire des images

FROM, COPY, RUN, CMD

Compose

Orchestrer multi-services

YAML, services, networks

Volumes

Persister les données

Volumes nommés, bind mounts

Multi-stage

Images optimisées

Builder + Runtime

Production

Restart, logs, limits

Patterns professionnels

🎯 Mini-projet : Consigne

Conteneuriser Bootcode Hub avec deux configurations

1. Structure du projet

bootcode-hub/

├── backend/

│ ├── Dockerfile (multi-stage)

│ └── src/

├── frontend/

│ ├── Dockerfile (multi-stage)

│ └── src/

├── docker-compose.yml

├── docker-compose.override.yml

├── docker-compose.prod.yml

├── .env.dev

└── .env.prod (ignorĂ© par git)

2. Contraintes

  • Health check sur PostgreSQL
  • Multi-stage build pour backend ET frontend
  • Bind mounts + hot reload en dev
  • Restart unless-stopped + logging + limits en prod
  • Adminer uniquement en dev
  • Fichiers .env diffĂ©rents pour dev et prod

Questions ?

Vous savez maintenant dockeriser un projet complet

Rappel :

docker compose --profile dev up pour dev
docker compose -f docker-compose.prod.yml up pour prod