La méthode
des baby steps

Développer un Dockerfile petit pas par petit pas - comme un vrai développeur, avec des échafaudages qui apparaissent puis disparaissent.

docker.projet.autos - le site compagnon Docker

Le Dockerfile final est un mensonge

Tous les tutoriels du monde montrent le Dockerfile final : cinq lignes propres, parfaites, dans le bon ordre. Ce fichier est un mensonge. Personne ne l'a écrit comme ça, de haut en bas, du premier coup.

Un vrai développeur avance par petits pas : une micro-modification, une vérification, puis la suivante. Il ajoute des lignes de test, les regarde travailler, puis les efface quand elles ont fini leur travail. Le fichier final ne garde aucune trace de ce chemin - mais ton historique de versions, lui, doit le raconter.

Évaluation Dans les laboratoires, versionner le Dockerfile ligne par ligne de haut en bas ne donne pas les points : c'est la signature d'une solution recopiée. Ce qui compte, c'est le versionnement dans l'ordre logique de développement - avec des lignes de test qui apparaissent puis disparaissent.

La boucle du baby step

Un baby step, c'est toujours la même boucle, répétée jusqu'à la victoire :

1
Une modif
Une seule petite modification. Jamais deux.
2
Build
docker build - le cache rend ça presque instantané.
3
Run
docker run --rm pour voir le résultat.
4
Constat
On lit la sortie. On comprend ce qui s'est passé.
5
Commit
On versionne le petit pas avec un message clair.
Le commit, à chaque tour de boucle
$ git add Dockerfile
$ git commit -m "exploration : ls de la racine de l'image"
Règle d'or Une seule modification à la fois. Si ça casse, la coupable est forcément la dernière modification - pas besoin de chercher.

L'objectif du jour

On va dockeriser un mini serveur web qui affiche le menu du jour de la cafétéria. Une image nommée menu, un port publié, une page menu.html servie au navigateur.

Le Dockerfile final fera 5 lignes. Mais on va y arriver en 12 baby steps - avec des détours volontaires, des lignes de test, et deux grands effacements. C'est le chemin qui t'apprend Docker, pas la destination.

Interdit de sauter à la fin pour recopier le fichier final. À l'examen pratique, chaque ligne devra être expliquée - et ton historique devra montrer le chemin.
Étape 1

Un socle qui roule

Premier pas : la plus petite image possible qui fait quelque chose de visible. Pas le serveur. Pas le menu. Juste un témoin qui prouve que la chaîne build-run fonctionne.

À taper dans le terminal
$ mkdir menu-du-jour
$ cd menu-du-jour
$ git init
$ nano Dockerfile
Dockerfile
FROM alpine

CMD echo "le socle roule"
$ docker build -t menu .
$ docker run --rm menu
le socle roule

Ce CMD echo est déjà un échafaudage : il ne survivra pas jusqu'à la fin. Son seul travail est de prouver que tout roule.

COMMITsocle alpine + echo temoin
Étape 2

Regarder dans l'image avec ls

ÉCHAFAUDAGE 1 Le ls d'exploration : quand tu te demandes ce qu'il y a dans l'image, tu ne devines pas - tu regardes.

Qu'est-ce qu'il y a réellement dans cette image alpine ? Le conteneur n'est pas ta machine : il a son propre système de fichiers. On remplace le témoin par un coup d'oeil :

Dockerfile
FROM alpine

CMD ls -la /
$ docker build -t menu . && docker run --rm menu
drwxr-xr-x 2 root root 4096 bin drwxr-xr-x 5 root root 360 dev drwxr-xr-x 1 root root 4096 etc drwxr-xr-x 2 root root 4096 home ...

Voilà le monde intérieur de l'image. Changer un CMD ne coûte presque rien : tout le reste vient du cache, le rebuild prend une seconde.

COMMITexploration : ls de la racine de l'image
Étape 3

Se placer avec WORKDIR, vérifier avec pwd

Notre application vivra dans /app. On ajoute un WORKDIR... et remarque bien : on insère la ligne au milieu du fichier, pas à la fin. L'ordre d'écriture n'est déjà plus l'ordre des lignes.

Dockerfile
FROM alpine

WORKDIR /app
CMD pwd
$ docker build -t menu . && docker run --rm menu
/app

Le dossier a été créé et on s'y trouve. Deuxième micro-vérification : CMD ls -la montre un dossier vide (juste . et ..). Deux petits pas valent mieux qu'un grand.

COMMITWORKDIR /app + pwd temoin
Étape 4

Apporter le menu, vérifier avec ls

Côté hôte, on crée la page du menu :

$ nano menu.html
menu.html
<h1>Le menu du jour</h1>
<p>Poutine du chef</p>
<p>Tarte au sucre</p>

Puis on l'embarque dans l'image, et on vérifie tout de suite avec le réflexe de l'étape 2 :

Dockerfile
FROM alpine

WORKDIR /app
COPY menu.html .
CMD ls -la
-rw-r--r-- 1 root root 78 menu.html

Le fichier est là, avec sa taille. Après chaque COPY, un ls. C'est le réflexe qui économise des heures de débogage.

COMMITCOPY menu.html + ls de controle
Étape 5

Vérifier le contenu avec cat

ÉCHAFAUDAGE 2 Le cat de contrôle : ls prouve que le fichier existe, cat prouve que son contenu est le bon.

Un fichier peut exister mais être vide, tronqué, ou être la mauvaise version. Un petit pas de plus :

Dockerfile
FROM alpine

WORKDIR /app
COPY menu.html .
CMD ls -la
CMD cat menu.html
$ docker build -t menu . && docker run --rm menu
<h1>Le menu du jour</h1> <p>Poutine du chef</p> <p>Tarte au sucre</p>

C'est exactement la technique de la diapo « un fichier dans l'image » du parcours : un CMD cat temporaire pour prouver que le COPY a bien fait son travail.

COMMITcat de controle du contenu du menu
Étape 6

Des jalons dans le build avec echo

ÉCHAFAUDAGE 3 Les jalons echo variés : des messages différents avant et après chaque sous-étape, pour savoir où le build en est rendu.

Il est temps d'installer le serveur web. Une installation, ça peut échouer à moitié - alors on l'encadre de jalons :

Dockerfile
FROM alpine

WORKDIR /app
COPY menu.html .
RUN echo "=== jalon 1 : avant installation ===" && \
    apk add --no-cache busybox-extras && \
    echo "=== jalon 2 : installation terminee ==="
CMD cat menu.html
$ docker build --progress=plain -t menu .
# --progress=plain force Docker a montrer chaque ligne de sortie du build
#8 [4/4] RUN echo "=== jalon 1 : avant installation ===" ... #8 0.311 === jalon 1 : avant installation === #8 0.514 (1/1) Installing busybox-extras (1.37.0-r30) #8 0.892 === jalon 2 : installation terminee ===
Variés ! Deux jalons identiques ne servent à rien : impossible de savoir lequel s'est affiché. Chaque message doit être unique - jalon 1, jalon 2, avant, après.
COMMITinstallation busybox-extras avec jalons
Étape 7

Le jalon qui disparaît = le cache

Relance le même build, sans rien changer :

$ docker build --progress=plain -t menu .
#8 [4/4] RUN echo "=== jalon 1 : avant installation ===" ... #8 CACHED

Les jalons n'apparaissent plus. Ce n'est pas un bogue : la couche vient du cache, Docker n'a rien réexécuté. Un jalon disparu, c'est une étape déjà faite.

Pas de modif ? Pas de commit. Ce baby step était un pas de lecture : on n'a rien changé, on a appris quelque chose. Ça arrive, et c'est très bien.
Étape 8

Le serveur est-il vraiment là ?

L'installation dit qu'elle a réussi. On ne la croit pas sur parole - on vérifie, avec un cousin du ls :

Dockerfile (extrait)
CMD cat menu.html
CMD which httpd
$ docker build -t menu . && docker run --rm menu
/usr/bin/httpd

ls, cat, pwd, which : c'est la même famille. Des petites commandes jetables qui répondent chacune à une seule question - le fichier est-il là, le contenu est-il bon, où suis-je, l'outil est-il installé.

COMMITverification : httpd est bien installe
Étape 9

Le grand ménage : la vraie commande

ÉCHAFAUDAGE 4 L'effacement. Quand un échafaudage a fini son travail, on le retire - et ce retrait est un baby step versionné comme les autres.

Tout est vérifié : le fichier est là, le contenu est bon, le serveur est installé. Les échafaudages peuvent tomber, la vraie commande prend leur place :

Dockerfile
FROM alpine

WORKDIR /app
COPY menu.html .
RUN echo "=== jalon 1 : avant installation ===" && \
RUN apk add --no-cache busybox-extras
CMD which httpd
CMD httpd -f -v -h /app

httpd -f -v -h /app : le serveur web de busybox, en avant-plan (-f), bavard (-v), qui sert le dossier /app.

Le point clé Ce commit efface des lignes. C'est exactement « les lignes de test qui disparaissent » que ton prof cherche dans ton historique : la preuve d'un vrai développement.
COMMITretrait des lignes de test, vraie commande httpd
Étape 10

Lancer SANS -d : le mode développement

ÉCHAFAUDAGE 5 Le lancement en avant-plan : tant qu'on développe, pas de -d. On veut les logs sous les yeux, en direct.
$ docker run --rm -p 8080:80 menu

Le terminal semble gelé... c'est parfait ! Le serveur tourne en avant-plan et attend des visites. Dans un deuxième terminal (ou un navigateur) :

$ curl http://localhost:8080/menu.html

Et dans le premier terminal, la ligne de log tombe en direct :

[::ffff:172.17.0.1]:52144: url:/menu.html

Ctrl+C arrête le serveur, --rm fait le ménage du conteneur. Modifier, relancer, observer : la boucle reste courte.

En développement chaque requête s'affiche au moment où elle arrive. Un problème ? Tu le vois à la seconde où il se produit, pas dix minutes plus tard.
Étape 11

Quand c'est stable : -d et docker logs

Le serveur est fiable, on ne veut plus le regarder en permanence. On le détache :

$ docker run -d --name menu-en-service -p 8080:80 menu
$ docker logs --follow menu-en-service  # les logs restent accessibles a la demande
$ docker stop menu-en-service
EN DÉVELOPPEMENT - sans -d
  • Le terminal est occupé par le serveur : voulu
  • Logs en direct, sous les yeux
  • --rm : le conteneur s'efface tout seul
  • Ctrl+C pour arrêter et repartir
EN SERVICE - avec -d
  • La main revient tout de suite
  • --name pour le retrouver
  • docker logs pour consulter à la demande
  • docker stop pour arrêter proprement

Remarque : -d n'est pas une ligne du Dockerfile, c'est un choix de lancement. Rien à committer ici - le fichier n'a pas bougé.

Étape 12

Dernier baby step : réordonner pour le cache

Un détail agace : chaque fois qu'on modifie menu.html, le build refait l'installation. Normal - le COPY est avant le RUN, et une couche modifiée invalide toutes celles qui suivent. On remonte l'installation :

Dockerfile - version finale
FROM alpine

RUN apk add --no-cache busybox-extras
WORKDIR /app
COPY menu.html .
RUN apk add --no-cache busybox-extras
CMD httpd -f -v -h /app

Maintenant, changer le menu ne réinstalle plus rien : le build est instantané. Et savoure l'ironie : la deuxième ligne du fichier final est arrivée au douzième baby step. L'ordre du fichier n'a jamais été l'ordre du développement.

COMMITremontee de l'installation avant le COPY (cache)

Ton historique raconte ton histoire

Dockerfile final - 5 lignes muettes
FROM alpine

RUN apk add --no-cache busybox-extras
WORKDIR /app
COPY menu.html .

CMD httpd -f -v -h /app
$ git log --oneline
a9f3c21 remontee de l'installation avant le COPY (cache)
8d17e40 retrait des lignes de test, vraie commande httpd
5b02c9d verification : httpd est bien installe
e64a1f7 installation busybox-extras avec jalons
c3d98b2 cat de controle du contenu du menu
7f45a06 COPY menu.html + ls de controle
2b8ce13 WORKDIR /app + pwd temoin
91d604f exploration : ls de la racine de l'image
4a7b5e9 socle alpine + echo temoin

Le fichier final ne dit rien du chemin. L'historique dit tout : les explorations, les vérifications, les échafaudages montés puis démontés.

Le test du prof Un historique qui recopie le fichier de haut en bas, ligne par ligne, dans l'ordre ? Solution copiée. Un historique avec des détours, des ls qui apparaissent et disparaissent ? Un vrai développement - et les points qui viennent avec.

La boîte à échafaudages

ls -la
Le fichier est-il là ? Bonne taille, bon dossier ? Après chaque COPY.
cat
Le contenu est-il le bon ? Exister ne suffit pas.
pwd
Où suis-je ? Le WORKDIR a-t-il fait son travail ?
which
L'outil est-il vraiment installé, et où ?
echo "=== jalon N ==="
Des jalons variés dans les RUN, pour suivre le build (avec --progress=plain).
run sans -d
Les logs en direct tant qu'on développe. Le -d viendra quand ce sera stable.
La règle Un échafaudage qui reste dans le fichier final, c'est un oubli. Un échafaudage effacé, c'est un travail bien fait - et un commit de plus dans ton histoire.

Pour aller plus loin