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

在 Dockerfiles 中,有两个与我相似的命令: CMDENTRYPOINT 。但是我想它们之间有一个(细微的?)差异 - 否则对于同一件事有两个命令是没有任何意义的。

CMD的文档状态

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

对于ENTRYPOINT

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也会得到相同的结果。由于ubuntu Dockerfile指定了默认的 CMD,因此您仍将在容器中启动 bash shell: 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相同的结果: docker run redisimg get key

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

CMD指定将提供给ENTRYPOINT

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

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

例如,如果您的 Dockerfile 是:

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

运行不带任何参数的映像将对 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"]

运行不带任何参数的映像将对 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/

根据docker docs

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

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

下表显示了针对不同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   ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD [“p1_cmd”, “p2_cmd”]   ║ /bin/sh -c exec_entry p1_entry   ║
╟────────────────────────────╫──────────────────────────────────╢
║ CMD exec_cmd p1_cmd        ║ /bin/sh -c exec_entry p1_entry   ║
╚════════════════════════════╩══════════════════════════════════╝

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

是的,这是一个好问题。我尚不完全了解,但是:

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

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

CMD 是容器的默认参数。没有入口点,默认参数是执行的命令。使用入口点,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

通过直觉 ,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之间的区别的更多ENTRYPOINT

诸如 / bin / bash 之类的 Docker docker run参数会覆盖我们在 Dockerfile 中编写的所有 CMD 命令。

不能在运行时使用 docker docker run [args]等常规命令覆盖 ENTRYPOINT。所述args在年底docker run [args]被设置作为参数传递给入口点。这样,我们可以创建一个类似于普通二进制文件(如lscontainer

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

可以使用--entrypoint覆盖--entrypoint

简而言之:

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

如果您需要了解更多信息或想看到的例子差,有一个博客贴子,综合比较 CMD 和入口点,有很多的例子 - http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/

公认的答案非常能解释历史。我发现这张表从官方文档中很好地解释了“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因为该值在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标志,后面是要使用新的切入点。

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

代码中对 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

ps:在存在 EntryPoint 的情况下,CMD 将保存要馈送到 EntryPoint 的参数。在缺少 EntryPoint 的情况下,将运行 CMD。

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