In Dockerfiles zijn er twee commando's die er voor mij hetzelfde uitzien: CMD
en ENTRYPOINT
. Maar ik denk dat er een (subtiel?) verschil tussen beide is - anders zou het geen zin hebben om twee commando's voor precies hetzelfde te hebben.
De documentatie zegt voor CMD
Het belangrijkste doel van een CMD is om standaards te geven voor een uitvoerende container.
en voor ENTRYPOINT
:
Een ENTRYPOINT helpt u bij het configureren van een container die u als een executable kunt uitvoeren.
Dus, wat's het verschil tussen deze twee commando's?
Docker heeft een standaard ingangspunt dat /bin/sh -c
is, maar heeft geen standaard commando.
Wanneer je docker als volgt uitvoert:
docker run -i -t ubuntu bash
is het ingangspunt de standaard /bin/sh -c
, de image is ubuntu
en het commando is bash
.
Het commando wordt uitgevoerd via het ingangspunt, d.w.z. het werkelijke ding dat wordt uitgevoerd is /bin/sh -c bash
. Dit stond Docker toe om RUN
snel te implementeren door te vertrouwen op de shell's parser.
Later vroegen mensen om dit te kunnen aanpassen, dus ENTRYPOINT
en --entrypoint
werden geïntroduceerd.
Alles na ubuntu
in het bovenstaande voorbeeld is het commando en wordt doorgegeven aan het ingangspunt. Wanneer je de CMD
instructie gebruikt, is het precies alsof je docker run -i -t ubuntu <cmd>
zou doen. <cmd>
zal de parameter van het entrypoint zijn.
Je zal hetzelfde resultaat krijgen als je in de plaats dit commando docker run -i -t ubuntu
typt. Je zal nog steeds een bash shell starten in de container omdat de ubuntu Dockerfile een standaard CMD specificeerde: CMD ["bash"]
.
Omdat alles wordt doorgegeven aan het entrypoint, kun je een heel mooi gedrag van je images krijgen. Het voorbeeld van @Jiri is goed, het laat zien hoe je een image kunt gebruiken als een "binary". Wanneer je ["/bin/cat"]
als entrypoint gebruikt en dan docker run img /etc/passwd
doet, krijg je het, /etc/passwd
is het commando en wordt doorgegeven aan het entrypoint dus het eindresultaat is simpelweg /bin/cat /etc/passwd
.
Een ander voorbeeld zou zijn om een willekeurige cli als entrypoint te hebben. Bijvoorbeeld, als je een redis image hebt, in plaats van docker run redisimg redis -H something -u toto get key
, kun je simpelweg ENTRYPOINT ["redis", "-H", "something", "-u", "toto"]
uitvoeren en dan op deze manier uitvoeren voor hetzelfde resultaat: docker run redisimg get key
.
De ENTRYPOINT
specificeert een commando dat altijd zal worden uitgevoerd wanneer de container start.
De CMD
specificeert argumenten die aan de ENTRYPOINT
worden meegegeven.
Als je een image wilt maken voor een specifiek commando, gebruik je ENTRYPOINT ["/path/dedicated_command"]
.
Anders, als je een image voor algemeen gebruik wilt maken, kun je ENTRYPOINT
ongespecificeerd laten en CMD ["/path/dedicated_command"]
gebruiken, omdat je dan de instelling kunt opheffen door argumenten mee te geven aan docker run
.
Bijvoorbeeld, als uw Dockerfile is:
FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
Het draaien van het image zonder enig argument zal de localhost pingen:
$ 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
Nu, het image met een argument draaien zal het argument pingen:
$ 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
Ter vergelijking, als je Dockerfile is:
FROM debian:wheezy
CMD ["/bin/ping", "localhost"]
Het draaien van het image zonder enig argument zal de localhost pingen:
$ 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
Maar het draaien van het image met een argument zal het argument draaien:
docker run -it test bash
root@e8bb7249b843:/#
Zie dit artikel van Brian DeHamer voor nog meer details: https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/
Ja, dat is een goede vraag. Ik begrijp het nog niet helemaal, maar:
Ik begrijp dat ENTRYPOINT
de binary is die wordt uitgevoerd. Je kunt entrypoint overschrijven door --entrypoint="".
docker run -t -i --entrypoint="/bin/bash" ubuntu
CMD is het standaard argument voor container. Zonder ingangspunt is het standaard argument het commando dat wordt uitgevoerd. Met entrypoint wordt cmd als argument aan entrypoint doorgegeven. Je kunt een commando emuleren met entrypoint.
# no entrypoint
docker run ubuntu /bin/cat /etc/passwd
# with entry point, emulating cat command
docker run --entrypoint="/bin/cat" ubuntu /etc/passwd
Het belangrijkste voordeel is dus dat je met entrypoint argumenten (cmd) kunt doorgeven aan je container. Om dit te bereiken, moet je beide gebruiken:
# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/cat"]
en
docker build -t=cat .
dan kunt u gebruiken:
docker run cat /etc/passwd
# ^^^^^^^^^^^
# CMD
# ^^^
# image (tag)- using the default ENTRYPOINT