在Dockerfile中,有两个命令与我类似:CMD和ENTRYPOINT。但我想它们之间有一个(微妙的)区别——否则,对同一件事使用两个命令是没有意义的。

CMD的文档状态-

CMD的主要目的是为正在执行的容器提供默认值。

对于入口点:

ENTRYPOINT帮助您配置可以作为可执行文件运行的容器。

那么,这两个命令之间有什么区别?


当前回答

Docker有一个默认入口点/bin/sh-c,但没有默认命令。

当你这样运行docker时:docker运行-i-t ubuntu bash入口点是默认的/bin/sh-c,图像是ubuntu,命令是bash。

该命令通过入口点运行。即,实际执行的是/bin/sh-cbash。这允许Docker依靠shell的解析器快速实现RUN。

后来,人们要求能够定制这个,所以引入了ENTRYPOINT和--ENTRYPOINT。

在上面的示例中,图像名称ubuntu之后的所有内容都是命令,并传递给入口点。当使用CMD指令时,它就像您在执行docker运行-i-t ubuntu<cmd>入口点的参数为<cmd>。

如果改为键入以下命令docker run-i-t ubuntu,也会得到相同的结果:bash shell将在容器中启动,因为在ubuntu Dockerfile中指定了默认CMD:CMD[“bash”]。

当一切都传递到入口点时,您可以从图像中获得非常好的行为@Jiri示例很好,它展示了如何将图像用作“二进制”。当使用[“/bin/cat”]作为入口点,然后执行docker运行img/etc/passwd时,您会发现,/etc/passwd是一个命令,并被传递到入口点,因此最终执行的结果只是/bin/cat/etc/passwd。

另一个示例是将任意cli作为入口点。例如,如果您有一个redis映像,而不是运行docker run redisig redis-H something-u to get key,您可以简单地使用ENTRYPOINT[“redis”,“-H”,“something”,“-u”,”toto“],然后像这样运行以获得相同的结果:docker run reisig get key。

其他回答

我已经阅读了所有答案,我想总结一下,以便更好地理解以下内容:

首先,在容器中执行的整个命令包括两部分:命令和参数

ENTRYPOINT定义当容器已启动(用于命令)CMD指定传递给ENTRYPOINT的参数(用于参数)

在《Kubernetes In Action》一书中指出了一个重要的注意事项。(第7章)

尽管可以使用CMD指令指定命令要在运行映像时执行,正确的方法是执行通过ENTRYPOINT指令,并仅在以下情况下指定CMD希望定义默认参数。

你也可以阅读这篇文章,以简单的方式获得很好的解释

Dockerfile文件中提到的CMD命令可以通过docker run命令覆盖,而ENTRYPOINT不能覆盖。

通过从头开始重建OS映像(只需从头开始编写,并在dockerfile中使用COPY复制最小的文件系统),我了解到,

如果没有在dockerfile中指定ENTRYPOINT和CMD,docker将使用

/bin/sh -c

作为默认ENTRYPOINT,如果在docker文件中定义CMD或在运行容器时传递命令行参数(这将覆盖已定义的CMD),则将采用CMD。

假设您传递一个参数(或在dockerfile中定义CMD)ls,那么它将被馈送到ENTRYPOINT。即,

/bin/sh -c ls

/bin/sh-c将运行传递给它的任何参数。您将获得“ls”命令的输出,然后容器将退出。


ubuntu映像没有显式定义ENTRYPOINT,因此docker将使用/bin/sh-c,但包含CMD定义,即bash运行容器的以下命令,

docker container run -it ubuntu

Docker实际上使用ENTRYPOINT作为/bin/sh-c,然后使用bash为其提供数据最终运行的是

/bin/sh -c bash

它启动交互式bash终端(仅当如上所述指定了-i标志并可选地指定了-t以获得类似本地终端的体验时)

当您通过命令行提供参数时,bash将被替换为您传递的任何内容,输出将根据该内容,即

/bin/sh -c passed_argument

您可以定义自定义ENTRYPOINT,它将覆盖默认ENTRYPOINT,但随后需要相应地使用CMD。

对于dockerfile中的RUN命令,它不考虑定义的ENTRYPOINT和CMD,而是运行指定的命令,因为它们被提供给中间容器中的交互式bash终端

根据docker文档,

CMD和ENTRYPOINT指令都定义执行什么命令当运行容器时。很少有规则描述合作。Dockerfile应至少指定CMD或ENTRYPOINT命令之一。将容器用作可执行文件时,应定义ENTRYPOINT。CMD应用作为ENTRYPOINT命令或在容器当使用可选参数运行容器时,CMD将被重写。

下表显示了对不同ENTRYPOINT/CMD组合执行的命令:

--无入口点

╔════════════════════════════╦═════════════════════════════╗
║ 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  ║
╚════════════════════════════╩═════════════════════════════╝

--入口点执行_入口p1_入口

╔════════════════════════════╦══════════════════════════════════╗
║ 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   ║
╚════════════════════════════╩══════════════════════════════════╝

--入口[“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  ║
╚════════════════════════════╩═════════════════════════════════════════════════╝

ENTRYPOINT指定在容器启动时始终执行的命令。

CMD指定将被馈送到ENTRYPOINT的参数。

如果要使图像专用于特定命令,则将使用ENTRYPOINT[“/path/dessisted_command”]

否则,如果您想为通用目的生成图像,可以不指定ENTRYPOINT,使用CMD[“/path/dessisted_command”],因为您可以通过为docker运行提供参数来覆盖设置。

例如,如果Dockerfile是:

FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]

在没有任何参数的情况下运行映像将ping本地主机:

$ 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

现在,运行带有参数的图像将ping该参数:

$ 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

作为比较,如果Dockerfile是:

FROM debian:wheezy
CMD ["/bin/ping", "localhost"]

在没有任何参数的情况下运行映像将ping本地主机:

$ 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

但使用参数运行图像将运行参数:

docker run -it test bash
root@e8bb7249b843:/#

有关更多详细信息,请参阅Brian DeHamer的这篇文章:https://www.ctl.io/developers/blog/post/dockerfile-entrypoint-vs-cmd/