Une image Docker mal construite peut être volumineuse, lente à puller, vulnérable ou exécuter des process en root. Ces problèmes sont tous évitables avec quelques bonnes pratiques.
Multi-stage builds : images de production sans les outils de build
# Stage 1 : build (avec toutes les dépendances de dev)
FROM python:3.12 AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# Stage 2 : image finale (sans pip, sans compilateurs)
FROM python:3.12-slim AS production
WORKDIR /app
# Copier uniquement les dépendances installées depuis le builder
COPY --from=builder /root/.local /root/.local
# Créer un utilisateur non-root
RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
COPY --chown=appuser:appgroup src/ ./
USER appuser
EXPOSE 8080
CMD ["python", "-m", "gunicorn", "app:create_app()", "--bind", "0.0.0.0:8080"]
python:3.12 → 1.0 GB
python:3.12-slim → 130 MB
Image finale → 165 MB (slim + dépendances Python)
Ordonner les layers pour maximiser le cache
Docker met en cache chaque layer. Si un layer change, tous les layers suivants sont reconstruits. L’ordre optimal : du plus stable (rarement modifié) au plus volatile (souvent modifié).
# ❌ Le code change souvent → invalide le cache des dépendances
FROM python:3.12-slim
COPY . /app # code + requirements ensemble
RUN pip install -r requirements.txt
# ✅ Séparer les dépendances du code
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt . # rarement modifié → layer stable
RUN pip install -r requirements.txt
COPY src/ . # fréquemment modifié → dernier layer
.dockerignore : exclure ce qui ne doit pas être dans l’image
# .dockerignore
**/__pycache__
*.pyc
*.pyo
.git
.gitignore
.env
.env.*
tests/
docs/
*.md
node_modules/
dist/
coverage/
.pytest_cache/
Sans .dockerignore, tout le dossier courant est envoyé au daemon Docker — y compris .git (peut contenir des secrets dans l’historique), node_modules (centaines de MB), et vos fichiers .env.
Base images : choisir la plus petite qui convient
ubuntu:22.04 → 77 MB (usage général, apt disponible)
debian:12-slim → 75 MB (sans les outils non essentiels)
python:3.12-slim → 130 MB (Debian slim + Python)
python:3.12-alpine → 55 MB (Alpine Linux, musl libc)
distroless/python3 → 52 MB (sans shell, sans apt, plus sécurisé)
Alpine est tentant pour sa taille, mais musl libc peut causer des problèmes avec certaines bibliothèques C (notamment des performances dégradées avec Python et Go dans certains cas). Pour la production, slim est souvent le meilleur compromis.
Scanner les vulnérabilités avant de pousser
# Trivy : scanner local avant le push
trivy image --severity HIGH,CRITICAL myapp:1.0
# Intégration CI/CD (GitHub Actions)
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: HIGH,CRITICAL
exit-code: 1 # fail le pipeline si vulnérabilité critique
Notre formation Docker couvre la construction d’images production-ready avec des ateliers pratiques.