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

CMD的文档状态-

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

对于入口点:

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

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


当前回答

我将把我的答案作为一个例子,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。

不是我的,我曾经看过一个提供了这个例子的教程

其他回答

CMD和ENTRYPOINT之间的直觉差异:

ENTRYPOINT:容器启动时运行的命令。CMD:容器启动时运行的命令,或ENTRYPOINT的参数(如果指定)。

是的,这令人困惑。

在运行docker run时,可以覆盖其中任何一个。

CMD和ENTRYPOINT之间的差异示例如下:

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

有关CMD和ENTRYPOINT之间差异的更多信息:

docker run的参数(例如/bin/bash)会覆盖我们在Dockerfile中编写的任何CMD命令。

ENTRYPOINT不能在运行时被docker run[args]等正常命令覆盖。docker运行结束时的args[args]作为ENTRYPOINT的参数提供。通过这种方式,我们可以创建一个像ls这样的普通二进制文件的容器。

因此,CMD可以作为ENTRYPOINT的默认参数,然后我们可以从[args]重写CMD参数。

ENTRYPOINT可以用--ENTRYPOINT重写。

简而言之:

CMD设置默认命令和/或参数,当docker容器运行时,可以从命令行覆盖这些命令和/参数。ENTRYPOINT命令和参数不会从命令行覆盖。相反,所有命令行参数都将添加到ENTRYPOINT参数之后。

如果你需要更多的细节,或者想看到例子上的差异,有一篇博客文章将CMD和ENTRYPOINT与很多例子进行了全面比较https://codewithyury.com/docker-run-vs-cmd-vs-entrypoint/

有一些很好的答案。我想通过每个文档的演示来解释

CMD定义容器的默认命令和/或参数。如果您需要用户可以轻松覆盖的默认命令,则最好使用CMD指令。如果Dockerfile有多个CMD,它只应用最后一个CMD中的指令。当您想要定义具有特定可执行文件的容器时,首选ENTRYPOINT。

除非添加--ENTRYPOINT标志,否则无法在启动容器时重写ENTRYPOINT。

命令

Docker文件

  FROM centos:8.1.1911

  CMD ["echo", "Hello Docker"]

运行结果

$ sudo docker run <image-id>
Hello Docker
$ sudo docker run <image-id> hostname   # hostname is exec to override CMD
244be5006f32

入口点

Docker文件

  FROM centos:8.1.1911

  ENTRYPOINT ["echo", "Hello Docker"]

运行结果

$ sudo docker run <image-id>
Hello Docker
$ sudo docker run <image-id> hostname   # hostname as parameter to exec
Hello Docker hostname

在许多情况下,将CMD和ENTRYPOINT结合起来是Docker容器的最佳解决方案。在这种情况下,可执行文件使用ENTRYPOINT定义,而CMD指定默认参数。

Docker文件

  FROM centos:8.1.1911

  ENTRYPOINT ["echo", "Hello"]
  CMD ["Docker"]

运行结果

$ sudo docker run <image-id>
Hello Docker
$ sudo docker run <image-id> Ben
Hello Ben

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。

我遇到了这个问题,一开始我发现老实说,这真的很令人困惑,我认为这种困惑来自于使用“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为入口点提供默认参数,或者在启动容器时运行自定义命令,该命令可以由用户从外部重写。