这里对Dockerfile中的CMD、ENTRYPOINT指令进行介绍
CMD指令
该指令可以用于指定容器被启动时需要运行的命令。具体地,其支持shell、exec两种形式的语法
1 | # shell格式 |
通常Docker中推荐使用exec格式语法,原因有二。一方面,shell格式语法下会通过/bin/sh -c来执行命令;另一方面,某些镜像甚至不包含Shell,致使shell格式下的命令无法被正常执行。但使用exec格式时,会无法获取环境变量的值。此时则可以考虑使用shell格式语法
shell格式
下面通过Dockerfile定义一个名为demo1:test的镜像
1 | # 镜像 demo1:test |
然后我们创建一个容器
1 | docker run --name demo1A --rm -it demo1:test |
如下所示,其会使用CMD指令设置的命令、参数执行
对于CMD指定的命令而言,不可以通过 docker run命令行实现传递参数。因为 CMD指令的命令、参数 会被 docker run命令行参数中指定的命令、参数 完全覆盖。例如我们创建一个容器,期望执行 ping weibo.com。则需要重新传递命令、参数
1 | # 错误方式: |
效果如下所示
又比如按下述方式创建命令,同理
1 | docker run --name demo1C --rm -it demo1:test top -H |
效果如下所示
exec格式
下面通过Dockerfile定义一个名为demo2:test的镜像
1 | # 镜像 demo2:test |
然后我们创建一个容器
1 | docker run --name demo2A --rm -it demo2:test |
如下所示,其会使用CMD指令设置的命令、参数执行
对于CMD指定的命令而言,不可以通过 docker run命令行实现传递参数。因为 CMD指令的命令、参数 会被 docker run命令行参数中指定的命令、参数 完全覆盖。例如我们创建一个容器,期望执行 ping weibo.com。则需要重新传递命令、参数
1 | # 错误方式 |
效果如下所示
又比如按下述方式创建命令,同理
1 | docker run --name demo2C --rm -it demo2:test top -H |
效果如下所示
ENTRYPOINT指令
该指令同样可以用于指定容器被启动时需要运行的命令。同理,其同样支持shell、exec两种形式的语法
1 | # shell格式 |
对于ENTRYPOINT指令而言,Docker中同样推荐使用exec格式语法,理由与CMD指令同理
shell格式
下面通过Dockerfile定义一个名为demo3:test的镜像
1 | # 镜像 demo3:test |
然后我们创建一个容器
1 | docker run --name demo3A --rm -it demo3:test |
如下所示,其会使用ENTRYPOINT指令设置的命令、参数执行
ENTRYPOINT指令 所设置命令、参数可被 docker run命令行参数中指定要运行的命令 覆盖, 但需要使用 —entrypoint 选项进行显式覆盖。否则将会忽略命令行参数
1 | # 错误方式 |
效果如下所示
当我们使用 —entrypoint 选项进行显式覆盖命令时,还可以传递参数
1 | docker run --name demo3D --rm -it --entrypoint ping demo3:test bing.com.cn |
效果如下所示
对于shell格式的ENTRYPOINT指令设置的命令而言,如果没有使用—entrypoint 选项。当通过 docker run命令行传递参数时, 其会被忽略
1 | docker run --name demo3E --rm -it demo3:test -H -m |
效果如下所示
exec格式
下面通过Dockerfile定义一个名为demo4:test的镜像
1 | # 镜像 demo4:test |
然后我们创建一个容器
1 | docker run --name demo4A --rm -it demo4:test |
如下所示,其会使用ENTRYPOINT指令设置的命令、参数执行
ENTRYPOINT指令 所设置命令、参数可被 docker run命令行参数中指定要运行的命令 覆盖, 但需要使用 —entrypoint 选项进行显式覆盖。否则将会忽略命令行参数
1 | # 错误方式 |
效果如下所示
当我们使用 —entrypoint 选项进行显式覆盖命令时,还可以传递参数
1 | docker run --name demo4D --rm -it --entrypoint ping demo4:test weibo.com |
效果如下所示
对于exec格式的ENTRYPOINT指令设置的命令而言,如果没有使用—entrypoint 选项。当通过 docker run命令行实现传递参数时, 其会被追加
1 | 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的镜像
1 | # 镜像 demo5:test |
然后我们创建一个容器
1 | docker run --name demo5A --rm -it demo5:test |
效果如下所示
由于此场景下CMD指令提供的是一个默认的可变参数,故我们可以通过docker run命令行参数 来覆盖 CMD指定的默认可变参数
1 | docker run --name demo5B --rm -it demo5:test -m |
效果如下所示
同理,ENTRYPOINT指令 所设置命令、参数可被 docker run命令行参数中指定要运行的命令 覆盖, 使用 —entrypoint 选项进行显式覆盖即可。当然此时CMD指令设置的默认可变参数也将失效
1 | docker run --name demo5C --rm -it --entrypoint ifconfig demo5:test |
效果如下所示
当然使用 —entrypoint 选项进行显式覆盖命令时,依然可以传递参数
1 | docker run --name demo5D --rm -it --entrypoint ping demo5:test weibo.com |
效果如下所示
参考文献
- 第一本Docker书·修订版 James Turnbull著
- 深入浅出Docker [英]Nigel Poulton著