记录 docker 中 exec form 和 shell form 的区别,CMD 和 ENTRYPOINT 区别,以及最佳实践。
exec form VS shell form
# exec form
<instruction> ["executable", "param1", "param2", ...]
# shell form
<instruction> <command>
- exec form 以 JSON 格式解析,所以命令参数必须使用
""双引号包裹。 - exec form 不会 invoke shell. 所以
CMD [ "echo", "$HOME" ]中$HOME变量不会被替换。 - shell form 实际是执行
/bin/sh -c "<command>"。 - 优先使用 exec form,因为在 shell form 中spawns your application in a new process and you won’t receive signals from Docker,在 k8s 中会遇到问题。
- 在 shell form 也可以使用
exec <cmd>形式。
CMD VS ENTRYPOINT
直接翻译 SO 上面的回答,比较清楚
ENTRYPOINT 是容器执行入口,CMD 是参数设置。
Docker 有默认的 ENTRYPOINT:/bin/sh -c,但是没有默认的 CMD.但是一般镜像都会设置一个默认的 CMD.(注意是docker的默认 ENTRYPOINT,和镜像的默认CMD,基础镜像一般不设置 ENTRYPOINT)docker run -i -t ubuntu bash the ENTRYPOINT is the default /bin/sh -c, the image is ubuntu and the command is bash.
所以上面的命令实际上在启动容器中执行了/bin/sh -c bash. 不是所有场景都需要"/bin/sh"的,所以引入了 ENTRYPOINT and --entrypoint.docker run -i -t ubuntu中 ubuntu(镜像名) 后面跟的所有内容都作为参数传递给 entrypoint. 这和使用CMD指令是完全一样的,也即是CMD指令可以在docker run中覆盖。
由于 ubuntu 镜像设置了默认 CMD: CMD ["bash"],所以docker run -i -t ubuntu和docker run -i -t ubuntu bash是完全一样的效果。
所以到此,可以总结:ENTRYPOINT 是容器的执行入口,CMD 是参数设置,不过参数也可以是 bash 中的可执行命令 (例如,CMD ["echo","hello"],实际执行 /bin/sh -c "echo hello").
ENTRYPOINT 和 CMD 的搭配可以实现将容器作为一个可执行文件启动,这个特性也是我们日常使用 docker 的主要目的。例如在 Dockerfile 中设置:
ENTRYPOINT ["/bin/cat"]
运行docker run cat-img /etc/passwd,/etc/passwd 是 cmd, 实际执行的是/bin/cat /etc/passwd. 恭喜你,得到一个 cat 程序,假设你安装了一个 linux 系统,里面没有 cat 命令,cat-img 镜像就可以实现你想要的功能。
再例如你有个 redis 镜像,与其运行 docker run redis-img redis -H srv-host -u toto get key,
不如设置ENTRYPOINT ["redis", "-H", "srv-host", "-u", "toto"] 然后运行docker run redis-img get key.
- Dockerfile 只有最后一个 CMD 会生效;
- 可以使用
docker inspect <img-id>查看默认的CMD参数; - 取消默认 ENTRYPOINT,可以在 Dockerfile 中设置:
ENTRYPOINT []