In Dockerfiles ci sono due comandi che mi sembrano simili: CMD
e ENTRYPOINT
. Ma immagino che ci sia una (sottile?) differenza tra loro - altrimenti non avrebbe senso avere due comandi per la stessa cosa.
La documentazione afferma per CMD
Lo scopo principale di un CMD è quello di fornire i valori predefiniti per un contenitore in esecuzione.
e per ENTRYPOINT
:
Un ENTRYPOINT aiuta a configurare un contenitore che si può eseguire come eseguibile.
Quindi, qual è la differenza tra questi due comandi?
Docker ha un entrypoint predefinito che è /bin/sh -c
ma non ha un comando predefinito.
Quando si esegue docker in questo modo
docker run -i -t ubuntu bash
l'entrypoint è il predefinito /bin/sh -c
, l'immagine è ubuntu
e il comando è bash
.
Il comando viene eseguito tramite l'entrypoint. cioè, la cosa effettiva che viene eseguita è /bin/sh -c bash
. Questo ha permesso a Docker di implementare rapidamente RUN
affidandosi al parser della shell.
Più tardi, la gente ha chiesto di poter personalizzare questo, così sono stati introdotti ENTRYPOINT
e --entrypoint
.
Tutto ciò che viene dopo ubuntu
nell'esempio sopra è il comando e viene passato all'entrypoint. Quando si usa l'istruzione CMD
, è esattamente come se si facesse docker run -i -t ubuntu <cmd>
. <cmd>
sarà il parametro dell'entrypoint.
Otterrete lo stesso risultato anche se invece digitate questo comando docker run -i -t ubuntu
. Si avvierà ancora una shell bash nel contenitore a causa del ubuntu Dockerfile specificato un CMD predefinito: CMD ["bash"]
Poiché tutto viene passato all'entrypoint, puoi avere un comportamento molto bello dalle tue immagini. L'esempio di @Jiri è buono, mostra come usare un'immagine come un "binario". Quando si usa ["/bin/cat"]
come entrypoint e poi si fa docker run img /etc/passwd
, si ottiene, /etc/passwd
è il comando e viene passato all'entrypoint quindi l'esecuzione del risultato finale è semplicemente /bin/cat /etc/passwd
.
Un altro esempio potrebbe essere quello di avere qualsiasi cli come entrypoint. Per esempio, se avete un'immagine redis, invece di eseguire docker run redisimg redis -H qualcosa -u toto get key
, potete semplicemente avere ENTRYPOINT ["redis", "-H", "qualcosa", "-u", "toto"]
e poi eseguire così per lo stesso risultato: docker run redisimg get key
.
Il ENTRYPOINT
specifica un comando che sarà sempre eseguito all'avvio del contenitore.
Il CMD
specifica gli argomenti che saranno forniti all' ENTRYPOINT
.
Se vuoi fare un'immagine dedicata a un comando specifico userai ENTRYPOINT ["/path/dedicated_command"]
Altrimenti, se vuoi creare un'immagine per scopi generali, puoi lasciare ENTRYPOINT
non specificato e usare CMD ["/path/dedicated_command"]
in quanto sarai in grado di sovrascrivere l'impostazione fornendo argomenti a docker run
.
Per esempio, se il tuo Dockerfile è:
FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
L'esecuzione dell'immagine senza alcun argomento farà il ping di localhost:
$ docker run -it test
PING localhost (127.0.0.1): 48 data bytes
56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.096 ms
56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.088 ms
56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.088 ms
^C--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.088/0.091/0.096/0.000 ms
Ora, eseguendo l'immagine con un argomento si farà il ping dell'argomento:
$ docker run -it test google.com
PING google.com (173.194.45.70): 48 data bytes
56 bytes from 173.194.45.70: icmp_seq=0 ttl=55 time=32.583 ms
56 bytes from 173.194.45.70: icmp_seq=2 ttl=55 time=30.327 ms
56 bytes from 173.194.45.70: icmp_seq=4 ttl=55 time=46.379 ms
^C--- google.com ping statistics ---
5 packets transmitted, 3 packets received, 40% packet loss
round-trip min/avg/max/stddev = 30.327/36.430/46.379/7.095 ms
Per confronto, se il tuo Dockerfile è:
FROM debian:wheezy
CMD ["/bin/ping", "localhost"]
L'esecuzione dell'immagine senza alcun argomento farà il ping di localhost:
$ docker run -it test
PING localhost (127.0.0.1): 48 data bytes
56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.076 ms
56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.087 ms
56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.090 ms
^C--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.076/0.084/0.090/0.000 ms
Ma eseguendo l'immagine con un argomento, verrà eseguito l'argomento:
docker run -it test bash
root@e8bb7249b843:/#
Vedere questo articolo di Brian DeHamer per ulteriori dettagli: https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
Sì, questa è una buona domanda. Non l'ho ancora capito del tutto, ma:
Ho capito che ENTRYPOINT
è il binario che viene eseguito. Puoi sovrascrivere entrypoint con --entrypoint="".
docker run -t -i --entrypoint="/bin/bash" ubuntu
CMD è l'argomento predefinito del contenitore. Senza entrypoint, l'argomento predefinito è il comando che viene eseguito. Con entrypoint, cmd viene passato a entrypoint come argomento. Potete emulare un comando con entrypoint.
# no entrypoint
docker run ubuntu /bin/cat /etc/passwd
# with entry point, emulating cat command
docker run --entrypoint="/bin/cat" ubuntu /etc/passwd
Quindi, il vantaggio principale è che con entrypoint potete passare argomenti (cmd) al vostro contenitore. Per fare questo, dovete usare entrambi:
# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/cat"]
e
docker build -t=cat .
allora puoi usare:
docker run cat /etc/passwd
# ^^^^^^^^^^^
# CMD
# ^^^
# image (tag)- using the default ENTRYPOINT