Dockerfile之CMD、ENTRYPOINT指令
这里对Dockerfile中的CMD、ENTRYPOINT指令进行介绍
CMD指令
该指令可以用于指定容器被启动时需要运行的命令。具体地,其支持shell、exec两种形式的语法
# shell格式
CMD command param1 param2
# exec格式
CMD ["command", "param1", "param1"]
通常Docker中推荐使用exec格式语法,原因有二。一方面,shell格式语法下会通过/bin/sh -c来执行命令;另一方面,某些镜像甚至不包含Shell,致使shell格式下的命令无法被正常执行。但使用exec格式时,会无法获取环境变量的值。此时则可以考虑使用shell格式语法
shell格式
下面通过Dockerfile定义一个名为demo1:test的镜像
# 镜像 demo1:test
FROM busybox:1.35.0
# 使用 shell 格式的CMD
CMD ping baidu.com
然后我们创建一个容器
docker run --name demo1A --rm -it demo1:test
如下所示,其会使用CMD指令设置的命令、参数执行
对于CMD指定的命令而言,不可以通过 docker run命令行实现传递参数。因为 CMD指令的命令、参数 会被 docker run命令行参数中指定的命令、参数 完全覆盖。例如我们创建一个容器,期望执行 ping weibo.com。则需要重新传递命令、参数
# 错误方式:
docker run --name demo1B --rm -it demo1:test weibo.com
# 正确方式
docker run --name demo1B --rm -it demo1:test ping weibo.com
效果如下所示
又比如按下述方式创建命令,同理
docker run --name demo1C --rm -it demo1:test top -H
效果如下所示
exec格式
下面通过Dockerfile定义一个名为demo2:test的镜像
# 镜像 demo2:test
FROM busybox:1.35.0
# 使用 exec 格式的CMD
CMD ["ping", "baidu.com"]
然后我们创建一个容器
docker run --name demo2A --rm -it demo2:test
如下所示,其会使用CMD指令设置的命令、参数执行
对于CMD指定的命令而言,不可以通过 docker run命令行实现传递参数。因为 CMD指令的命令、参数 会被 docker run命令行参数中指定的命令、参数 完全覆盖。例如我们创建一个容器,期望执行 ping weibo.com。则需要重新传递命令、参数
# 错误方式
docker run --name demo2B --rm -it demo2:test weibo.com
# 正确方式
docker run --name demo2B --rm -it demo2:test ping weibo.com
效果如下所示
又比如按下述方式创建命令,同理
docker run --name demo2C --rm -it demo2:test top -H
效果如下所示
ENTRYPOINT指令
该指令同样可以用于指定容器被启动时需要运行的命令。同理,其同样支持shell、exec两种形式的语法
# shell格式
ENTRYPOINT command param1 param2
# exec格式
ENTRYPOINT ["command", "param1", "param1"]
对于ENTRYPOINT指令而言,Docker中同样推荐使用exec格式语法,理由与CMD指令同理
shell格式
下面通过Dockerfile定义一个名为demo3:test的镜像
# 镜像 demo3:test
FROM busybox:1.35.0
# 使用 shell 格式的ENTRYPOINT
ENTRYPOINT top -b
然后我们创建一个容器
docker run --name demo3A --rm -it demo3:test
如下所示,其会使用ENTRYPOINT指令设置的命令、参数执行
ENTRYPOINT指令 所设置命令、参数可被 docker run命令行参数中指定要运行的命令 覆盖, 但需要使用 --entrypoint 选项进行显式覆盖。否则将会忽略命令行参数
# 错误方式
docker run --name demo3B --rm -it demo3:test ifconfig
# 正确方式
docker run --name demo3C --rm -it --entrypoint ifconfig demo3:test
效果如下所示
当我们使用 --entrypoint 选项进行显式覆盖命令时,还可以传递参数
docker run --name demo3D --rm -it --entrypoint ping demo3:test bing.com.cn
效果如下所示
对于shell格式的ENTRYPOINT指令设置的命令而言,如果没有使用--entrypoint 选项。当通过 docker run命令行传递参数时, 其会被忽略
docker run --name demo3E --rm -it demo3:test -H -m
效果如下所示
exec格式
下面通过Dockerfile定义一个名为demo4:test的镜像
# 镜像 demo4:test
FROM busybox:1.35.0
# 使用 exec 格式的ENTRYPOINT
ENTRYPOINT ["top", "-b"]
然后我们创建一个容器
docker run --name demo4A --rm -it demo4:test
如下所示,其会使用ENTRYPOINT指令设置的命令、参数执行
ENTRYPOINT指令 所设置命令、参数可被 docker run命令行参数中指定要运行的命令 覆盖, 但需要使用 --entrypoint 选项进行显式覆盖。否则将会忽略命令行参数
# 错误方式
docker run --name demo4B demo4:test ifconfig
# 正确方式
docker run --name demo4C --rm -it --entrypoint ifconfig demo4:test
效果如下所示
当我们使用 --entrypoint 选项进行显式覆盖命令时,还可以传递参数
docker run --name demo4D --rm -it --entrypoint ping demo4:test weibo.com
效果如下所示
对于exec格式的ENTRYPOINT指令设置的命令而言,如果没有使用--entrypoint 选项。当通过 docker run命令行实现传递参数时, 其会被追加
docker run --name demo4E --rm -it demo4:test -H -m
效果如下所示
组合使用
对于大多数场景下,CMD、ENTRYPOINT指令都是互相通用的,而且一般也会只使用其中一种指令。具体地,CMD指令方便镜像使用者更改容器运行的命令,故适用于较为灵活的场景;而如果不期望镜像使用者去轻易更改容器运行的命令,故推荐使用ENTRYPOINT指令。同时如前文所述,exec格式较shell格式更为推荐。而对于CMD、ENTRYPOINT指令二者组合使用时,其效果可参考下图
事实上对于组合使用二者来说,更为常见的一种实践方式是通过exec格式的ENTRYPOINT设置固定的命令、参数,而利用exec格式的CMD 设置默认的可变参数
下面通过Dockerfile定义一个名为demo5:test的镜像
# 镜像 demo5:test
FROM busybox:1.35.0
# 使用 exec 格式的ENTRYPOINT 设置固定的命令、参数
ENTRYPOINT ["top", "-b"]
# 使用 exec 格式的CMD 设置默认的可变参数
CMD ["-H"]
然后我们创建一个容器
docker run --name demo5A --rm -it demo5:test
效果如下所示
由于此场景下CMD指令提供的是一个默认的可变参数,故我们可以通过docker run命令行参数 来覆盖 CMD指定的默认可变参数
docker run --name demo5B --rm -it demo5:test -m
效果如下所示
同理,ENTRYPOINT指令 所设置命令、参数可被 docker run命令行参数中指定要运行的命令 覆盖, 使用 --entrypoint 选项进行显式覆盖即可。当然此时CMD指令设置的默认可变参数也将失效
docker run --name demo5C --rm -it --entrypoint ifconfig demo5:test
效果如下所示
当然使用 --entrypoint 选项进行显式覆盖命令时,依然可以传递参数
docker run --name demo5D --rm -it --entrypoint ping demo5:test weibo.com
效果如下所示
参考文献
第一本Docker书·修订版 James Turnbull著 深入浅出Docker Nigel Poulton著