3.3 RUN vs CMD vs ENTRYPOINT
RUN、CMD和ENTRYPOINT这三个Dockerfile指令看上去很类似,很容易混淆。本节将通过实践详细讨论它们的区别。
简单地说:
(1)RUN:执行命令并创建新的镜像层,RUN经常用于安装软件包。
(2)CMD:设置容器启动后默认执行的命令及其参数,但CMD能够被docker run后面跟的命令行参数替换。
(3)ENTRYPOINT:配置容器启动时运行的命令。
下面我们详细分析。
3.3.1 Shell和Exec格式
我们可用两种方式指定RUN、CMD和ENTRYPOINT要运行的命令:Shell格式和Exec格式,二者在使用上有细微的区别。
Shell格式:
<instruction> <command>
例如:
RUN apt-get install python3 CMD echo "Hello world" ENTRYPOINT echo "Hello world"
当指令执行时,shell格式底层会调用 /bin/sh -c [command]。例如下面的Dockerfile片段:
ENV name Cloud Man ENTRYPOINT echo "Hello, $name"
执行docker run [image]将输出:
Hello, Cloud Man
注意环境变量name已经被值Cloud Man替换。
下面来看Exec格式。
<instruction> ["executable", "param1", "param2", ...]
例如:
RUN ["apt-get", "install", "python3"] CMD ["/bin/echo", "Hello world"] ENTRYPOINT ["/bin/echo", "Hello world"]
当指令执行时,会直接调用 [command],不会被shell解析。
例如下面的Dockerfile片段:
ENV name Cloud Man ENTRYPOINT ["/bin/echo", "Hello, $name"]
运行容器将输出:
Hello, $name
注意:环境变量name没有被替换。
如果希望使用环境变量,做如下修改Dockerfile:
ENV name Cloud Man ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
运行容器将输出:
Hello, Cloud Man
CMD和ENTRYPOINT推荐使用Exec格式,因为指令可读性更强,更容易理解。RUN则两种格式都可以。
3.3.2 RUN
RUN指令通常用于安装应用和软件包。
RUN在当前镜像的顶部执行命令,并创建新的镜像层。Dockerfile中常常包含多个RUN指令。
RUN有两种格式:
(1)Shell格式:RUN
(2)Exec格式:RUN ["executable", "param1", "param2"]
下面是使用RUN安装多个包的例子:
RUN apt-get update && apt-get install-y\bzr\cvs\git\mercurial\ subversion
注意:apt-get update和apt-get install被放在一个RUN指令中执行,这样能够保证每次安装的是最新的包。如果apt-get install在单独的RUN中执行,则会使用apt-get update创建镜像层,而这一层可能是很久以前缓存的。
3.3.3 CMD
CMD指令允许用户指定容器的默认执行的命令。
此命令会在容器启动且docker run没有指定其他命令时运行。
● 如果docker run指定了其他命令,CMD指定的默认命令将被忽略。
● 如果Dockerfile中有多个CMD指令,只有最后一个CMD有效。
CMD有三种格式:
(1)Exec格式:CMD ["executable", "param1", "param2"]
这是CMD的推荐格式。
(2)CMD["param1", "param2"]为ENTRYPOINT提供额外的参数,此时ENTRYPOINT必须使用Exec格式。
(3)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:/#
3.3.4 ENTRYPOINT
ENTRYPOINT指令可让容器以应用程序或者服务的形式运行。
ENTRYPOINT看上去与CMD很像,它们都可以指定要执行的命令及其参数。不同的地方在于ENTRYPOINT不会被忽略,一定会被执行,即使运行docker run时指定了其他命令。
ENTRYPOINT有两种格式:
(1)Exec格式:ENTRYPOINT ["executable", "param1", "param2"]这是ENTRYPOINT的推荐格式。
(2)Shell格式:ENTRYPOINT command param1 param2。
在为ENTRYPOINT选择格式时必须小心,因为这两种格式的效果差别很大。
1. Exec格式
ENTRYPOINT的Exec格式用于设置要执行的命令及其参数,同时可通过CMD提供额外的参数。
ENTRYPOINT中的参数始终会被使用,而CMD的额外参数可以在容器启动时动态替换掉。
比如下面的Dockerfile片段:
ENTRYPOINT ["/bin/echo", "Hello"] CMD ["world"]
当容器通过docker run -it [image]启动时,输出为:
Hello world
而如果通过docker run -it [image] CloudMan启动,则输出为:
Hello CloudMan
2. Shell格式
ENTRYPOINT的Shell格式会忽略任何CMD或docker run提供的参数。
3.3.5 最佳实践
(1)使用RUN指令安装应用和软件包,构建镜像。
(2)如果Docker镜像的用途是运行应用程序或服务,比如运行一个MySQL,应该优先使用Exec格式的ENTRYPOINT指令。CMD可为ENTRYPOINT提供额外的默认参数,同时可利用docker run命令行替换默认参数。
(3)如果想为容器设置默认的启动命令,可使用CMD指令。用户可在docker run命令行中替换此默认命令。