Docker里的RUN-CMD-ENTRYPOINT
概述
网上的 Dockerfile 很好找,同时也很好 copy,但是当真正需要自己去写 Dockerfile 的时候还是会有不少让人感觉 confused 的地方,就比如 RUN
, CMD
和 ENTRYPOINT
这三个指令看上去很类似,但很容易混淆,写的时候有些时候能够通用,但是却又得到不同的效果。本文主要针对这三个命令简单解释一下。
Shell和Exec格式
我们可用两种方式指定 RUN
、CMD
和 ENTRYPOINT
要运行的命令: Shell 格式和 Exec 格式,二者在使用上有细微的区别。
Shell格式
Shell 格式 <instruction> <command>
例如:
|
|
当指令执行时,Shell 格式底层会调用 /bin/sh -c <command>
,所以上述命令,其实底层是执行了这样的命令。
|
|
例如下面的 Dockerfile 片段,指定了 ENV
:
|
|
执行 docker run <image>
将输出: Hello, runzhliu
,注意环境变量 name
已经被值替换。因为其底层执行的命令是这样的 /bin/sh -c echo "Hello, $name"
。
Exec格式
Exec 格式 <instruction> ["executable", "param1", "param2", ...]
。
例如:
|
|
当指令执行时,会直接调用命令,不会被 Shell 解析。例如下面的 Dockerfile 片段:
|
|
运行容器将输出: Hello, $name
。注意环境变量 “name” 没有被替换。如果希望使用环境变量,照如下修改:
|
|
运行容器将输出: Hello, runzhliu
。
CMD
, ENTRYPOINT
推荐使用 Exec
格式,因为指令可读性更好,更容易理解。RUN
则两种格式都可以。
RUN
RUN
指令通常用于安装应用和软件包。在当前镜像的顶部执行命令,并通过创建新的镜像层。Dockerfile 中常常包含多个 RUN
指令。
RUN
有两种格式:
- Shell格式:
RUN
- Exec格式:
RUN ["executable", "param1", "param2"]
下面是使用 RUN
安装多个包的例子:
|
|
注意: apt-get update
和 apt-get install
被放在一个 RUN
指令中执行,这样能够保证每次安装的是最新的包。如果 apt-get install
在单独的 RUN
中执行,则会使用 apt-get update
创建的镜像层,而这一层可能是很久以前缓存的。
CMD
CMD
指令允许用户指定容器的默认执行的命令。此命令会在容器启动且 docker run
没有指定其他命令时运行。
如果 docker run
指定了其他命令,CMD
指定的默认命令将被忽略。如果 Dockerfile 中有多个 CMD
指令,只有最后一个 CMD
有效,当然一般不会写多个 CMD
命令在 Dockerfile 中。
如本文开头说的,CMD
有两种格式:
- Exec格式:
CMD ["executable", "param1", "param2"]
。这是CMD
的推荐格式。CMD ["param1","param2"]
为ENTRYPOINT
提供额外的参数,此时ENTRYPOINT
必须使用Exec格式 - Shell格式:
CMD command param1 param2
。Exec和Shell格式前面已经介绍过了。第二种格式CMD ["param1","param2"]
要与Exec格式的ENTRYPOINT
指令配合使用,其用途是为ENTRYPOINT
设置默认的参数
下面看看 CMD
是如何工作的。Dockerfile 片段如下: CMD echo "Hello world"
。运行容器 docker run -it [image]
将输出 Hello world
。
但当后面加上一个命令,比如 docker run -it [image] /bin/bash
,CMD
会被忽略掉,命令 bash
将被执行。
ENTRYPOINT
ENTRYPOINT
指令可让容器以应用程序或者服务的形式运行。
ENTRYPOINT
看上去与 CMD
很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT
不会被忽略,一定会被执行,即使运行 docker run
时指定了其他命令。
ENTRYPOINT
有两种格式:
- Exec格式:
ENTRYPOINT ["executable", "param1", "param2"]
这是ENTRYPOINT
的推荐格式 - Shell格式:
ENTRYPOINT command param1 param2
在为 ENTRYPOINT
选择格式时必须小心,因为这两种格式的效果差别很大。
ENTRYPOINT
的 Exec 格式用于设置要执行的命令及其参数,同时可通过 CMD
提供额外的参数。ENTRYPOINT
中的参数始终会被使用,而 CMD
的额外参数可以在容器启动时动态替换掉。
比如下面的 Dockerfile 片段:
|
|
当容器通过 docker run -it [image]
启动时,输出为: Hello world
。而如果通过 docker run -it [image] runzhliu
启动,则输出为: Hello runzhliu
。ENTRYPOINT
的 Shell 格式会忽略任何 CMD
或 docker run
提供的参数。
最佳实践
使用 RUN
指令安装应用和软件包,构建镜像。如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT
指令。CMD
可为 ENTRYPOINT
提供额外的默认参数,同时可利用 docker run
命令行替换默认参数。如果想为容器设置默认的启动命令,可使用 CMD
指令。用户可在 docker run
命令行中替换此默认命令。