¿Cuál es la diferencia entre CMD y ENTRYPOINT en un Dockerfile?
En Dockerfiles hay dos comandos que me parecen similares: CMD
y ENTRYPOINT
. Pero supongo que hay una diferencia (¿sutil?) entre ellos; de lo contrario, no tendría ningún sentido tener dos comandos para exactamente la misma cosa.
La documentación establece para CMD
-
El objetivo principal de un CMD es proporcionar valores predeterminados para un contenedor en ejecución.
y para ENTRYPOINT
:
Un ENTRYPOINT le ayuda a configurar un contenedor que puede ejecutar como ejecutable.
Entonces, ¿cuál es la diferencia entre esos dos comandos?
Docker tiene un punto de entrada predeterminado que es, /bin/sh -c
pero no tiene, un comando predeterminado.
Cuando ejecuta Docker de esta manera:
docker run -i -t ubuntu bash
el punto de entrada es el predeterminado /bin/sh -c
, la imagen es ubuntu
y el comando es bash
.
El comando se ejecuta a través del punto de entrada. es decir, lo que realmente se ejecuta es /bin/sh -c bash
. Esto permitió a Docker implementarlo RUN
rápidamente confiando en el analizador del shell.
Más tarde, la gente pidió poder personalizar esto, así ENTRYPOINT
que --entrypoint
nos presentaron.
Todo lo que sigue al nombre de la imagen, ubuntu
en el ejemplo anterior, es el comando y se pasa al punto de entrada. Al usar la CMD
instrucción, es exactamente como si estuviera ejecutando.
docker run -i -t ubuntu <cmd>
El parámetro del punto de entrada es <cmd>
.
También obtendrá el mismo resultado si escribe este comando docker run -i -t ubuntu
: se iniciará un shell bash en el contenedor porque en el Dockerfile de ubuntuCMD
se especifica un valor predeterminado :
CMD ["bash"]
.
Como todo se pasa al punto de entrada, puedes tener un comportamiento muy agradable en tus imágenes. El ejemplo de @Jiri es bueno, muestra cómo usar una imagen como "binaria". Cuando se usa ["/bin/cat"]
como punto de entrada y luego se hace docker run img /etc/passwd
, lo obtiene, /etc/passwd
es el comando y se pasa al punto de entrada, por lo que la ejecución del resultado final es simplemente /bin/cat /etc/passwd
.
Otro ejemplo sería tener cualquier cli como punto de entrada. Por ejemplo, si tiene una imagen de Redis, en lugar de ejecutarla docker run redisimg redis -H something -u toto get key
, puede simplemente tenerla ENTRYPOINT ["redis", "-H", "something", "-u", "toto"]
y luego ejecutarla así para obtener el mismo resultado docker run redisimg get key
:
Especifica ENTRYPOINT
un comando que siempre se ejecutará cuando se inicie el contenedor.
Especifica los CMD
argumentos que se enviarán al archivo ENTRYPOINT
.
Si desea crear una imagen dedicada a un comando específico, utilizaráENTRYPOINT ["/path/dedicated_command"]
De lo contrario, si desea crear una imagen para fines generales, puede dejarla ENTRYPOINT
sin especificar y usarla CMD ["/path/dedicated_command"]
, ya que podrá anular la configuración proporcionando argumentos a docker run
.
Por ejemplo, si su Dockerfile es:
FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
Ejecutar la imagen sin ningún argumento hará ping al 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
Ahora, ejecutar la imagen con un argumento hará ping al argumento:
$ 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
A modo de comparación, si su Dockerfile es:
FROM debian:wheezy
CMD ["/bin/ping", "localhost"]
Ejecutar la imagen sin ningún argumento hará ping al 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
Pero ejecutar la imagen con un argumento ejecutará el argumento:
docker run -it test bash
root@e8bb7249b843:/#
Consulte este artículo de Brian DeHamer para obtener aún más detalles: https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
Según los documentos de Docker ,
Tanto las instrucciones CMD como ENTRYPOINT definen qué comando se ejecuta cuando se ejecuta un contenedor. Hay pocas reglas que describen su cooperación.
- Dockerfile debe especificar al menos uno de los comandos
CMD
oENTRYPOINT
.ENTRYPOINT
debe definirse al utilizar el contenedor como ejecutable.CMD
debe usarse como una forma de definir argumentos predeterminados para unENTRYPOINT
comando o para ejecutar un comando ad-hoc en un contenedor.CMD
se anulará cuando se ejecute el contenedor con argumentos alternativos.
Las siguientes tablas muestran qué comando se ejecuta para diferentes combinaciones ENTRYPOINT
/CMD
:
--No ENTRYPOINT
╔════════════════════════════╦═════════════════════════════╗
║ No CMD ║ error, not allowed ║
╟────────────────────────────╫─────────────────────────────╢
║ CMD ["exec_cmd", "p1_cmd"] ║ exec_cmd p1_cmd ║
╟────────────────────────────╫─────────────────────────────╢
║ CMD ["p1_cmd", "p2_cmd"] ║ p1_cmd p2_cmd ║
╟────────────────────────────╫─────────────────────────────╢
║ CMD exec_cmd p1_cmd ║ /bin/sh -c exec_cmd p1_cmd ║
╚════════════════════════════╩═════════════════════════════╝
--ENTRYPOINT exec_entry p1_entry
╔════════════════════════════╦══════════════════════════════════╗
║ No CMD ║ /bin/sh -c exec_entry p1_entry ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD ["exec_cmd", "p1_cmd"] ║ /bin/sh -c exec_entry p1_entry ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD ["p1_cmd", "p2_cmd"] ║ /bin/sh -c exec_entry p1_entry ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD exec_cmd p1_cmd ║ /bin/sh -c exec_entry p1_entry ║
╚════════════════════════════╩══════════════════════════════════╝
--ENTRYPOINT ["exec_entry", "p1_entry"]
╔════════════════════════════╦═════════════════════════════════════════════════╗
║ No CMD ║ exec_entry p1_entry ║
╟────────────────────────────╫─────────────────────────────────────────────────╢
║ CMD ["exec_cmd", "p1_cmd"] ║ exec_entry p1_entry exec_cmd p1_cmd ║
╟────────────────────────────╫─────────────────────────────────────────────────╢
║ CMD ["p1_cmd", "p2_cmd"] ║ exec_entry p1_entry p1_cmd p2_cmd ║
╟────────────────────────────╫─────────────────────────────────────────────────╢
║ CMD exec_cmd p1_cmd ║ exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd ║
╚════════════════════════════╩═════════════════════════════════════════════════╝
Sí, esa es una buena pregunta. Aún no lo entiendo del todo, pero:
Entiendo que ENTRYPOINT
es el binario que se está ejecutando. Puede anular el punto de entrada mediante --entrypoint="".
docker run -t -i --entrypoint="/bin/bash" ubuntu
CMD es el argumento predeterminado del contenedor. Sin punto de entrada, el argumento predeterminado es el comando que se ejecuta. Con el punto de entrada, cmd se pasa al punto de entrada como argumento. Puede emular un comando con punto de entrada.
# no entrypoint
docker run ubuntu /bin/cat /etc/passwd
# with entry point, emulating cat command
docker run --entrypoint="/bin/cat" ubuntu /etc/passwd
Entonces, la principal ventaja es que con el punto de entrada puedes pasar argumentos (cmd) a tu contenedor. Para lograr esto, necesita usar ambos:
# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/cat"]
y
docker build -t=cat .
entonces puedes usar:
docker run cat /etc/passwd
# ^^^^^^^^^^^
# CMD
# ^^^
# image (tag)- using the default ENTRYPOINT
En una palabra:
- CMD establece el comando y/o los parámetros predeterminados en el punto de entrada, que se pueden sobrescribir desde la línea de comando cuando se ejecuta el contenedor acoplable (
docker run example "override"
). - El comando ENTRYPOINT se sobrescribe antes de la imagen con su propio indicador de línea de comando (
docker run --entrypoint="override" image
). Luego, todos los argumentos CMD se agregarán después de ENTRYPOINT como parámetros. En muchos casos, el punto de entrada se establece comosh -c
. Puedes encontrar esto condocker inspect image -f '{{ .Config.Entrypoint }}'
- Ambos se pueden combinar. (
docker run --entrypoint="/docker-entrypoint.sh" image arg1 arg2
)
Si necesita más detalles o le gustaría ver la diferencia en un ejemplo, hay una publicación de blog que compara exhaustivamente CMD y ENTRYPOINT con muchos ejemplos: https://codewithyury.com/docker-run-vs-cmd-vs-entrypoint/
Diferencia entre CMD y ENTRYPOINT por intuición :
- PUNTO DE ENTRADA: comando a ejecutar cuando se inicia el contenedor.
- CMD: comando para ejecutar cuando se inicia el contenedor o argumentos para ENTRYPOINT si se especifica.
Sí, es confuso.
Puede anular cualquiera de ellos al ejecutar Docker Run.
Diferencia entre CMD y ENTRYPOINT por ejemplo :
docker run -it --rm yourcontainer /bin/bash <-- /bin/bash overrides CMD
<-- /bin/bash does not override ENTRYPOINT
docker run -it --rm --entrypoint ls yourcontainer <-- overrides ENTRYPOINT with ls
docker run -it --rm --entrypoint ls yourcontainer -la <-- overrides ENTRYPOINT with ls and overrides CMD with -la
Más sobre la diferencia entre CMD
y ENTRYPOINT
:
Argumentos docker run
como /bin/bash anulan cualquier comando CMD que escribimos en Dockerfile.
ENTRYPOINT no se puede anular en tiempo de ejecución con comandos normales como docker run [args]
. Los args
al final de docker run [args]
se proporcionan como argumentos para ENTRYPOINT. De esta manera podemos crear un container
binario normal como ls
.
Entonces CMD puede actuar como parámetros predeterminados para ENTRYPOINT y luego podemos anular los argumentos de CMD desde [args].
ENTRYPOINT se puede anular con --entrypoint
.