关于docker:在dockerfile中,cmd和entrypoint有什么区别?

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

有两个命令在dockerfiles看起来类似于:CMDENTRYPOINT亩。但我想这是有一个(相对?)他们之间的差异,否则它将不会让任何一个有两个镰刀一样的东西来为甚。

在文档中的CMD

The main purpose of a CMD is to provide defaults for an executing container.

ENTRYPOINT

An ENTRYPOINT helps you to configure a container that you can run as an executable.

那么,什么是"两个命令之间的差异如何?


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 指令一样。是入口点的参数。

如果您改为键入该命令docker run -i -t ubuntu,也会得到相同的结果。由于Ubuntu dockerfile指定了默认的cmd:CMD ["bash"],所以您仍将在容器中启动bash shell。

当所有东西都传递到入口点时,您可以从图像中获得非常好的行为。@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是:

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

不带任何参数运行映像将ping本地主机:

1
2
3
4
5
6
7
8
$ 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该参数:

1
2
3
4
5
6
7
8
$ 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是:

1
2
FROM debian:wheezy
CMD ["/bin/ping","localhost"]

不带任何参数运行映像将ping本地主机:

1
2
3
4
5
6
7
8
$ 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

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

1
2
docker run -it test bash
root@e8bb7249b843:/#

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


根据Docker文件,

Both CMD and ENTRYPOINT instructions define what command gets executed
when running a container. There are few rules that describe their
co-operation.

  • Dockerfile should specify at least one of CMD or ENTRYPOINT commands.
  • ENTRYPOINT should be defined when using the container as an executable.
  • CMD should be used as a way of defining default arguments for an ENTRYPOINT command or for executing an ad-hoc command in a
    container.
  • CMD will be overridden when running the container with alternative arguments.
  • 下表显示了针对不同ENTRYPOINTCMD组合执行的命令:

    --埃多克斯1〔2〕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ╔════════════════════════════╦═════════════════════════════╗
    ║ 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  ║
    ╚════════════════════════════╩═════════════════════════════╝

    --埃多克斯1〔3〕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ╔════════════════════════════╦═══════════════════════════════════════════════════════════╗
    ║ 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 ║
    ╚════════════════════════════╩═══════════════════════════════════════════════════════════╝

    --江户十一〔四〕号

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ╔════════════════════════════╦═════════════════════════════════════════════════╗
    ║ 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()超越入口点。

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

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

    1
    2
    3
    4
    5
    # no entrypoint
    docker run ubuntu /bin/cat /etc/passwd

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

    所以,主要的优点是使用入口点可以将参数(cmd)传递到容器中。要实现这一点,您需要同时使用以下两种方法:

    1
    2
    3
    # Dockerfile
    FROM ubuntu
    ENTRYPOINT ["/bin/cat"]

    1
    docker build -t=cat .

    然后您可以使用:

    1
    2
    3
    4
    5
    docker run cat /etc/passwd
    #              ^^^^^^^^^^^
    #                   CMD
    #          ^^^      
    #          image (tag)- using the default ENTRYPOINT


    Cmd和入口点的直觉差异:

    • entrypoint:容器启动时运行的命令。
    • 命令:当容器启动时运行的命令,或者如果指定了入口点的参数。

    是的,它混淆了。

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

    命令和入口点之间的区别(通过示例):

    1
    2
    3
    4
    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中编写的任何命令。

    entrypoint不能在运行时被正常命令(如docker run [args]覆盖。在docker run [args]末尾的args作为入口点的论据。这样我们就可以创建一个类似于普通二进制文件(如ls)的container

    因此,cmd可以作为入口点的默认参数,然后我们可以从[args]重写cmd参数。

    入口点可以用--entrypoint覆盖。


    简而言之:

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

    如果你需要更多的细节或者想看看例子的不同,有一篇博文综合比较了cmd和entrypoint和很多例子-http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/


    公认的答案在解释历史方面是极好的。我发现这张表很好地解释了来自官方文档关于"cmd和entrypoint如何交互"的内容:enter image description here


    代码中入口点函数的注释

    // ENTRYPOINT /usr/sbin/nginx.

    // Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx.

    // Will accept the CMD as the arguments to /usr/sbin/nginx.

    来自文档的另一个引用

    You can use the exec form of ENTRYPOINT to set fairly stable default commands and arguments and then use CMD to set additional defaults that are more likely to be changed.

    例子:

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

    构建:sudo docker build-t ent_cmd。

    1
    2
    3
    4
    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

    .

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

    附笔:在入口点存在的情况下,cmd将持有fed到入口点的参数。在缺少入口点的情况下,将运行命令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提供默认参数。

    入口点(以下列表不考虑将cmd和入口点一起使用的情况):

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

    正如克里克所说,命令是先开发的。然后开发了入口点,以便进行更多的定制。由于它们不是一起设计的,所以在cmd和entrypoint之间有一些功能重叠,这常常使人感到困惑。


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


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

    创建表单的一个小文件:

    1
    2
    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"]

    这样你就可以很容易地看到所有可能的方法之间的差异。