在Dockerfile中,CMD和ENTRYPOINT有什么区别?
在Dockerfiles中有两个看起来类似于我的命令: CMD
和ENTRYPOINT
。 但是我猜他们之间有一个(微妙的)区别 – 否则,对于同样的事情,有两个命令是没有意义的。
该文档说明了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指令都定义了运行容器时执行的命令。 有几条规则来描述他们的合作。
- Dockerfile应至less指定
CMD
或ENTRYPOINT
命令之一。- 当使用容器作为可执行文件时,应该定义
ENTRYPOINT
。- 应该使用
CMD
作为为ENTRYPOINT
命令定义默认参数或在容器中执行ad-hoc命令的一种方法。- 当使用替代参数运行容器时,
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/
CMD
和ENTRYPOINT
之间的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
更多关于CMD
和ENTRYPOINT
之间的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重叠,这经常使人们感到困惑。