Dockerfile中CMD和ENTRYPOINT有什么区别?

时间:2014-02-04 13:04:24

标签: docker

在Dockerfiles中有两个与我类似的命令:CMDENTRYPOINT。但是我猜它们之间有一个(微妙的?)区别 - 否则对于同样的事情有两个命令没有任何意义。

CMD

的文档说明
  

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

ENTRYPOINT

  

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

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

17 个答案:

答案 0 :(得分:1339)

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,也会得到相同的结果。由于ubuntu Dockerfile指定了默认CMD,您仍会在容器中启动bash shell:CMD ["bash"]

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

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

答案 1 :(得分:403)

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

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

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

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

例如,如果您的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/

答案 2 :(得分:157)

根据docker docs

  

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

     
      
  1. Dockerfile应至少指定CMDENTRYPOINT个命令之一。
  2.   将容器用作可执行文件时,应定义
  3. ENTRYPOINT
  4.   
  5. CMD应该用作定义ENTRYPOINT命令的默认参数或执行ad-hoc命令的方法。   容器
  6.   使用其他参数运行容器时,将覆盖
  7. CMD
  8.   

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

- No ENTRYPOINT

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

- ENTRYPOINT exec_entry p1_entry

╔════════════════════════════╦═══════════════════════════════════════════════════════════╗
║ No CMD                     ║ /bin/sh -c exec_entry p1_entry                            ║
╟────────────────────────────╫───────────────────────────────────────────────────────────╢
║ CMD [“exec_cmd”, “p1_cmd”] ║ /bin/sh -c exec_entry p1_entry exec_cmd p1_cmd            ║
╟────────────────────────────╫───────────────────────────────────────────────────────────╢
║ CMD [“p1_cmd”, “p2_cmd”]   ║ /bin/sh -c exec_entry p1_entry p1_cmd p2_cmd              ║
╟────────────────────────────╫───────────────────────────────────────────────────────────╢
║ CMD exec_cmd p1_cmd        ║ /bin/sh -c exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd ║
╚════════════════════════════╩═══════════════════════════════════════════════════════════╝

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

答案 3 :(得分:153)

是的,这是个好问题。我还没完全明白,但是:

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

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

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

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

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

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

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

docker build -t=cat .

然后你可以使用:

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

答案 4 :(得分:30)

简而言之:

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

如果您需要更多详细信息或想在示例中看到差异,可以通过博客文章全面比较CMD和ENTRYPOINT以及大量示例 - http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/

答案 5 :(得分:30)

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

有关CMDENTRYPOINT之间差异的更多信息:

{/ 1}}的参数如/ bin / bash会覆盖我们在Dockerfile中编写的任何CMD命令。

使用docker run等常规命令在运行时无法覆盖ENTRYPOINT。 docker run [args]末尾的args作为ENTRYPOINT的参数提供。通过这种方式,我们可以创建docker run [args],就像container这样的普通二进制文件。

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

可以使用ls覆盖ENTRYPOINT。

答案 6 :(得分:13)

我将以示例的形式添加答案 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

虽然可行,但这不是一个好的解决方案,因为我们有一个多余的“ sleep”命令(容器的目的是 sleep ,因此必须显式指定sleep命令是不是一个好习惯)。

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

FROM ubuntu
ENTRYPOINT sleep

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

现在我们可以运行:

docker run custom_sleep 20

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

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

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

可以通过指定ENTRYPOINT标志和后面要使用的新入口点来覆盖--entrypoint

不是我的,我曾经看过提供此示例的教程

答案 7 :(得分:9)

接受的答案很棒,可以解释历史。我发现这个表格从official doc on 'how CMD and ENTRYPOINT interact'解释得非常好: enter image description here

答案 8 :(得分:6)

code

中的EntryPoint函数的评论
  

// ENTRYPOINT / usr / sbin / nginx。

     

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

     

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

文件的另一个参考资料

  

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

示例:

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

构建:sudo docker build -t ent_cmd。

CMD arguments are easy to override.

NO argument (sudo docker -it ent_cmd)                :  ping localhost 
argument    (sudo docker run -it ent_cmd google.com) :  ping google.com

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

P.S: 在EntryPoint存在的情况下,CMD将保留为EntryPoint提供的参数。 在没有EntryPoint的情况下,CMD将成为将要运行的命令。

答案 9 :(得分:5)

我已经阅读了所有答案,并希望总结一下以便更好地理解,如下所示:

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

  • ENTRYPOINT 定义了在容器为 已启动(用于命令)

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

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

尽管您可以使用 CMD 指令指定您要使用的命令 要在图像运行时执行,正确的方法是执行该操作 通过 ENTRYPOINT 指令并仅指定 CMD (如果您 要定义默认参数。

您也可以阅读this文章,以简单的方式获得出色的解释

答案 10 :(得分:2)

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是从第一个进程分叉的。

正如creack所述,CMD是首先开发的。然后开发了ENTRYPOINT以进行更多定制。由于它们不是一起设计的,因此CMD和ENTRYPOINT之间存在一些功能重叠,这常常让人感到困惑。

答案 11 :(得分:2)

Dockerfile最佳实践的官方文档在解释差异方面做得很好。 Dockerfile best practices

CMD:

应使用CMD指令来运行映像所包含的软件以及任何参数。 CMD几乎应始终以CMD ["executable", "param1", "param2"…]的形式使用。因此,如果该映像用于服务(例如Apache和Rails),则将运行CMD ["apache2","-DFOREGROUND"]之类的内容。实际上,建议将这种形式的指令用于任何基于服务的图像。

ENTRYPOINT:

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

答案 12 :(得分:2)

我碰到了这一点,一开始我发现老实说确实让人感到困惑,我认为这种混淆来自于使用“ CMD”一词,因为实际上那里发生的事情作为争论。因此,在进行了一点了解之后,我了解了它的工作原理。基本上:

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

CMD ->这些是附加到ENTRYPOINT的参数,除非用户指定了一些自定义参数,例如:docker run ubuntu <custom_cmd>在这种情况下而不是附加在CMD部分的映像上指定的内容,docker将运行ENTRYPOINT <custom_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 run testimg <my_cmd>中。如果我们想将默认值(默认参数)传递给入口点,会发生什么?在这种情况下,我们只需要在 CMD 部分中指定它,例如:

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

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

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

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

如果我们重建并运行该映像,它将基本上休眠5秒钟。

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

答案 13 :(得分:1)

CMD文件中提到的

Dockerfile命令可以通过docker run命令覆盖,而ENTRYPOINT则不能。

答案 14 :(得分:1)

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

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

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

•您应使用CMD指令作为定义以下参数的默认方法 定义为ENTRYPOINT的命令或用于在 容器

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

•ENTRYPOINT设置每次 容器是使用图片创建的

•如果将ENTRYPOINT与CMD耦合,则可以从CMD中删除可执行文件 并保留其参数,这些参数将传递给ENTRYPOINT

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

答案 15 :(得分:1)

有一些很好的答案。我想通过每个Doc

的演示对其进行解释
  • CMD 定义容器的默认命令和/或参数。如果您需要用户可以轻松覆盖的默认命令,那么CMD是最适合使用的指令。 如果Dockerfile具有多个CMD,则仅应用最后一个的指令。
  • 如果您想使用特定的可执行文件定义容器,则首选
  • ENTRYPOINT

启动容器时,不能覆盖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 CMD
244be5006f32
  1. ENTRYPOINT

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
  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> Ben
Hello Ben

答案 16 :(得分:-2)

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

创建以下形式的微型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"]
  • ...

这样,您将轻松地自己了解所有可能方法之间的差异。