容器的本质是一个在前台运行的进程,进程启动时需要设置启动命令与参数。对 docker 而言,设置的方式则是通过 CMD 或 ENTRYPOINT,实际使用时一般是通过 Dockerfile 直接设置,也可在执行docker run 时进行指定。无论是CMD或是ENTRYPOINT,都有shell与exec两种形式。
CMD
- shell格式
FROM busybox:1.0ENV name dockerCMD echo "hello $name"
- 执行
docker run --rm <image>通过默认命令来启动容器,此时容器打印 hello docker 并会自动被删除(因为增加了 —rm 参数) - 执行
docker run --rm <image> echo "hello world"来启动容器,此时echo "hello world"会覆盖默认命令,容器打印内容为 hello world

- exec格式
FROM busybox:1.0 ENV name docker CMD ["echo","hello $name"]
- 执行
docker run --rm <image>通过默认命令来启动容器,此时容器打印内容为 hello $name 变量并未被替换 - 执行
docker run --rm <image> echo "hello world"来启动容器,此时echo "hello world"会覆盖默认命令,容器打印内容为 hello world

- CMD 示例汇总:
无论是 shell 格式还是 exec 格式,Dockerfile 中 CMD 的指定内容均会被 docker run 命令中指定的 [COMMAND] [ARG…] 所替换

exec 格式下默认命令中的变量无法被替换,原因在于 shell 格式默认会调用 bin/sh -c 来执行命令,而 exec 则不会,若想达到同样的效果可在命令中直接指明使用哪种 shell,比如 /bin/sh。将 Dockerfile 修改为如下,此时重新构建镜像,再次依据默认命令执行时,变量 $name 会被解析,容器会输出 hello docker
FROM busybox:1.0 ENV name docker CMD ["/bin/sh","-c","echo hello $name"]注意:指明 shell 时需先确定该镜像中包含有此种 shell,否则执行时会出错,比如本次实例中 busybox 镜像并未包含 bash,若指定为 bash,即 CMD 值为[“/bin/bash”,”-c”,”echo hello $name”] 运行时会提示 /bin/bash 不存在
ENTRYPOINT
- shell格式
FROM busybox:1.0 ENV name docker ENTRYPOINT echo "hello $name"
- 执行
docker run --rm <image>通过默认命令来启动容器,此时容器打印 hello docker 执行
docker run --rm <image> echo "hello world"来启动容器,此时echo "hello world"并不会覆盖默认的命令,容器打印内容依旧为 hello docker<br />该示例中,ENTRYPOINT 的 shell 格式下默认命令的执行结果与 CMD 的 shell 格式并无区别,区别在于docker run 中指定的 [COMMAND] [ARG...] 所覆盖,该情况不难理解,因为 COMMAND 命令对应的是 Dockerfile 中的 CMD,要是想覆盖默认的ENTRYPOINT,需要使用 --entrypoint,比如:`docker run --rm --entrypoint echo busybox-entry:shell "hello world"`—entrypoint 设置的命令是 echo ,其会将默认的 echo “hello $name” 所覆盖
该命令中docker run 指定的 COMMAND 值为”hello world” ,此时的 COMMAND 会作为 ENTRYPOINT 的参数传入,所以真正的命令应该是 echo “hello world”

- exec格式
FROM busybox:1.0 ENV name docker ENTRYPOINT ["echo","hello $name"]
- 执行
docker run --rm <image>通过默认命令来启动容器,此时容器打印 hello $name 变量未被替换 执行
docker run --rm <image> echo "hello world"来启动容器,此时的echo "hello world"会作为参数,容器打印内容为 hello world
- ENTRYPOINT 示例汇总
- shell 格式下命令中的变量会被解析,exec 格式则不会
- shell 格式下,docker run 指定的 COMMAND 对默认的 ENTRYPOINT 命令无影响;exec 格式下,COMMAND 的内容会被当做参数传给默认的ENTRYPOINT命令
- 无论是 shell 格式还是 exec 格式,都可在 docker run 时通过 —entrypoint 来替换默认的 ENTRYPOINT 命令
使用汇总
shell 格式 | CMD | ENTRYPOINT | docker run | 实际命令 | 备注 | | —- | —- | —- | —- | —- | | echo “hello cmd” | 未设置 | echo “hello run” | echo “hello run” | 被 docker run 指定的 COMMAND 替换 | | 未设置 | echo “hello entrypoint” | echo “hello run” | echo “hello entrypoint” | 不会被替换 | | echo “hello cmd” | echo “hello entrypoint” | 未设置 | echo “hello entrypoint” | 不会被替换 | | echo “hello cmd” | echo “hello entrypoint” | echo “hello run” | echo “hello entrypoint” | 不会被替换 |
exec 格式 | CMD | ENTRYPOINT | docker run | 实际命令 | 备注 | | —- | —- | —- | —- | —- | | [“echo”,”hello cmd”] | 未设置 | [“echo”,”hello run”] | [“echo”,”hello run”] | 被 docker run 指定的 COMMAND 替换 | | 未设置 | [“echo”,”hello entrypoint”] | [“echo”,”hello run”] | [“echo”,”hello entrypoint echo hello run”] | COMMAND 内容当做参数传给ENTRYPOINT | | [“echo”,”hello cmd”] | [“echo”,”hello entrypoint”] | 未设置 | [“echo”,”hello entrypoint echo hello cmd”] | CMD 内容当做参数传给ENTRYPOINT | | [“echo”,”hello cmd”] | [“echo”,”hello entrypoint”] | [“echo”,”hello run”] | [“echo”,”hello entrypoint echo hello run”] | COMMAND 替换CMD 内容后当做参数传给ENTRYPOINT |
使用建议
- 灵活使用 docker run 中的 COMMAND 和 —entrypoint 来进行调试。比如容器本身进程无法正常运行,可在docker run 时指定一个前台运行的进程(个人常用tail -f /etc/hosts),再进入到容器内部来进行调试
- 若容器启动命令包含太多参数,尽量使用 ENTRYPOINT 与 CMD 结合的方式,CMD 中指定参数,这样方便在使用 docker run 时进行参数替换
