Outils pour utilisateurs

Outils du site


sysadmin:docker:cmd_vs_entrypoint

Ceci est une ancienne révision du document !


Docker : CMD et ENTRYPOINT

Les directives CMD et ENTRYPOINT utilisées dans le Dockerfile permettent toutes les deux de définir une commande initiale présente dans l'image qui sera invoquée au démarrage du conteneur.

Ces deux directives existent et présentent quelques différences. Selon les besoins on utilisera l'une, l'autre ou les deux.

Au final, CMD ou ENTRYPOINT permet d'identifier quel fichier exécutable devrait être invoqué lorsque le conteneur est démarré à partir de l'image.

Tenter de démarrer une image qui ne contient aucune directive CMD ou ENTRYPOINT retourne une erreur:

docker run alpine
FATA[0000] Error response from daemon: No command specified
De nombreuses images de distributions GNU/Linux présentes sur le dépôt Docker Hub utilise les shells (sh, bash) avec la directive CMD, cela permet à l'utilisateur d'être directement introduit dans un shell lorsque le conteneur est créé avec cette image (si les argument -i et -t sont utilisés lors de l'appel à docker run).

La commande définie dans le Dockerfile peut être redéfinie sur la ligne de commande en spécifiant un argument après le nom de l'image:

docker run --rm --name test_cmd -t busybox:1.34-glibc /bin/sh -c "ping -c 2 localhost"
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.056 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.089 ms
 
--- localhost ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.056/0.072/0.089 ms

On peut également redéfinir la valeur de l' ENTRYPOINT via l'option --entrypoint, dans ce cas l'option est placée avant le nom de l'image:

docker run --rm --name test_cmd --entrypoint hostname -t busybox:1.34-glibc
636ecd585d67

Comme il est plus simple pour l'usager final de redéfinir la directive CMD, la recommandation générale est de l'utiliser de préférence dans le Dokerfile quand on souhaite permettre à l'utilisateur de choisir quel exécutable il souhaite invoquer lors du démarrage du conteneur (offrir à l'utilisateur de la flexibilité).

Au contraire la directive ENTRYPOINT s'utilise plutôt dans les cas ou le conteneur définit un environnement particulier pour un exécutable, un service et qu'il n' est pas prévu que l'usager final redéfinisse cet exécutable.

La même chose est faisable avec la directive CMD cependant l'usage de ENTRYPOINT envoi un signal fort indiquant que le conteneur est conçu exclusivement dans le but d’invoquer correctement cet exécutable.

Shell vs Exec

Les directives CMD et ENTRYPOINT supportent les deux formes d'invocation possibles: shell ou exec.

Dans l'extrait ci-dessous le Dockerfile utilise l'invocation shell

FROM debian:stretch-slim
RUN apt-get update && apt-get install -y iputils-ping procps

# Syntaxe pour invocation type shell 
CMD ping localhost

Si on crée l'image puis le conteneur, tout fonctionne normalement

# création de l'image à partir du Dockerfile présent dans le répertoire courant
docker image build -t "phobos/debian:shell" . 
 
# Création d'un conteneur utilisant cette image
docker container run --rm --detach --name debian_shell phobos/debian:shell
 
# Si on affiche les conteneur en cours d’exécution on peut lire la commande lancée
# dans notre conteneur
docker container list
CONTAINER ID   IMAGE                   COMMAND                  CREATED          NAMES
886ab5702130   phobos/debain:shell    "/bin/sh -c 'ping lo…"   30 seconds ago   busybox_shell
 
# Si on affiche les processus s'exécutant depuis l'intérieur du conteneur
docker container exec debian_shell ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 17:23 ?        00:00:00 /bin/sh -c ping localhost
root           7       1  0 17:23 ?        00:00:00 ping localhost
root           8       0  0 17:25 ?        00:00:00 ps -f

On visualise qu'en effet le processus avec le PID 1 est un shell demandant l’exécution de la commande ping. La commande ping est le processus fils avec ici le PID 7, il a bien pour parent ( PPID 1) le shell.

Ce mode de fonctionnement peut poser des problèmes dans le cas ou l'on souhaite communiquer avec le processus via des signaux POSIX car le shell ne communiquera pas ces signaux aux processus enfants.

Un autre problème pouvant se présenter en cas de construction d'une image minimale, c'est l'absence d'un shell. Lorsque Docker construit la commande a exécuter, il ne vérifie pas la présence du shell à l'intérieur du conteneur, si le binaire /bin/sh n'est pas présent dans l'image le démarrage du conteneur échouera systématiquement.

Une meilleure option est donc d'utiliser la forme d'invocation exec

Pour notre exemple le Dockerfile prend alors la forme suivante:

FROM debian:stretch-slim
RUN apt-get update && apt-get install -y iputils-ping procps

# Syntaxe pour invocation type exec
CMD ["/bin/ping", "localhost"]
# Construction d'une nouvelle image
 docker image build -t "phobos/debian:exec" .
 
# Création du conteneur avec la nouvelle image
docker container run --detach --rm --name debian_exec phobos/debian:exec
 
# affiche les processus en cours d'execution 
# visibles depuis l’intérieur du conteneur
docker container exec debian_exec ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 17:43 ?        00:00:00 /bin/ping localhost
root           7       0  0 17:44 ?        00:00:00 ps -f

Cette fois on voit que la commande s’exécute directement sans intervention intermédiaire du shell avec le PID 1.

Quelle que soit la directive CMD ou ENTRYPOINT la recommandation générale est d'utiliser la forme d'invocation exec.

Utilisation conjointe

Jusqu'à présent on a proposer d'utiliser soit CMD soit ENTRYPOINT mais pas les deux conjointement.

En combinant ENTRYPOINT et CMD

Références

sysadmin/docker/cmd_vs_entrypoint.1640800433.txt.gz · Dernière modification : 2021/12/29 17:53 de yoann