Dockerfile et images
Layers, cache, USER node, secrets hors image.
Objectifs
À la fin de cette leçon, vous saurez :
- écrire un Dockerfile complet pour une application Node ;
- comprendre les layers et le cache de build ;
- distinguer image (modèle) et conteneur (instance).
🔗 Pour vous rafraîchir la mémoire : commandes docker de base · package.json et scripts npm
Le Dockerfile, ligne par ligne
FROM node:22-alpine # image de base (OS + Node)
WORKDIR /app # dossier courant des instructions suivantes
COPY package*.json ./ # d'abord les manifestes...
RUN npm ci # ...puis l'installation (cacheable !)
COPY . . # ensuite votre code
RUN npm run build # compile TypeScript
EXPOSE 3000 # documentation du port
USER node # on ne tourne PAS en root
CMD ["node", "dist/main.js"] # commande au démarrage
docker build -t todo-api .
docker run -d -p 3000:3000 --env-file .env todo-api
Layers et cache : l'ordre qui compte
Sous le capot, chaque instruction crée une couche en lecture seule, et Docker empile ces couches en un système de fichiers union : l'image présente la fusion de toutes les couches, comme si c'était un dossier unique. Deux images partagent leurs couches identiques sur disque — d'où des téléchargements et builds bien plus légers qu'il n'y paraît. Gardez ce modèle en tête : il explique à la fois le cache qui suit et la sécurité des secrets (une couche existe pour toujours, même « écrasée » ensuite).
Chaque instruction crée un layer ; le cache invalide un layer dès qu'une couche précédente change. D'où l'ordre stratégique :
COPY package.json → RUN npm ci ← invalidé seulement si les dépendances changent
COPY . . ← invalidé à chaque modification de code
Résultat : modifier un fichier source ne réinstalle pas node_modules — build de secondes au lieu de minutes. Inversez les deux COPY et chaque build re-télécharge tout.
Image vs conteneur
IMAGE = modèle immuable, construit une fois, versionné (todo-api:v1)
CONTENEUR = instance en cours d'exécution de cette image
Un conteneur peut être arrêté, supprimé, relancé : l'image ne bouge jamais. Pour changer l'application, on construit une NOUVELLE image — c'est ce qui rend les déploiements prévisibles et les rollbacks triviaux (relancer l'ancienne version).
Les bonnes pratiques visibles ici
alpine: base minimale (image de ~150 Mo vs ~1 Go).npm ciplutôt que install : fidèle au lockfile.USER node: moindre privilège dans le conteneur..dockerignore(comme .gitignore) : exclure node_modules, .env, dist — jamais de secret dans une image.
Exercice
- Écrivez le Dockerfile de votre API ; construisez-la ; lancez-la avec son .env.
- Modifiez un fichier source puis rebuildez : observez les layers mis en cache.
- Ajoutez
COPY .env .puis vérifiez dans l'image (docker run ... cat .env) : pourquoi ne faut-il JAMAIS faire ça ?
Résumé
- FROM/WORKDIR/COPY/RUN/CMD : la grammaire minimale.
- Cache par layers : dépendances d'abord, code ensuite.
- Image immuable + conteneurs jetables = déploiements prévisibles.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Question 3. Les images se partagent via des registres potentiellement publics : le .env y figerait vos secrets pour toujours (les layers restent dans l'historique même si on « supprime » le fichier après). Les secrets s'injectent au RUN (--env-file), jamais au BUILD.