协慌网

登录 贡献 社区

Dockerfile 中 CMD 和 ENTRYPOINT 有什么区别?

在 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相同的结果: 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 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/

是的,这是个好问题。我还不完全理解它,但是:

我知道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