No Dockerfiles há dois comandos que se parecem comigo: CMD
e ENTRYPOINT
. Mas acho que há uma diferença (subtil?) entre eles - caso contrário não faria sentido ter dois comandos para a mesma coisa.
A documentação indica para o `CMD'.
O objetivo principal de um CMD é fornecer padrões para um recipiente de execução.
e para o ENTRYPOINT
:
Um ENTRYPOINT ajuda-o a configurar um contentor que pode correr como executável.
Então, qual'é a diferença entre esses dois comandos?
O Docker tem um ponto de entrada padrão que é `/bin/sh -c' mas não tem um comando padrão.
Quando se gere uma doca como esta:
docker run -i -t ubuntu bash
o ponto de entrada é o padrão /bin/sh -c', a imagem é
ubuntu' e o comando é `bash'.
O comando é executado através do ponto de entrada, ou seja, a coisa real que é executada é /bin/sh -c bash'. Isto permitiu ao Docker implementar
RUN` rapidamente, confiando no parser da shell.
Mais tarde, as pessoas pediram para poder personalizar isto, por isso foram introduzidos o ENTRYPOINT' e o
--entrypoint'.
Tudo após o ubuntu
no exemplo acima é o comando e é passado para o ponto de entrada. Ao utilizar a instrução CMD
, é exatamente como se você estivesse fazendo docker run -i -t ubuntu <cmd>
. O <cmd>
será o parâmetro do ponto de entrada.
Você também terá o mesmo resultado se você digitar este comando docker run -i -t ubuntu
. Você ainda irá iniciar uma shell bash no container por causa do ubuntu Dockerfile especificou um CMD padrão: CMD ["bash"]
Como tudo é passado para o ponto de entrada, você pode ter um comportamento muito agradável a partir de suas imagens. @Jiri exemplo é bom, mostra como usar uma imagem como um "binário". Ao utilizar ["/bin/cat"]
como ponto de entrada e depois fazer docker run img /etc/passwd
, você recebe o comando /etc/passwd
e é passado para o ponto de entrada, então o resultado final da execução é simplesmente /bin/cat /etc/passwd
.
Outro exemplo seria ter qualquer cliente como ponto de entrada. Por exemplo, se você tiver uma imagem redis, ao invés de executar docker run redisimg redisimg -H something -u toto get key', você pode simplesmente ter
ENTRYPOINT ["redis", "-H", "something", "-u", "toto"]e então executar assim para o mesmo resultado:
docker run redisimg get key'.
O ENTRYPOINT
especifica um comando que será sempre executado quando o container for iniciado.
O CMD
especifica os argumentos que serão alimentados ao ENTRYPOINT
.
Se você quiser fazer uma imagem dedicada a um comando específico você utilizará ENTRYPOINT ["/caminho/comando_edicado"]
Caso contrário, se você quiser fazer uma imagem para fins gerais, você pode deixar o ENTRYPOINT
não especificado e utilizar CMD ["/caminho/comando_dedicado"]
, pois você será capaz de anular a configuração, fornecendo argumentos para docker run
.
Por exemplo, se o seu Dockerfile estiver:
FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
A execução da imagem sem qualquer argumento vai pingar o local:
$ 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
Agora, rodar a imagem com um argumento vai pingar o 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
Para comparação, se o seu Dockerfile estiver:
FROM debian:wheezy
CMD ["/bin/ping", "localhost"]
A execução da imagem sem qualquer argumento vai pingar o local:
$ 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
Mas correr a imagem com um argumento vai correr o argumento:
docker run -it test bash
root@e8bb7249b843:/#
Veja este artigo de Brian DeHamer para obter ainda mais detalhes: https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
Sim, essa é uma boa pergunta. Ainda não a entendo totalmente, mas:
Eu entendo que ENTRYPOINT
é o binário que está sendo executado. Você pode substituir o ponto de entrada por --entrypoint=""".
docker run -t -i --entrypoint="/bin/bash" ubuntu
CMD é o argumento padrão para o recipiente. Sem ponto de entrada, o argumento padrão é o comando que é executado. Com ponto de entrada, o cmd é passado para ponto de entrada como argumento. Você pode emular um comando com ponto 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
Portanto, a principal vantagem é que com o ponto de entrada você pode passar argumentos (cmd) para o seu recipiente. Para conseguir isso, você precisa usar ambos:
# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/cat"]
e
docker build -t=cat .
então você pode usar:
docker run cat /etc/passwd
# ^^^^^^^^^^^
# CMD
# ^^^
# image (tag)- using the default ENTRYPOINT