Docker Compose et optimisation pour la production
🎯 Objectifs
- Construire des images légères avec les multi-stage builds
- Orchestrer plusieurs conteneurs avec Docker Compose
- Optimiser la taille et la sécurité des images
- Mettre en place des pratiques de production
📋 Prérequis
- Docker Intermédiaire - Dockerfile, volumes, réseaux
- Docker et Docker Compose installés
🏗️ Multi-stage Builds
Pourquoi ?
Un build classique inclut les outils de compilation dans l'image finale. Un multi-stage build sépare la phase de build de la phase de production pour obtenir une image minimale.
Exemple : application Node.js
# Étape 1 : Build
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Étape 2 : Production
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/package*.json ./
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]Comparaison de taille
| Approche | Taille approximative |
|---|---|
| Build classique (tout inclus) | ~800 Mo |
| Multi-stage | ~150 Mo |
💡 Moins de fichiers = surface d'attaque réduite + déploiement plus rapide.
🐙 Docker Compose
Pourquoi ?
Gérer plusieurs conteneurs avec docker run devient vite ingérable. Compose décrit toute l'infrastructure dans un fichier YAML.
Exemple complet
# docker-compose.yml
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/app
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
restart: unless-stopped
volumes:
pgdata:Commandes essentielles
docker compose up -d # Lancer en arrière-plan
docker compose down # Arrêter et supprimer
docker compose ps # Lister les services
docker compose logs -f # Suivre les logs
docker compose build # Reconstruire les images
docker compose exec web sh # Shell dans un service
docker compose restart web # Redémarrer un serviceOptions utiles
| Option | Rôle | Exemple |
|---|---|---|
depends_on | Ordre de démarrage | depends_on: [db, redis] |
restart | Politique de redémarrage | unless-stopped, always |
build | Construire depuis un Dockerfile | build: ./app |
env_file | Charger des variables depuis un fichier | env_file: .env |
healthcheck | Vérification de santé | Voir section suivante |
Variables d'environnement
Inline dans le YAML ou via un fichier .env :
# .env
POSTGRES_USER=user
POSTGRES_PASSWORD=secretservices:
db:
image: postgres:16-alpine
env_file: .env⚠️ Ne versionnez jamais le fichier
.envdans Git. Ajoutez-le à.gitignore.
🔬 Docker Compose Avancé
Profiles
Activez certains services uniquement quand nécessaire :
services:
web:
build: .
ports: ["3000:3000"]
debug:
image: busybox
profiles: ["dev"]
command: sleep infinitydocker compose --profile dev up -d # Active le service debugHealth checks
services:
web:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10sFichiers override
Docker Compose fusionne automatiquement docker-compose.yml et docker-compose.override.yml :
# docker-compose.override.yml - surcharges pour le dev
services:
web:
volumes:
- ./src:/app/src
environment:
- NODE_ENV=development💡 Utilisez
overridepour le dev et le fichier principal pour la production.
⚡ Optimisation des Images
Choisir une image de base minimale
# ❌ Image complète (~900 Mo)
FROM node:20
# ✅ Variante Alpine (~130 Mo)
FROM node:20-alpineRéduire les couches
# ❌ Plusieurs couches
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# ✅ Une seule couche
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*Utiliser .dockerignore
node_modules
.git
*.log
.envComparaison de tailles
| Image de base | Taille |
|---|---|
ubuntu:22.04 | ~77 Mo |
debian:bookworm-slim | ~52 Mo |
alpine:3.19 | ~7 Mo |
node:20 | ~900 Mo |
node:20-slim | ~200 Mo |
node:20-alpine | ~130 Mo |
🔒 Sécurité Docker
Utilisateur non-root
Par défaut, les conteneurs tournent en root. C'est un risque en cas d'évasion :
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuserSystème de fichiers en lecture seule
docker run --read-only --tmpfs /tmp mon-appScanner les vulnérabilités
docker scout cves mon-app:latestRègles essentielles
- Ne stockez jamais de secrets (mots de passe, clés API) dans une image
- Utilisez les secrets Docker ou des variables d'environnement injectées au runtime
- Mettez à jour régulièrement vos images de base
- Scannez vos images avant chaque déploiement
⚠️ Un conteneur
rootcompromis peut potentiellement accéder au système hôte.
🧹 Nettoyage et Maintenance
Docker accumule des ressources inutilisées. Vérifiez l'espace utilisé :
docker system dfNettoyage ciblé
docker container prune # Conteneurs arrêtés
docker image prune # Images non utilisées
docker volume prune # Volumes orphelins
docker network prune # Réseaux inutilisésNettoyage global
docker system prune -a --volumes⚠️ Cette commande supprime tout ce qui n'est pas utilisé par un conteneur actif : images, conteneurs, volumes et réseaux. Vérifiez avant d'exécuter.
📌 Points Clés
- Les multi-stage builds réduisent drastiquement la taille des images
- Docker Compose simplifie l'orchestration multi-conteneurs avec un fichier YAML
- Choisir une image de base minimale (alpine) améliore sécurité et performance
- Toujours exécuter les conteneurs avec un utilisateur non-root en production
- Nettoyez régulièrement avec
docker system prune
📚 Ressources
🚀 Prochaines étapes
Vous gérez Docker en production. Automatisez maintenant vos déploiements :
- Créer son premier pipeline CI/CD avec GitHub Actions - Premiers workflows CI/CD avec GitHub Actions