L’image Docker

Une construction en plusieurs étapes, d’un seul Dockerfile : les dépendances mises en cache, le build autonome de Next, un migrateur à part et une image de service qui ne contient que ce qui s’exécute — sans un seul secret. Ce que chaque étape fait, pourquoi elle est là, et les deux corrections de cette relecture.

Précédemment. La qualité se vérifie sur la machine du développeur : pnpm check à chaque commit, les tests de bout en bout avant un déploiement. Reste à mettre le site en ligne.

Cette partie suit le site de la machine du développeur jusqu’au serveur : l’image qui le contient (ce chapitre), sa mise en production (chapitre 2), et la sandbox, une copie complète et autonome pour l’essayer sans rien risquer (chapitre 3).

Une image, des étapes 🐳#

Une image Docker est un système de fichiers figé : un Linux minimal, Node, l’application compilée, ce qu’il faut pour la lancer. Le serveur la construit à partir du dépôt, puis en démarre un conteneur. Le Dockerfile en est la recette, exécutée ligne à ligne ; chaque ligne produit une couche, que Docker garde en cache tant que ce dont elle dépend n’a pas changé.

Le Dockerfile du projet est multi-étapes : plusieurs FROM, chacun une image intermédiaire nommée, qui peut recopier des fichiers d’une étape précédente. Deux images en sortent :

Étape

Rôle

Sort de la construction ?

base
Debian slim, Node 22, openssl, pnpm par Corepack.
Non : le socle des autres.
deps
Les dépendances, installées depuis le fichier de verrouillage.
Non.
builder
Le client Prisma généré, le build Next, sharp mis à plat.
Non : seuls ses résultats sont recopiés.
migrator
Le schéma, les migrations, la CLI Prisma.
Oui : lancé avant de démarrer une nouvelle version, puis arrêté.
runner
Le serveur Next autonome, ffmpeg, libheif. Rien d’autre.
Oui : le service qui répond aux visiteurs.

Séparer construction et service, c’est ne pas livrer les outils de construction : le compilateur TypeScript, ESLint, les sources, les dépendances de développement restent dans builder, qui n’est jamais démarré. L’image de service est plus petite, et ce qu’elle ne contient pas ne peut pas servir à un attaquant.

Dockerfile

Clique une étape pour mettre en évidence la zone correspondante dans le fichier complet.

Debian, pas Alpine
DOCKERFILE
Dockerfile
guided extract: lines 17-105focus line 4
// ⚠ Fichier introuvable : Dockerfile// Il a été déplacé ou supprimé depuis la rédaction de ce chapitre.

Ordonner les couches du plus stable au plus changeant. Le système et les outils, puis les dépendances, puis le code. Une construction ne refait alors que ce qui a changé, et la suite : une ligne de code modifiée ne refait que les dernières couches.

Ce que Docker ne reçoit pas#

Avant de construire, Docker envoie au moteur le contexte : le dossier du dépôt, filtré par .dockerignore. Tout ce qui entre dans le contexte peut finir dans une couche, et une couche garde ce qu’on y a copié même si une étape suivante le supprime.

Les dépendances, les constructions, les secrets
BASH
.dockerignore
// ⚠ Fichier introuvable : .dockerignore// Il a été déplacé ou supprimé depuis la rédaction de ce chapitre.

Aucun .env n’entre dans l’image : les secrets sont donnés au conteneur, au démarrage, par le serveur (partie « Configuration », chapitre « Les variables d’environnement »). Une image peut être partagée, poussée sur un registre, inspectée couche par couche ; elle ne contient rien qu’on ne puisse montrer.

La relecture a ajouté la construction des tests de bout en bout (.next-e2e, qui pèse plusieurs centaines de mégaoctets), les tests et leurs résultats, et les scripts apply-*.sh qu’on dépose à la racine du dépôt pour appliquer une modification : ils ne servent ni au build ni au site.

Exclu

Pourquoi

node_modules, .next, .next-e2e
Reconstruits dans l’image, pour Linux : ceux du Mac viseraient une autre plateforme, et alourdiraient le contexte.
.env, .env.*
Des secrets : jamais dans une couche.
.git, .github, .vscode
L’historique et l’outillage : rien à faire dans le site.
e2e, test-results, playwright-report
Les tests et leurs traces : utiles sur la machine, pas en production.
apply-*.sh, *.sql, *.png de la racine
Des scripts et des fichiers de passage, parfois des sauvegardes : ce qui traîne à la racine n’entre pas.

Récapitulatif#

Question

Réponse du projet

Combien d’images sort la construction ?
Deux : migrator (les migrations) et runner (le site).
Pourquoi Debian slim et pas Alpine ?
La bibliothèque C d’Alpine impose à Prisma un moteur différent, à surveiller à chaque version.
Pourquoi les dépendances dans une étape à part ?
pnpm install, la ligne la plus longue, vient du cache tant que les dépendances ne changent pas.
Comment une NEXT_PUBLIC_ entre-t-elle dans le site ?
Par un argument de build, avec une valeur par défaut ; la changer impose de reconstruire.
Sous quel utilisateur tourne le site ?
nextjs, sans privilèges.
Où sont les secrets ?
Nulle part dans l’image : donnés au conteneur au démarrage.
Qu’a changé la relecture ?
pnpm figé au build du migrateur, ffmpeg et libheif remontés avant la copie (en cache), contexte allégé, défaut de la racine de stockage.

À suivre#

L’image est prête. La mettre en production, c’est un ordre précis : le code, la migration, puis le nouveau conteneur — et savoir lire ce qu’il dit en démarrant.

Chapitre suivant : mettre en production. Ce qui vit sur le serveur, l’ordre d’un déploiement, ce que fait le site au démarrage, et comment revenir en arrière.

Sur cette page