2.1 · Écrire son premier Dockerfile

Niveau 2 · Dockerfile & build

2.1Écrire son premier Dockerfile

Objectif : Écrire un Dockerfile minimal qui emballe une application et la lance.
Temps estimé : 14 min · lecture autonome · Si vous n'avez que 5 min : lisez « L'essentiel en 30s » + le schéma.

L'essentiel en 30 secondes

  • 5 instructions = 1 image lançable : FROM, WORKDIR, COPY, RUN, CMD — chacune a un rôle distinct.
  • Build cuit, run exécute : le build fige les couches, le run démarre le conteneur — lire la dernière étape en cas d'échec.
  • RUN construit, CMD démarre : installer au build, lancer au run — jamais le serveur dans RUN.

Mini-cas filé — Idir et l'app qui ne voyageait pas

Idir, devops junior à Alger, doit livrer la petite app Node de son équipe sur une autre machine : « ça marche chez moi » ne suffit plus. Il écrit son premier Dockerfile à la racine du projet : base officielle, dossier de travail, dépôt du code, installation au build, commande de démarrage. Il builde en mon-app:1.0 puis lance avec le port publié : l'app répond sur localhost:3000, sur n'importe quelle machine. Cet app « emballée par Idir » revient en 2.2 (build plus rapide et image plus maigre), 2.3 (tag et publication), 2.4 (port configurable) et 2.5 (version durcie non-root).

Fil conducteur du niveau 2 : l'app d'Idir revient dans le cours, le schéma, le script et le défi 48h — et continue en 2.2, 2.3, 2.4 et 2.5.

1. Les 5 instructions vitales

FROM choisit la base (par exemple node:20-slim, une image officielle légère), WORKDIR fixe le dossier de travail interne (créé s'il manque), COPY y dépose le code depuis le contexte, RUN installe pendant le build (chaque RUN fige une couche), CMD définit la commande de démarrage du conteneur. Cinq lignes suffisent pour un premier service qui tourne. Idir les écrit dans cet ordre : base, dossier, code, installation, lancement.

2. Build puis run : les deux temps

docker build -t mon-app:1.0 . cuit l'image : le point final désigne le contexte, c'est-à-dire le dossier envoyé au daemon pour le build. docker run l'exécute : le conteneur démarre et publie ses ports. Si le build échoue, on lit la dernière étape affichée : c'est elle qui accuse (dépendance manquante, faute de frappe, réseau coupé). Idir nomme son image nom:version dès le build pour s'y retrouver.

3. CMD vs RUN : ne plus confondre

RUN s'exécute pendant le build (installer, compiler) et fige une couche dans l'image. CMD s'exécute au démarrage du conteneur (lancer le serveur), à chaque run. Mettre le lancement dans RUN fige un build qui ne démarre jamais : l'erreur classique du débutant. On écrit le CMD en forme JSON (["node", "server.js"]), une seule commande de démarrage. Idir installe avec RUN et démarre avec CMD : construire puis lancer, jamais l'inverse.

Erreur d'interprétation fréquente : « RUN et CMD, c'est pareil : les deux lancent des commandes ». Non. RUN s'exécute pendant le build et fige une couche (installer, compiler) ; CMD s'exécute au démarrage du conteneur (lancer le serveur). La règle d'Idir : tout ce qui prépare l'image → RUN ; la commande qui fait vivre le conteneur → CMD, en forme JSON.
Du code à l'app qui tourne Base, code, installation au build, lancement au run FROM + COPY Base officielleet dépôt du codeRUN (au build) Installer,figer une coucheCMD (au run) Lancer leserveur au démarrage
Base, code, installation au build, lancement au run
Version texte du schéma (SEO / lecteurs d'écran / traduction) : 1) Idir POSE la base : FROM sur une image officielle légère et WORKDIR comme dossier de travail, puis COPY pour y déposer le code depuis le contexte. 2) Il CUIT : RUN installe les dépendances pendant le build (couche figée), docker build -t mon-app:1.0 fige l'image. 3) Il LANCE : CMD en forme JSON démarre le serveur au docker run, port publié. Leçon : RUN construit, CMD démarre — deux temps, deux rôles.

🔎 Exemple concret — 🔎 Exemple concret — Une app Node emballée

L'app d'Idir, du code à l'image lançable :

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

docker build -t mon-app:1.0 .
docker run -d --name app -p 3000:3000 mon-app:1.0

Lecture : les dépendances s'installent au build (RUN), le serveur démarre au run (CMD), le port est déclaré (EXPOSE) et publié (-p).

Leçon : copier package*.json d'abord prépare déjà l'optimisation du cache (2.2).

Vocabulaire de la section

FROM
L'image de base du Dockerfile, point de départ de toutes les couches (de préférence une image officielle légère).

Exemple simple : Idir part de node:20-slim pour son app Node.

Ce que ce n'est pas : la commande qui lance le conteneur.

Réflexe : base officielle légère d'abord.

WORKDIR
Le dossier de travail interne du conteneur, créé automatiquement s'il manque ; toutes les instructions suivantes s'y rapportent.

Exemple simple : Idir fixe WORKDIR /app avant de copier son code.

Ce que ce n'est pas : un volume qui persiste les données.

Réflexe : fixer le dossier avant de copier.

RUN
Exécuter une commande PENDANT le build (installer, compiler) ; chaque RUN fige une couche dans l'image.

Exemple simple : Idir installe ses dépendances avec RUN npm ci.

Ce que ce n'est pas : la commande de démarrage du conteneur.

Réflexe : préparer l'image = RUN.

CMD
La commande de DÉMARRAGE du conteneur, exécutée à chaque docker run ; une seule, de préférence en forme JSON.

Exemple simple : Idir démarre avec CMD ["node", "server.js"].

Ce que ce n'est pas : une installation figée au build.

Réflexe : faire vivre le conteneur = CMD.

En pratique — En pratique — Emballer une mini-app

  1. Créez un dossier avec un server.js qui répond « OK » sur le port 3000.
  2. Écrivez le Dockerfile (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD) à la racine du projet.
  3. Buildez en mon-app:1.0 et lisez chaque étape du build.
  4. Lancez avec -p 3000:3000 et ouvrez localhost:3000.
Vous transformez n'importe quel code en image lançable : build ici, run partout.

Script prêt à dire — expliquer le Dockerfile à un collègue (30 s)

« Ce Dockerfile fait cinq choses : il part d'une base officielle, fixe le dossier de travail, copie le code, installe au build avec RUN, et démarre avec CMD. On cuit avec docker build, on lance avec docker run : l'app tourne pareil sur toutes les machines. RUN construit, CMD démarre — c'est la seule distinction à retenir. »

À dire en montrant le Dockerfile d'Idir puis le build étape par étape.

Modèle copier-coller — premier Dockerfile Node

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

App Python

Idir emballe un script Python : FROM python:3.12-slim, COPY requirements puis RUN pip install, CMD en forme JSON.

Page statique

Un site vitrine : Idir part d'une base serveur légère, copie les fichiers, expose le port, CMD lance le serveur.

Contexte énorme

Build qui rame : Idir ajoute un .dockerignore (node_modules, .git) et le contexte redevient léger.

RUN piégé

Un collègue lance le serveur dans RUN : le build fige et ne démarre jamais — Idir déplace le lancement en CMD.

✓ Bonnes pratiques

  • Une seule commande de démarrage en CMD (forme JSON).
  • Nommer l'image nom:version dès le build.
  • Lire le build étape par étape en cas d'échec.

✗ Pièges fréquents

  • Lancer le serveur dans RUN au lieu de CMD.
  • Oublier le .dockerignore (contexte énorme).
  • Copier node_modules local dans l'image.

Points clés à retenir

  • FROM, WORKDIR, COPY, RUN, CMD : le socle de tout Dockerfile.
  • Build cuit l'image, run l'exécute (le « . » est le contexte envoyé au daemon).
  • RUN = build (fige une couche), CMD = démarrage (forme JSON).
  • Nommer nom:version dès le build ; lire la dernière étape en cas d'échec.

Défi 48h — appliquez demain

Emballez une vraie mini-app (la vôtre ou celle d'Idir) : Dockerfile en 7 lignes (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD), build tagué 1.0 lu étape par étape, puis run avec port publié vérifié dans le navigateur.

Checklist sortie de section
  • Je peux écrire un Dockerfile minimal (FROM, WORKDIR, COPY, RUN, EXPOSE, CMD).
  • Je sais builder (contexte compris) et lire un échec à la dernière étape.
  • Je distingue RUN (build) et CMD (démarrage) sans hésiter.
Auto-évaluation 1-5 : est-ce que je place chaque instruction au bon temps (build vs run) ?est-ce que mon image se lance sans retouche sur une autre machine ?

Questions fréquentes

Où écrire le Dockerfile ?

À la racine du projet, nommé exactement « Dockerfile ». Le contexte est le dossier envoyé au daemon : restez à la racine pour COPY . . et allégez-le avec un .dockerignore (node_modules, .git) afin de ne pas envoyer des centaines de Mo à chaque build.

Pourquoi le build est-il si lent ?

Souvent un contexte énorme (node_modules, .git) envoyé à chaque fois. Le .dockerignore et l'ordre des couches (dépendances d'abord, code ensuite — voir 2.2) règlent 80 % des cas avant toute autre optimisation.

Vérifiez votre compréhension

Dans un Dockerfile, que fait RUN par rapport à CMD ?

Ressources supplémentaires (optionnel, pour aller plus loin)

Examen du niveau 2Questions inédites et durcies pour valider vos acquis