在Dockerfile中,CMD和ENTRYPOINT有什么区别?

在Dockerfiles中有两个看起来类似于我的命令: CMDENTRYPOINT 。 但是我猜他们之间有一个(微妙的)区别 – 否则,对于同样的事情,有两个命令是没有意义的。

该文档说明了CMD

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

ENTRYPOINT

一个ENTRYPOINT可以帮助你configuration一个你可以作为可执行文件运行的容器。

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

Docker的默认入口点是/bin/sh -c但没有默认的命令。

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

该命令通过入口点运行。 即,执行的实际内容是/bin/sh -c bash 。 这使得docker可以依靠shell的parsing器快速执行RUN 。 之后,人们要求能够定制这个,所以ENTRYPOINT-entrypoint已经被引入。

在上面的例子中, ubuntu之后的所有东西都是命令,并传递给入口点。 当使用CMD指令时,就好像你在做docker run -i -t ubuntu <cmd><cmd>将是入口点的参数。

如果你input这个命令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相同的结果: docker run redisimg get key

ENTRYPOINT指定一个在容器启动时总是执行的命令。

CMD指定将被馈送到进入点的ENTRYPOINT

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

否则,如果要为通用目的创build映像,则可以不指定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 

现在,运行一个参数的图像将平实的论点:

 $ 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/

是的,这是一个很好的问题。 我还没有完全理解,但是:

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

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

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

 # 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 

根据docker文件 ,

CMD和ENTRYPOINT指令都定义了运行容器时执行的命令。 有几条规则来描述他们的合作。

  1. Dockerfile应至less指定CMDENTRYPOINT命令之一。
  2. 当使用容器作为可执行文件时,应该定义ENTRYPOINT
  3. 应该使用CMD作为为ENTRYPOINT命令定义默认参数或在容器中执行ad-hoc命令的一种方法。
  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 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 ║ ╚════════════════════════════╩═════════════════════════════════════════════════╝ 

简而言之:

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

如果你需要更多的细节,或想看到不同的例子,有一个博客文章,综合比较CMD和ENTRYPOINT与大量的例子 – http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/

CMDENTRYPOINT之间的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

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

ENTRYPOINT不能在运行时使用普通命令(如docker run [args]重写。 ENTRYPOINT docker run [args]结尾处的docker run [args]作为参数提供给ENTRYPOINT 。 这样我们就可以创build一个container ,就像一个普通的二进制文件,比如ls

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

ENTRYPOINT可以用 – --entrypoint来覆盖。

在代码中的 EntryPoint函数的评论

// ENTRYPOINT / usr / sbin / nginx。

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

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

另有参考文件

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

例:

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

构build :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将成为将要运行的命令。

被接受的答案在解释历史上是神话般的。 我觉得这张表很好地解释了“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 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是从第一个进程派生。

正如creack所说,CMD是第一个开发的。 那么进入点是为了更多的定制而开发的。 由于它们不是一起devise的,所以在CMD和进入点之间有一些function重叠,这经常使人们感到困惑。