
RUN、CMD 和 ENTIRYPOINT这三个Dockerfile指令看起来都很类似,很容易搞混。我们来通过一些实践来详细讨论一下它们之间的差别。
简单的说:
RUN执行命令并创建新的镜像层,RUN经常用来安装Docker image中需要的软件。CMD设置容器启动后默认执行的命令及其参数,但CMD能够被docker run后面跟着的命令行参数代替。ENTIRYPOINT配置容器启动时运行的命令。
Shell和Exec格式
我们可以用Shell和Exec两种方式指定RUN、CMD和ENTIRYPOINT要运行的命令,这两者在使用中有细微的区别。
Shell格式
<instruction> <command> |
例如:
RUN apt-get install python3 |
当指令执行是,shell格式底层会调用/bin/sh -c <command>。
例如下面的Dockerfile片段:
ENV name Tony Deng |
注意环境变量
name已经被值Tony Deng替换了
Exec格式
<instruction> ["executable","param1","param2",...] |
例如:
RUN ["apt-get","install","python3"] |
当指令执行是,会直接调用<command>,不会被shell解析。
例如下面的Dockerfile片段:
ENV name Tony Deng |
运行容器将输出
Hello, $name |
注意环境变量
name没有被替换
如果希望使用环境变量,可以这样来修改
ENV name Tony Deng |
这样,容器将会输出
Hello, Tony Deng |
CMD和ENTRYPOINT推荐使用Exec格式,因为指令可读性更强,更容易理解,RUN则两种格式都可以。
RUN
RUN指令通常用于安装应用和软件包。
RUN在当前镜像的顶部执行命令,并创建新的镜像层。Dockerfile中常常包含多个RUN指令。
安装多个包的例子:
RUN apt-get update && apt-get install -y \ |
注意:
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有三种格式:
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设置默认的参数。我们将在后面讨论ENTRYPOINT时举例说明。
下面看看 CMD 是如何工作的。Dockerfile 片段如下:
CMD echo "Hello world" |
运行容器 docker run -it [image] 将输出:
Hello world |
但当后面加上一个命令,比如 docker run -it [image] /bin/bash,CMD 会被忽略掉,命令 bash 将被执行:
root@10a32dc7d3d3:/# |
ENTRYPOINT
ENTRYPOINT指令可让容器以应用程序或者服务的形式运行。
ENTRYPOINT看上去与CMD很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT不会被忽略,一定会被执行,即使运行docker run时指定了其他命令。
ENTRYPOINT有两种格式:
Exec格式:ENTRYPOINT ["executable", "param1", "param2"]这是ENTRYPOINT的推荐格式。Shell格式:ENTRYPOINT command param1 param2
在为 ENTRYPOINT 选择格式时必须小心,因为这两种格式的效果差别很大。
Exec 格式
ENTRYPOINT 的 Exec 格式用于设置要执行的命令及其参数,同时可通过 CMD 提供额外的参数。
ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。
比如下面的 Dockerfile 片段:
ENTRYPOINT ["/bin/echo", "Hello"] |
当容器通过 docker run -it [image] 启动时,输出为:
Hello world |
而如果通过 docker run -it [image] TonyDeng 启动,则输出为:
Hello TonyDengq |
Shell 格式
ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。
最佳实践
使用 RUN 指令安装应用和软件包,构建镜像。
如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命令行替换默认参数。
如果想为容器设置默认的启动命令,可使用 CMD 指令。用户可在 docker run 命令行中替换此默认命令。
参考
Dockerfile: ENTRYPOINT vs CMD
Abhishek Tomar在Slideshare关于Docker的分享