在Dockerfiles中有两个看起来与我类似的命令:CMD
和ENTRYPOINT
。但是我猜它们之间有一个(微妙的?)区别 - 否则对于同样的事情有两个命令没有任何意义。
CMD
的文档说明
COMMAND的主要目的是为执行容器提供默认值。
并为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
。
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:
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"]
通过这种方式,您可以轻松地看到所有可能的方法之间的差异。
ENTRYPOINT
指定一个在容器启动时始终执行的命令。
CMD
指定将被提供给ENTRYPOINT
的参数。
如果您想制作专用于特定命令的图像,您将使用ENTRYPOINT ["/path/dedicated_command"]
否则,如果你想为一般目的制作图像,你可以不指定ENTRYPOINT
并使用CMD ["/path/dedicated_command"]
,因为你可以通过向docker run
提供参数来覆盖设置。
例如,如果您的Dockerfile是:
FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
不带任何参数运行映像将ping localhost:
$ 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 localhost:
$ 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指令都定义了在运行容器时执行的命令。很少有规则描述他们的合作。
- Dockerfile应至少指定
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 ║
╚════════════════════════════╩═════════════════════════════════════════════════╝
是的,这是个好问题。我还不完全理解它,但是:
我知道ENTRYPOINT
是正在执行的二进制文件。您可以通过--entrypoint =“”覆盖入口点。
docker run -t -i --entrypoint="/bin/bash" ubuntu
CMD是容器的默认参数。如果没有入口点,则default参数是执行的命令。使用入口点,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之间的区别:
是的,它正在混淆。
运行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
更多关于CMD
和ENTRYPOINT
之间的区别:
诸如/ bin / bash之类的docker run
参数会覆盖我们在Dockerfile中编写的任何CMD命令。
使用docker run [args]
等常规命令在运行时无法覆盖ENTRYPOINT。 args
末尾的docker run [args]
作为ENTRYPOINT的参数提供。通过这种方式,我们可以创建一个container
,就像ls
这样的普通二进制文件。
所以CMD可以作为ENTRYPOINT的默认参数,然后我们可以从[args]覆盖CMD args。
可以使用--entrypoint
覆盖ENTRYPOINT。
简而言之:
如果您需要更多详细信息或希望看到示例上的差异,那么有一篇博文全面地比较了CMD和ENTRYPOINT以及大量示例 - http://goinbigdata.com/docker-run-vs-cmd-vs-entrypoint/
接受的答案很棒,可以解释历史。我发现这张表很好地解释了official doc on 'how CMD and ENTRYPOINT interact':
关于code中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
p.s:在EntryPoint存在的情况下,CMD将保留参与EntryPoint的参数。如果没有EntryPoint,CMD将是将要运行的命令。
在CMD
文件中提到的Dockerfile
命令可以通过docker run
命令覆盖,而ENTRYPOINT
不能。