Dockerfile中的CMD和ENTRYPOINT有什么区别?

在Dockerfile中,有两个命令看起来与我相似:CMDENTRYPOINT。但我想它们之间存在(微妙的?)差异-否则为同一事物使用两个命令将没有任何意义。

留档状态CMD-

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

对于ENTRYPOINT

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

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

811004 次浏览

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

我知道ENTRYPOINT是正在执行的二进制文件。您可以通过--entrypoint=""覆盖入口点。

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

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

# no entrypointdocker run ubuntu /bin/cat /etc/passwd
# with entry point, emulating cat commanddocker run --entrypoint="/bin/cat" ubuntu /etc/passwd

因此,主要优点是使用entrypoint可以将参数(cmd)传递到容器。要实现这一点,你需要同时使用两者:

# DockerfileFROM ubuntuENTRYPOINT ["/bin/cat"]

docker build -t=cat .

然后您可以使用:

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

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

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

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

后来,人们要求能够自定义这个,所以引入了ENTRYPOINT--entrypoint

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

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

当所有内容都传递到入口点时,你可以从图像中获得非常好的行为。@Jiri示例很好,它展示了如何将图像用作“二进制”。当使用["/bin/cat"]作为入口点然后执行docker run img /etc/passwd时,你会得到它,/etc/passwd是命令并传递给入口点,因此最终结果执行简单地/bin/cat /etc/passwd

另一个例子是将任何cli作为入口点。例如,如果您有一个redis图像,而不是运行docker run redisimg redis -H something -u toto get key,您可以简单地拥有ENTRYPOINT ["redis", "-H", "something", "-u", "toto"],然后像这样运行以获得相同的结果:docker run redisimg get key

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

CMD指定将提供给ENTRYPOINT的参数。

如果您想将图像专用于特定命令,您将使用ENTRYPOINT ["/path/dedicated_command"]

否则,如果您想为通用目的制作图像,您可以不指定ENTRYPOINT并使用CMD ["/path/dedicated_command"],因为您可以通过向docker run提供参数来覆盖设置。

例如,如果您的Dockerfile是:

FROM debian:wheezyENTRYPOINT ["/bin/ping"]CMD ["localhost"]

在没有任何参数的情况下运行图像将pinglocalhost:

$ docker run -it testPING localhost (127.0.0.1): 48 data bytes56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.096 ms56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.088 ms56 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 lossround-trip min/avg/max/stddev = 0.088/0.091/0.096/0.000 ms

现在,使用参数运行图像将ping参数:

$ docker run -it test google.comPING google.com (173.194.45.70): 48 data bytes56 bytes from 173.194.45.70: icmp_seq=0 ttl=55 time=32.583 ms56 bytes from 173.194.45.70: icmp_seq=2 ttl=55 time=30.327 ms56 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 lossround-trip min/avg/max/stddev = 30.327/36.430/46.379/7.095 ms

作为比较,如果您的Dockerfile是:

FROM debian:wheezyCMD ["/bin/ping", "localhost"]

在没有任何参数的情况下运行图像将pinglocalhost:

$ docker run -it testPING localhost (127.0.0.1): 48 data bytes56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.076 ms56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.087 ms56 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 lossround-trip min/avg/max/stddev = 0.076/0.084/0.090/0.000 ms

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

docker run -it test bashroot@e8bb7249b843:/#

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

CMD:

  • CMD ["executable","param1","param2"]["executable","param1","param2"]是第一个过程。
  • CMD command param1 param2/bin/sh -c CMD command param1 param2是第一个进程。CMD command param1 param2是从第一个进程分叉的。
  • CMD ["param1","param2"]:此表单用于为ENTRYPOINT提供默认参数。

ENTRYPOINT(以下列表不考虑CMD和ENTRYPOINT一起使用的情况):

  • ENTRYPOINT ["executable", "param1", "param2"]["executable", "param1", "param2"]是第一个过程。
  • ENTRYPOINT command param1 param2/bin/sh -c command param1 param2是第一个进程。command param1 param2是从第一个进程分叉的。

正如吱吱作响所说,CMD是先开发的。然后ENTRYPOINT是为了更多的定制而开发的。由于它们不是一起设计的,CMD和ENTRYPOINT之间存在一些功能重叠,这经常让人困惑。

评论代码中的EntryPoint函数

//入口 /usr/sbin/nginx.

//设置入口点(默认为sh-c) /usr/sbin/nginx.

//将接受CMD作为 /usr/sbin/nginx.的参数

另一个参考文件

您可以使用ENTRYPOINT的exec形式设置相当稳定的默认命令和参数,然后使用CMD设置更有可能更改的其他默认值。

示例:

FROM ubuntu:14.04.3ENTRYPOINT ["/bin/ping"]CMD ["localhost", "-c", "2"]

构建:sudo docker build-tent_cmd。

CMD arguments are easy to override.
NO argument (sudo docker -it ent_cmd)                :  ping localhostargument    (sudo docker run -it ent_cmd google.com) :  ping google.com

.

To override EntryPoint argument, you need to supply entrypointsudo docker run -it --entrypoint="/bin/bash" ent_cmdd

p:在EntryPoint存在的情况下,CMD将保存参数以馈送到EntryPoint。在没有EntryPoint的情况下,CMD将是将要运行的命令。

简而言之:

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

如果您需要更多细节或希望看到示例的差异,有一篇博客文章全面比较了CMD和ENTRYPOINT,并提供了大量示例-https://codewithyury.com/docker-run-vs-cmd-vs-entrypoint/

根据docker docs

CMD和ENTRYPOINT指令都定义了执行什么命令在运行容器时。很少有规则描述它们合作。

  1. Dockerfile应至少指定CMDENTRYPOINT命令之一。
  2. 将容器用作可执行文件时应定义ENTRYPOINT
  3. CMD应该用作定义ENTRYPOINT命令的默认参数的一种方式,或者用于在容器。
  4. 使用替代参数运行容器时,CMD将被覆盖。

下面的表格显示了对于不同的#0/#1组合执行什么命令

--#0

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

--#0

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

--#0

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

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 ENTRYPOINTdocker run -it --rm --entrypoint ls yourcontainer      <-- overrides ENTRYPOINT with lsdocker run -it --rm --entrypoint ls yourcontainer  -la  <-- overrides ENTRYPOINT with ls and overrides CMD with -la

CMDENTRYPOINT的区别:

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

ENTRYPOINT不能在运行时使用docker run [args]等普通命令覆盖。docker run [args]末尾的args作为ENTRYPOINT的参数提供。通过这种方式,我们可以创建一个类似于ls等普通二进制文件的container

所以CMD可以作为ENTRYPOINT的默认参数,然后我们可以从[args]覆盖CMD args。

ENTRYPOINT可以被--entrypoint覆盖。

公认的答案在解释历史方面非常出色。我发现这张表从关于“CMD和ENTRYPOINT如何相互作用”的官方文档中很好地解释了它:输入图片描述

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

创建一个表单的小Dockerfile:

FROM ubuntu:latestCMD /bin/bash

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

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

通过这种方式,您可以轻松地看到所有可能的方法之间的差异。

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

我将添加我的答案作为示例1,这可能有助于您更好地理解差异。

假设我们要创建一个图像,该图像将在启动时总是运行睡眠命令。我们将创建自己的图像并指定一个新命令:

FROM ubuntuCMD sleep 10

建立形象:

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

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

docker run custom_sleep sleep 20

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

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

FROM ubuntuENTRYPOINT sleep

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

现在我们可以运行:

docker run custom_sleep 20

默认值呢?好吧,你猜对了:

FROM ubuntuENTRYPOINT ["sleep"]CMD ["10"]

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

可以通过指定--entrypoint标志来覆盖ENTRYPOINT,然后是要使用的新切入点。

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

Dockerfile最佳实践的官方留档很好地解释了差异。Dockerfile最佳实践

CMD:

CMD指令应该用于运行映像中包含的软件以及任何参数。CMD几乎总是应该以CMD ["executable", "param1", "param2"…]的形式使用。因此,如果映像用于Apache和Rails等服务,您将运行类似于CMD ["apache2","-DFOREGROUND"]的内容。事实上,这种形式的指令推荐用于任何基于服务的映像。

入口:

ENTRYPOINT的最佳用途是设置图像的主命令,允许该图像像该命令一样运行(然后使用CMD作为默认标志)。

我已经阅读了所有的答案,我想总结一下,以便乍一看更好地理解,如下所示:

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

  • <区块链>

    入口定义当容器被调用时调用的可执行文件开始(用于命令)

  • <区块链>

    CMD指定传递给ENTRYPOINT的参数(用于参数)

库伯内特斯在行动的书中指出了一个重要的注意事项(第7章)。

虽然您可以使用CMD指令来指定您的命令如果你想在镜像运行时执行,正确的方法是这样做通过入口指令和仅在以下情况下指定CMD想要定义默认参数。

您也可以阅读这个文章,以简单的方式进行很好的解释

•Dockerfile应至少指定一条CMD或ENTRYPOINT指令

•仅使用Dockerfile中的最后一个CMD和ENTRYPOINT

•在将容器用作可执行文件时应定义ENTRYPOINT

•您应该使用CMD指令作为定义默认参数的一种方式定义为ENTRYPOINT的命令或用于在容器

•当使用替代参数运行容器时,CMD将被覆盖

•ENTRYPOINT设置每次执行任务时使用的具体默认应用程序使用图像

创建容器

•如果将ENTRYPOINT与CMD耦合,则可以从CMD中删除可执行文件并且只留下它的参数,这些参数将被传递给ENTRYPOINT

•ENTRYPOINT的最佳用途是设置图像的主命令,允许图像将像该命令一样运行(然后使用CMD作为默认值标志)

我遇到了这个,一开始我发现说实话真的很困惑,我认为这种困惑来自于使用“CMD”这个词,因为实际上那里的行为作为论据。所以在挖掘了一点之后,我明白了它是如何工作的。基本上:

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

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

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

From ubuntuENTRYPOINT ["sleep"]

然后我通过运行以下内容来构建它:

docker build . -t testimg

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

docker run testimg

我会得到以下内容:

sleep: missing operandTry 'sleep --help' for more information.

发生这种情况是因为切入点是需要参数的“睡眠”命令。所以为了解决这个问题,我只提供睡眠量:

docker run testimg 5

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

From ubuntuENTRYPOINT ["sleep"]CMD ["10"]

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

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

FROM ubuntuCMD ["sleep", "5"]

如果我们重建并运行这个图像,它基本上会休眠5秒。

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

有一些很好的答案。我想通过演示来解释它Doc

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

除非添加--entrypoint标志,否则在启动容器时不能覆盖ENTRYPOINT

  1. CMD

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 CMD244be5006f32
  1. 入口

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 execHello Docker hostname
  1. 在许多情况下,结合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> BenHello Ben

我想以轻松的方式区分CMD、运行和入口之间的差异。

让我们为节点举一个npm init示例。

CMD:

让我们假设下面是我们在dockerfile中添加的初始命令

CMD [ "npm", "init" ]

现在,如果我运行docker run -t node npm install

它将覆盖dockerfile中的npm init命令。

CMD [ "npm", "init" ] This will become  CMD [ "npm", "install" ]

它将执行npm install命令而不是npm init,因为它会覆盖npm install。

现在,让我们谈谈

入口:

让我们假设在docker文件中添加了相同的命令,但使用ENTRYPOINT

ENTRYPOINT [ "npm", "init" ]

现在,如果我运行docker run -t node install

它将在dockerfile中附加带有npm install的npm init命令。

ENTRYPOINT [ "npm", "init" ] This will become  ENTRYPOINT [ "npm", "init", "install" ]

它将执行npm init和npm安装命令。

总结:

运行:这将在图像生成时执行。用于安装任何依赖项,例如node_modules。RUN npm install

CMD:当您想要覆盖完整命令时使用

入口:当您想附加一些附加命令时使用。

从从头开始重建操作系统映像(只是编写FROM scratch并在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命令,它不考虑定义的ENTRYPOINTCMD,而是运行指定的命令,因为它们提供给中间容器中的交互式bash终端