1. Multi-stage Builds
Un build multi-stage utilise plusieurs FROM dans un même Dockerfile. Seul le dernier stage est gardé — les outils de build (compilateurs, node_modules de dev) n'arrivent jamais en production.
# Dockerfile multi-stage Node.js
# Stage 1 : Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # installe TOUT (dev deps inclus)
COPY . .
RUN npm run build # compile le frontend, transpile TS...
# Stage 2 : Production (image finale)
FROM node:18-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production # seulement les deps de prod
COPY --from=builder /app/dist ./dist # copier le build
USER node
CMD ["node", "dist/server.js"]
# Résultat : image finale sans les 800 MB de node_modules de dev !
2. Choisir des images légères
| Base image | Taille | Shell | Package manager |
|---|---|---|---|
node:18 | ~950 MB | bash | apt (Debian) |
node:18-slim | ~240 MB | bash | apt (minimal) |
node:18-alpine | ~115 MB | sh | apk |
gcr.io/distroless/nodejs18 | ~85 MB | Aucun | Aucun |
# Alpine — très légère, basée sur musl libc
FROM python:3.11-alpine
RUN apk add --no-cache gcc musl-dev # apk au lieu de apt
# Distroless — pas de shell, surface d'attaque minimale
FROM gcr.io/distroless/java17
COPY --from=builder /app/target/app.jar /app.jar
CMD ["/app.jar"]
3. Optimiser l'utilisation du cache
# Règle : du moins au plus souvent modifié
# ❌ MAUVAIS — COPY . invalide tout à chaque changement
FROM node:18-alpine
WORKDIR /app
COPY . . # invalide le cache npm
RUN npm install # réinstalle tout à chaque build
# ✅ BON — package.json stable = cache npm préservé
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./ # rarement modifié
RUN npm ci # layer caché si package.json inchangé
COPY . . # souvent modifié — en dernier
# BuildKit cache mounts (Node.js)
RUN --mount=type=cache,target=/root/.npm npm ci
# BuildKit cache mounts (Python)
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
4. Sécurité des conteneurs
USER non-root
# Créer et utiliser un utilisateur non-root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Pour Node.js — user 'node' inclus dans l'image officielle
USER node
COPY --chown=node:node . .
Secrets Docker
# ❌ JAMAIS — secret dans une variable ENV ou ARG
ENV DATABASE_PASSWORD=secret123 # visible dans docker inspect !
# ✅ BuildKit secrets — non persistés dans les layers
RUN --mount=type=secret,id=db_pass \
DB_PASS=$(cat /run/secrets/db_pass) \
&& echo "Connected"
# Lancer le build avec le secret
docker build --secret id=db_pass,src=.secrets/db_pass .
# Runtime : injecter via -e ou --env-file (jamais dans l'image)
docker run --env-file .env monapp
.dockerignore
# Exclure fichiers sensibles du contexte de build
.env
.env.*
*.pem
*.key
secrets/
.git/
node_modules/
coverage/
.DS_Store
5. Scan de vulnérabilités
# Docker Scout (intégré dans Docker Desktop)
docker scout cves monapp:latest # Lister les CVEs
docker scout quickview monapp # Vue d'ensemble
docker scout recommendations monapp # Recommandations
# Trivy (outil open-source)
trivy image monapp:latest
# Snyk
snyk container test monapp:latest
6. Checklist production
- Utiliser multi-stage builds pour les apps compilées
- Toujours spécifier des tags précis (pas
:latesten prod) - Utiliser USER non-root dans le Dockerfile
- Activer HEALTHCHECK pour les services critiques
- Définir des limites de mémoire et CPU
- Utiliser .dockerignore pour exclure les fichiers sensibles
- Ne jamais stocker de secrets dans les layers
- Scanner les images avec Docker Scout / Trivy en CI/CD
- Fixer les versions des images de base et des dépendances
- Utiliser
--read-onlyquand possible
# HEALTHCHECK — conteneur auto-reporté
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1