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

CMD的文档状态-

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

对于入口点:

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

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


当前回答

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/

其他回答

是的,这是个好问题。我还不完全理解,但是:

我知道ENTRYPOINT是正在执行的二进制文件。可以通过--entrypoint=“”覆盖entrypoint。

docker run -t -i --entrypoint="/bin/bash" ubuntu

CMD是容器的默认参数。如果没有入口点,默认参数是执行的命令。使用entrypoint,cmd将作为参数传递给entryppoint。您可以使用入口点模拟命令。

# no entrypoint
docker run ubuntu /bin/cat /etc/passwd

# with entry point, emulating cat command
docker run --entrypoint="/bin/cat" ubuntu /etc/passwd

所以,主要优点是使用entrypoint可以将参数(cmd)传递给容器。要做到这一点,您需要同时使用:

# Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/cat"]

and

docker build -t=cat .

那么您可以使用:

docker run cat /etc/passwd
#              ^^^^^^^^^^^
#                   CMD
#          ^^^      
#          image (tag)- using the default ENTRYPOINT

大多数人在这里都解释得很好,所以我不会重复所有的答案。但为了得到一个好的感觉,我建议自己通过查看容器中的流程来测试它。

创建一个小型Dockerfile,格式如下:

FROM ubuntu:latest
CMD /bin/bash

构建它,用docker run-it-theimage运行它,并在容器中运行ps-eo-ppid、pid和args。使用以下选项时,将此输出与从ps接收的输出进行比较:

docker run-it theimage bash重建图像,但使用ENTRYPOINT/bin/bash并以两种方式运行使用CMD[“/bin/bash”]...

这样,您将很容易看到所有可能的方法之间的差异。

我遇到了这个问题,一开始我发现老实说,这真的很令人困惑,我认为这种困惑来自于使用“CMD”一词,因为事实上,那里的东西充当了论据。所以挖了一点后,我明白了它是如何工作的。基本上:

ENTRYPOINT-->此处指定的是容器启动时要执行的命令。如果省略此定义,docker将使用/bin/sh-cbash来运行容器。

CMD-->这些是附加到ENTRYPOINT的参数,除非用户指定了一些自定义参数,即:docker run ubuntu<custom_CMD>在这种情况下,docker将运行ENTRYPOINT<custom_CMD>,而不是附加在CMD部分的图像上指定的内容。如果未指定ENTRYPOINT,则此处的内容将传递给/bin/sh-c,实际上作为启动容器时要执行的命令。

作为一切,最好用例子来解释发生了什么。因此,假设我使用以下规范Dockerfile创建了一个简单的docker映像:

From ubuntu
ENTRYPOINT ["sleep"]

然后我通过运行以下命令来构建它:

docker build . -t testimg

这将创建一个容器,每次运行时它都会休眠。所以,如果我按如下方式运行:

docker run testimg

我会得到以下信息:

sleep: missing operand
Try 'sleep --help' for more information.

这是因为入口点是需要参数的“sleep”命令。为了解决这个问题,我只需要提供睡眠量:

docker run testimg 5

这将正确运行,因此容器将运行,休眠5秒并退出。正如我们在这个示例中看到的,docker只是将图像名称后面的内容添加到入口点二进制docker runtestimg<my_cmd>。如果我们想将默认值(默认参数)传递给入口点,会发生什么?在这种情况下,我们只需要在CMD部分中指定它,例如:

From ubuntu
ENTRYPOINT ["sleep"]
CMD ["10"]

在这种情况下,如果用户没有传递任何参数,容器将使用默认值(10)并将其传递给入口点睡眠。

现在,让我们只使用CMD并省略ENTRYPOINT定义:

FROM ubuntu
CMD ["sleep", "5"]

如果我们重建并运行此图像,它将基本休眠5秒。

总之,您可以使用ENTRYPOINT使容器充当可执行文件。您可以使用CMD为入口点提供默认参数,或者在启动容器时运行自定义命令,该命令可以由用户从外部重写。

我将把我的答案作为一个例子,1这样可以帮助你更好地理解其中的区别。

假设我们希望创建一个图像,该图像在启动时始终运行sleep命令。我们将创建自己的映像并指定一个新命令:

FROM ubuntu
CMD sleep 10

构建图像:

docker build -t custom_sleep .
docker run custom_sleep
# sleeps for 10 seconds and exits

如果我们想改变秒数怎么办?我们必须更改Dockerfile,因为该值是硬编码的,或者通过提供不同的值来重写该命令:

docker run custom_sleep sleep 20

虽然这是可行的,但这不是一个好的解决方案,因为我们有一个冗余的“睡眠”命令。为什么冗余?因为容器的唯一目的是睡眠,所以必须显式指定睡眠命令有点尴尬。

现在,让我们尝试使用ENTRYPOINT指令:

FROM ubuntu
ENTRYPOINT sleep

此指令指定容器启动时将运行的程序。

现在我们可以运行:

docker run custom_sleep 20

默认值如何?嗯,你猜对了:

FROM ubuntu
ENTRYPOINT ["sleep"]
CMD ["10"]

ENTRYPOINT是将要运行的程序,传递给容器的值将附加到该程序。

可以通过指定--ENTRYPOINT标志,后跟要使用的新入口点来覆盖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终端