繁体   English   中英

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

[英]What is the difference between CMD and ENTRYPOINT in a Dockerfile?

在 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 ,您也会得到相同的结果: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: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/

根据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 设置默认命令和/或参数,可以在 docker 容器运行时从命令行覆盖。
  • ENTRYPOINT 命令和参数不会被命令行覆盖。 相反,所有命令行参数都将添加到 ENTRYPOINT 参数之后。

如果您需要更多详细信息或想查看示例的不同之处,有一篇博文将 CMD 和 ENTRYPOINT 与大量示例进行了全面比较 - https://codewithyury.com/docker-run-vs-cmd-vs-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之间的区别:

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

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

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

ENTRYPOINT 可以用--entrypoint覆盖。

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

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

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命令有点尴尬。

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

FROM ubuntu
ENTRYPOINT sleep

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

现在我们可以运行:

docker run custom_sleep 20

默认值呢? 嗯,你猜对了:

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

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

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

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

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

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

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

  1. 命令

码头工人文件

  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. 入口点

码头工人文件

  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 指定默认参数。

码头工人文件

  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

公认的答案在解释历史方面非常出色。 我发现这张表从官方文档中很好地解释了“CMD 和 ENTRYPOINT 如何交互” 在此处输入图像描述

我遇到了这个问题,一开始我发现说实话真的很混乱,我认为这种混乱来自使用“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.

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

docker run testimg 5

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

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

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

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

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

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

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

代码中对 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定义容器启动时调用的可执行文件(用于命令)

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

Kubernetes In Action一书中,有一个重要的说明。 (第七章)

虽然您可以使用CMD指令指定在运行映像时要执行的命令,但正确的方法是通过ENTRYPOINT指令执行此操作,并且仅在要定义默认参数时指定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 之间存在一些功能重叠,这常常使人们感到困惑。

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

命令:

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

入口点:

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

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

创建一个小的 Dockerfile 形式:

FROM ubuntu:latest
CMD /bin/bash

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

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

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

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

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

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

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

• 您应该使用 CMD 指令为定义为 ENTRYPOINT 的命令或在容器中执行临时命令定义默认参数

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

• ENTRYPOINT 设置每次使用映像创建容器时使用的具体默认应用程序

• 如果您将 ENTRYPOINT 与 CMD 结合,您可以从 CMD 中删除一个可执行文件,只保留将传递给 ENTRYPOINT 的参数

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

我想以轻松的方式区分CMD、RUN 和 ENTRYPOINT之间的差异。

让我们以 node 的 npm init 为例。

指令:

假设下面是我们在 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 init 命令和 npm install。

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

它将执行 npm init 和 npm install 命令。

总结一下 :

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

CMD :当你想覆盖完整的命令时使用

ENTRYPOINT:当你想附加一些额外的命令时使用。

通过从头开始重建操作系统映像,我了解到,

如果您未在 dockerfile 中指定 ENTRYPOINT 和 CMD,则 docker 将使用

/bin/sh -c

作为默认入口点,如果您在 docker 文件中定义 CMD 或在运行容器时传递命令行参数(这将覆盖定义的 CMD),则将采用 CMD。

假设您传递一个参数(或在 dockerfile 中定义ls ),那么它将被输入到 ENTRYPOINT。 那是,

/bin/sh -c ls

/bin/sh -c将运行传递给它的任何参数。 您将获得“ls”命令的 output,然后容器将退出。


ubuntu 图像包含定义的 CMD 即bash 。这意味着当你运行
以下命令运行容器,

docker container run -it ubuntu

Docker 实际上使用 ENTRYPOINT 作为/bin/sh -c然后用bash提供它,最终运行的是

/bin/sh -c bash

这将启动交互式 bash 终端(仅当如上所述指定-i标志和可选 -t 以获得类似本机终端的体验时)

当您通过命令行提供 arguments 时, bash将被您传递的任何内容替换,并且 output 就是这样,即

/bin/sh -c passed_argument

您可以定义自定义 ENTRYPOINT 来覆盖默认值,但随后您需要相应地使用 CMD。

在 dockerfile 中的RUN命令的情况下,它不考虑定义的ENTRYPOINTCMD ,而是运行指定的命令,因为它们提供给中间容器终端中的交互式 ZD574D4BB40C84861791A694A999CCE69 终端

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM