Deux moments bien distincts

Toutes les instructions vues jusqu'ici, FROM, WORKDIR, COPY, RUN, s'exécutent à un seul moment précis : pendant la construction de l'image, quand vous lancerez docker build au prochain guide. Une fois l'image construite, ces instructions ne se rejouent plus jamais.

Mais une image, seule, ne fait rien : c'est un modèle immuable, comme vu au guide 3. Il faut donc indiquer une dernière chose : quelle commande exécuter au moment où un conteneur démarre réellement à partir de cette image. C'est le rôle de CMD.

CMD : l'instruction de démarrage

Cette syntaxe, avec des crochets et des guillemets, s'appelle la forme « exec ». Chaque mot de la commande à exécuter est un élément séparé de la liste : ici, node est le programme, server.js est l'argument qu'on lui passe. C'est strictement équivalent à taper node server.js dans un terminal, mais Docker recommande cette forme en liste plutôt qu'une chaîne de texte unique, pour un fonctionnement plus prévisible.

Une seule instruction CMD utile par Dockerfile

Un Dockerfile peut contenir plusieurs instructions CMD, mais seule la dernière compte réellement : elle remplace toutes les précédentes. En pratique, n'en mettez jamais qu'une seule, à la toute fin du fichier, pour éviter toute confusion.

Pourquoi un conteneur s'arrête tout seul si CMD se termine

Un conteneur reste actif tant que le processus lancé par CMD tourne. Souvenez-vous de hello-world, au guide 8 : son processus affichait un message puis se terminait immédiatement, c'est exactement pour cela que son conteneur s'arrêtait tout seul dans la seconde. À l'inverse, server.js reste actif en permanence, en écoute sur un port, ce qui maintient le conteneur de Mini-Carnet en fonctionnement tant que vous ne l'arrêtez pas vous-même.

C'est un piège fréquent chez les débutants qui écrivent leur premier Dockerfile pour une vraie application : si le programme lancé par CMD se termine de lui-même, par exemple à cause d'une erreur, le conteneur entier s'arrête avec lui. Vous verrez au guide 19 comment diagnostiquer ce genre de situation avec docker logs.

Vérifiez que vous avez compris

Vous ajoutez une nouvelle route à server.js après avoir déjà construit l'image une première fois, mais vous ne reconstruisez pas l'image avant de relancer un conteneur. Ce nouveau conteneur va-t-il afficher cette modification ?

Non. Le code copié dans l'image au moment du docker build est figé dans cette image jusqu'à la prochaine construction. Un conteneur démarré à partir d'une image plus ancienne exécute le code tel qu'il était lors de cette construction précédente, pas votre version modifiée sur votre ordinateur.