对比分析 Dockerfile 中的 ENTRYPOINT 和 CMD
5 min read · November 15th 2017
概述
Dockerfile 中的 ENTRYPOINT 指令和 CMD 指令都可以设置容器启动时要执行的命令。
ENTRYPOINT 有两种形式:
- exec 模式,如
ENTRYPOINT ["executable", "param1", "param2"]
- shell 模式, 如
ENTRYPOINT command param1 param2
而 CMD 也有两种形式:
-
exec 模式,如
CMD ["executable","param1","param2"]
- 当有 entrypoint 时,executable 等价于 param, 官方手册将其也作为了一种模式,严格来讲这仍是 exec 模式。
- shell 模式,
CMD command param1 param2
实验过程
为了研究它们的不同之处,我们通过实验,任意组合各种情形,分析其执行结果。
ENTRYPOINT 和 CMD 分别有好多种情形,彼此间的组合情况更多,为了简化描述,我们引入代号。
规则如下:
- e -> 表示 ENTRYPOINT
- c -> 表示 CMD
- 0 -> 表示 Dockerfile 中没有指令。
- 1 -> 表示 Dockerfile 中指令采用 exec 形式,且仅有一个参数。
- 2 -> 表示 Dockerfile 中指令采用 shell 形式,且仅由一个参数。
- 3 -> 表示 Dockerfile 中指令采用 exec 形式, 有多个参数。
- 4 -> 表示 Dockerfile 中指令采用 shell 形式, 有多个参数。
举例说明,e1c1 表示 Dockerfile 中同时存在 ENTRYPOINT 和 CMD,且是 exec 形式,仅用一个参数。 其 Dockerfile 如下:
// ./Dockerfile.e1c1
FROM alpine
COPY ./mycmd /usr/bin/
ENTRYPOINT ["/usr/bin/mycmd"]
CMD ["arg"]
mycmd 命令用来打印命令行参数,其内容如下:
#!/bin/sh
echo $@
实验中任意组合各种情况下的 ENTRYPOINT 和 CMD, 生成 Dockerfile。
创建和执行过程:
docker build -t e1c1 -f Dockerfile.e1c1 .
docker run -rm e1c1
docker run -rm e1c1 a
收集 docker run
的输出,可以得到下表:
N.O. | ENTRYPOINT | CMD | RUN | OUPUT |
---|---|---|---|---|
e1c1 | ["/usr/bin/mycmd"] | ["arg"] | arg | |
["/usr/bin/mycmd"] | ["arg"] | a | a | |
e1c0 | ["/usr/bin/mycmd"] | |||
["/usr/bin/mycmd"] | a | a | ||
e1c2 | ["/usr/bin/mycmd"] | arg | /bin/sh -c "arg" | |
["/usr/bin/mycmd"] | arg | a | a | |
e2c0 | /usr/bin/mycmd | |||
/usr/bin/mycmd | a | |||
e2c1 | /usr/bin/mycmd | ["arg"] | ||
/usr/bin/mycmd | ["arg"] | a | ||
e2c2 | /usr/bin/mycmd | arg | ||
/usr/bin/mycmd | arg | a | ||
e0c0 | ||||
a | docker: Error..."exec: "a": executable file not found... | |||
mycmd a | a | |||
e0c1 | ["mycmd"] | |||
["mycmd"] | a | docker: Error..."exec: "a": executable file not found... | ||
e0c2 | mycmd | |||
mycmd | a | docker: Error..."exec: "a": executable file not found... | ||
e0c3 | ["mycmd", "a"] | a | ||
["mycmd", "a"] | a | docker: Error..."exec: "a": executable file not found... | ||
e0c4 | mycmd a | a | ||
mycmd a | a | docker: Error..."exec: "a": executable file not found... | ||
e3c0 | ["/usr/bin/mycmd", "a"] | a | ||
["/usr/bin/mycmd", "a"] | b | a b | ||
e3c1 | ["/usr/bin/mycmd", "a"] | ["b"] | a b | |
["/usr/bin/mycmd", "a"] | ["b"] | c | a c | |
e3c2 | ["/usr/bin/mycmd", "a"] | b | a /bin/sh -c "b" | |
["/usr/bin/mycmd", "a"] | b | c | a c | |
e4c0 | /usr/bin/mycmd a | a | ||
/usr/bin/mycmd a | b | a | ||
e4c1 | /usr/bin/mycmd a | ["b"] | b: line 1: /usr/bin/mycmd a: not found | |
/usr/bin/mycmd a | ["b"] | c | c: line 1: /usr/bin/mycmd a: not found | |
e4c2 | /usr/bin/mycmd a | b | /bin/sh: line 1: /usr/bin/mycmd a: not found | |
/usr/bin/mycmd a | b | c | c: line 1: /usr/bin/mycmd a: not found |
- N.O. -> 组合情形编号。
- ENTRYPOINT -> Dockerfile 中指令 ENTRYPOINT 的值。
- CMD -> Dockerfile 中指令 CMD 的值。
- RUN 表示执行时传递给容器的值。
- OUTPUT 时执行后打印的结果。
结论
- e2 时 cmd 值全部无效,可以得出 ENTRYPOINT 采用 shell 模式且没有参数时会忽略 cmd 。
- e4 有 cmd 值全部报错,可以得出 ENTRYPOINT 采用 shell 模式有参数时传入 cmd 会报错 。
- 从 e2 和 e4 可以看出, 推荐使用 exec 模式配置 ENTRYPOINT ,它接受 cmd 且更安全。
- docker run 中 命令行传入的 cmd 会覆盖 Dockerfile 中的 CMD。
- 根据 e1c2 和 e3c2, Dockerfile 中 CMD 在 shell 模式下内部会被转换为前部拼接 ["/bin/sh", "-c"] 的 exec 模式。
- 结合 e1c2 和 e3c2, 推荐使用 exec 模式配置 CMD 。
- 从 e3 可以得出: Dockerfile 中的 ENTRYPOINT 在 exec 模式下自带的参数会与 cmd 参数拼接