ENTRYPOINT VS CMD

Un peu de théorie : la différence entre ENTRYPOINT et CMD

DONNE MOI LE RÉSUMÉ

Depuis le début, dans chacun de nos Dockerfile, nous utilisons deux mots clefs de manière conjointement : ENTRYPOINT et CMD. Cela peut être un peu compliqué de comprendre la différence entre les deux et pourquoi utiliser les deux. Je propose que l’on regarde cela ensemble avec quelques cas pratiques.

Ce qu’il faut comprendre, c’est qu’il faut voir le mot clef ENTRYPOINT comme le point d’entrée de notre conteneur. On lui passe le chemin vers un script que l’on souhaite qu’il soit constamment exécuté, peu importe si la personne qui va démarrer notre conteneur souhaite changer la commande.

On peut voir ENTRYPOINT comme un passage obligatoire, une sorte de douane que l’on souhaite faire passer à tous les utilisateurs de notre conteneur. Cela ne veut pas dire que l’utilisateur final ne peut pas modifier l’ENTRYPOINT, mais juste que c’est à ses risques et périls de le faire en utilisant l’option --entrypoint. En d’autre terme, modifier la commande (définis par CMD)que lance un conteneur, c’est courant, changer l’ENTRYPOINT c’est plus rare.

Ainsi, dans le script que l’on donne à ENTRYPOINT, on y définit l’ensemble de ce qui va être nécessaire au bon fonctionnement de l’application et de la commande CMD. C’est important aussi de savoir que l’ensemble de ce que l’on définit dans CMD sera passé en paramètres de l’ENTRYPOINT.

Ok assez de théorie, voyons la pratique maintenant avec quelques exemples.

Commençons par créer un nouveau dossier entrypoint-cmd

mkdir entrypoint-cmd
cd entrypoint-cmd

Dans cette partie, nous allons modifier plusieurs fois notre script et notre Dockerfile. N’oublie pas de reconstruire à chaque fois l’image si tu ne vois pas les changements dans tes conteneurs.

Écrivons un premier script entrypoint.sh tout simple qui commence par nous dire bonjour puis affiche ces arguments.

#!/bin/bash

echo "Hello Tornade.io"

echo "Voici les arguments: $@"

On crée ensuite un Dockerfile tout simple, qui ne fait que copier ce script, le rend exécutable et le définis comme ENTRYPOINT par défaut. On passe aussi une CMD par défaut.

FROM debian

COPY ./entrypoint.sh /usr/bin/entrypoint.sh
RUN chmod +x /usr/bin/entrypoint.sh

ENTRYPOINT ["entrypoint.sh"]
CMD ["des parametres ", "par defaut"]

On construit notre image et lance notre conteneur dans la foulée.

docker build --tag entrypoint-vs-cmd .
docker run entrypoint-vs-cmd
> Hello Tornade.io
> Voici les arguments: des parametres  par defaut

On voit bien que notre script d’entrypoint est exécuté et que ses paramètres sont ceux définis dans CMD.

Regardons maintenant ce qu’il se passe lorsque l’on modifie la commande a exécuté au lancement d’un autre conteneur.

docker run entrypoint-vs-cmd 'des autres ' 'parametres'
> Hello Tornade.io
> Voici les arguments : des autres  parametres 

Comme la première fois, c’est bien notre entrypoint.sh qui est exécuté et les arguments donnés à docker run ont pris la place de ceux du Dockerfile de CMD.

Ok, mais admettons maintenant que je veux réellement exécuter quelque chose et pas juste passer des paramètres qui vont être répétés.

Reprenons notre Dockerfile, et modifions les paramètres dans CMD pour effectuer un ls -la par défaut.

FROM debian

COPY ./entrypoint /usr/bin/entrypoint.sh
RUN chmod +x /usr/bin/entrypoint.sh

ENTRYPOINT ['entrypoint.sh']
CMD ["ls", "-la"]

Modifions aussi notre script entrypoint.sh pour qu’il exécute la commande qu’il reçoit en paramètres.

#!/bin/bash

set -e # On dit à bash : si y'a la moindre erreur, tu arrete l'execution du script.

echo "Tu passeras par la dans tous les cas !"

exec "$@" # Execute ce qui est passé en paramètres

Reconstruisons notre image et lançons la

docker build --tag entrypoint-vs-cmd .
docker run entrypoint-vs-cmd
> Tu passeras par la dans tous les cas !
> total 60
drwxr-xr-x   1 root root 4096 Feb 17 13:41 .
drwxr-xr-x   1 root root 4096 Feb 17 13:41 ..
-rwxr-xr-x   1 root root    0 Feb 17 13:41 .dockerenv
lrwxrwxrwx   1 root root    7 Feb 11 00:00 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Jan 28 21:20 boot
drwxr-xr-x   5 root root  340 Feb 17 13:41 dev
drwxr-xr-x   1 root root 4096 Feb 17 13:41 etc
drwxr-xr-x   2 root root 4096 Jan 28 21:20 home
lrwxrwxrwx   1 root root    7 Feb 11 00:00 lib -> usr/lib
lrwxrwxrwx   1 root root    9 Feb 11 00:00 lib64 -> usr/lib64
drwxr-xr-x   2 root root 4096 Feb 11 00:00 media
drwxr-xr-x   2 root root 4096 Feb 11 00:00 mnt
drwxr-xr-x   2 root root 4096 Feb 11 00:00 opt
dr-xr-xr-x 225 root root    0 Feb 17 13:41 proc
drwx------   2 root root 4096 Feb 11 00:00 root
drwxr-xr-x   3 root root 4096 Feb 11 00:00 run
lrwxrwxrwx   1 root root    8 Feb 11 00:00 sbin -> usr/sbin
drwxr-xr-x   2 root root 4096 Feb 11 00:00 srv
dr-xr-xr-x  13 root root    0 Feb 17 13:41 sys
drwxrwxrwt   2 root root 4096 Feb 11 00:00 tmp
drwxr-xr-x   1 root root 4096 Feb 11 00:00 usr
drwxr-xr-x  11 root root 4096 Feb 11 00:00 var

On voit bien que notre script entrypoint est exécuté et lance ensuite la commande ls -la donnée en paramètre par défaut avec CMD

Si on modifie à la volée les paramètres au lancement avec docker run, on voit de nouveau que entrypoint n’est pas modifié, seulement ces arguments.

docker run entrypoint-vs-cmd echo "J'adore Tornade.io"
> Tu passeras par la dans tous les cas !
> J'adore Tornade.io

Grâce à cela, il est possible de forcer les utilisateurs de nos images a passé par un chemin et une suite de commande que nous dirigeons dans le ENTRYPOINT. Il est tout de même possible de modifier l’entrypoint avec docker run --entrypoint <script>, mais c’est plutôt rare.

L’intérêt de la distinction entre CMD et ENTRYPOINT prend tout son sens si nous conteneurisons une application ayant des dépendances avec d’autre système. Si pour démarrer, ton appli, tu dois obligatoirement te connecter à une base de donnée, il peut être judicieux d’attendre que la connexion soit possible dans le script d’entrypoint pour ensuite démarrer ton application.

Un autre exemple et si tu dois absolument télécharger ou créer des fichiers à la volée avant de lancer ton application. On peut faire ces étapes dans le script d’entrypoint, nous assurant de la création comme il faut des fichiers nécessaires avant de réellement démarrer l’application.

Dans la majorité des applications déployés par conteneur, entrypoint est utilisé comme un script de préparation de dernière minute, avant de lancer l’application en prod.

On commence à avoir pas mal de conteneur / d’image inutile. Il ne faut pas hésiter à nettoyer tout cela. Pour rappel, on regarde tous les conteneurs avec docker ps -a et on les supprime avec docker rm ID. Pour les images, docker image ls -a et on les supprime avec docker rmi ID ou docker image rm ID .

Pro tip : il est possible de supprimer tous d’un coup

docker rm $(docker ps -aq)
docker rmi $(docker image ls -aq)

Takeaways

ENTRYPOINT et CMD ont deux rôles différents.

ENTRYPOINT peut être vu comme le point d’entrée, le passage obligatoire pour l’exécution de la commande CMD.

Lorsque l’on modifie avec docker run la commande a exécuté, on modifie à la volée le contenu de CMD.

Il est possible de modifier l’entrypoint avec l’option --entrypoint <script> .