Projet · Phase 3.7

Docker (7/9) — Un Dockerfile de production

Une image qui marche n'est pas une image prête pour la production. On la rend légère (multi-stage), sûre (non-root) et reproductible (versions figées).

⏱ ~1 h 30Niveau intermédiairePrérequis : Docker (6/9)Pratique · TP
L’idée reçue

« Si l’image se construit et que le conteneur démarre, elle est bonne. »

« Ça marche » n’est pas « c’est prêt ». Une image de production doit être légère (rapide à transférer), sûre (pas root, surface d’attaque réduite) et reproductible (mêmes versions partout). On va transformer une image « qui marche » en image de production.

À la fin de ce module, tu sauras…

  • Construire en plusieurs étapes (multi-stage) pour alléger l’image finale
  • Faire tourner un conteneur sous un utilisateur non-root
  • Figer les versions pour des builds reproductibles
  • Réduire le contexte de build avec un .dockerignore

01Le problème : des images trop grosses

Expérience 1

Mesurer le gaspillage

Imagine une image qui a besoin d’outils pour se construire mais pas pour tourner. Exemple : un site généré par un outil Node, servi ensuite par Nginx. Une version naïve embarque tout Node dans l’image finale.

Manipule
docker images | grep soria-web
Note la taille de ton image actuelle. On va voir comment ne garder que le strict nécessaire à l’exécution.
Comprends outils de build ≠ besoins d’exécution

Compilateurs, gestionnaires de paquets, dépendances de développement : utiles pour fabriquer, inutiles pour servir. Les laisser dans l’image finale, c’est du poids mort et une surface d’attaque supplémentaire.

02Le build multi-stage

Expérience 2

Construire dans une étape, ne garder que le résultat

Manipule
cd ~/soria-web
cat > Dockerfile <<'EOF'
# --- étape 1 : build ---
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# --- étape 2 : image finale ---
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
EOF
La première étape (build) fabrique le site avec Node. La seconde repart d’une base Nginx propre et ne copie que le résultat (–from=build). Node n’existe pas dans l’image finale.
Comprends le principe du multi-stage

Un Dockerfile peut contenir plusieurs FROM : chaque bloc est une étape. On construit dans une étape « lourde », puis on copie uniquement les artefacts dans une étape finale « légère ». Résultat : une image de production minuscule, sans les outils de build. (Adapte selon ton appli — un site statique pur n’a pas besoin de l’étape Node.)

03Ne pas tourner en root

Expérience 3

Réduire les privilèges du conteneur

Vérifie sous quel utilisateur tourne un conteneur par défaut :

Manipule
docker run --rm debian whoami
Observe
root
Par défaut : root. En production, on veut un utilisateur sans privilèges. Ajoute-le dans le Dockerfile :
Manipule
RUN useradd -r -u 10001 appuser
USER appuser
Comprends pourquoi non-root, encore

Même principe de moindre privilège que pour les services Linux (Phase 1). Si un attaquant s’échappe de l’application, il ne doit hériter que de droits minimaux, pas de root. L’instruction USER fixe l’utilisateur qui exécute le conteneur. (Note : servir sur un port < 1024 exige root ; en conteneur on utilise un port haut, ex. 8080, et on publie.)

04Figer les versions & ignorer l’inutile

Expérience 4

Reproductibilité et contexte propre

Tu as sûrement remarqué : on écrit nginx:1.27-alpine, pas nginx:latest. Une version figée garantit que l’image sera identique dans six mois.

Réduis aussi le contexte envoyé au build avec un .dockerignore :

Manipule
cat > .dockerignore &lt;&lt;'EOF'
node_modules
.git
*.md
EOF
Comprends latest, le piège

latest n’est pas « la dernière version » figée : c’est une étiquette mouvante. Un build qui marche aujourd’hui peut casser demain si latest change. On fige donc les versions de base. Et .dockerignore évite d’envoyer node_modules, .git… au build : plus rapide, plus propre, plus sûr.

Ce que tu retiens

Une image de production est légère (multi-stage : on ne garde que les artefacts), sûre (USER non-root, moindre privilège), et reproductible (versions figées, pas de latest, .dockerignore). « Ça marche » devient « c’est prêt à déployer ».

Mini-projet

Durcis l’image SORIA Web

À faire

  1. Réécris le Dockerfile de SORIA Web avec une base figée (ex. nginx:1.27-alpine).
  2. Ajoute un .dockerignore pertinent.
  3. Si ton appli a une étape de build, passe en multi-stage ; sinon, justifie pourquoi ce n’est pas nécessaire.
  4. Construis soria-web:2.0 et compare sa taille aux versions précédentes (docker images).
  5. Explique en 3 lignes les trois qualités d’une image de production.
Fil rouge — Une belle image, seule, ne suffit pas : une vraie application, c’est plusieurs conteneurs qui coopèrent (un site, une base de données…). Au module suivant, on les orchestre ensemble avec Docker Compose.

?Auto-évaluation

1. À quoi sert un build multi-stage ?

À construire dans une étape avec tous les outils, puis ne copier que les artefacts dans une image finale légère — sans les outils de build.

2. Pourquoi éviter le tag latest en production ?

C’est une étiquette mouvante : le build n’est pas reproductible. On fige une version précise pour garantir des images identiques dans le temps.

3. Pourquoi faire tourner le conteneur en non-root ?

Moindre privilège : en cas de compromission, l’attaquant n’hérite pas des droits root.

Connecte-toi pour enregistrer ta progression.

Suite → Docker (8/9) — Plusieurs conteneurs : Docker Compose