每天5分钟玩转Docker容器技术
上QQ阅读APP看书,第一时间看更新

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命令行中替换此默认命令。