RUN、CMD 和 ENTRYPOINT 这三个 Dockerfile 指令看上去很类似很容易混淆。本节将通过实践详细讨论它们的区别。
简单的说
RUN 执行命令并创建新的镜像层RUN 经常用于安装软件包。
CMD 设置容器启动后默认执行的命令及其参数但 CMD 能够被
docker run
后面跟的命令行参数替换。ENTRYPOINT 配置容器启动时运行的命令。
下面我们详细分析。
Shell 和 Exec 格式
我们可用两种方式指定 RUN、CMD 和 ENTRYPOINT 要运行的命令Shell 格式和 Exec 格式二者在使用上有细微的区别。
Shell 格式
例如
RUN apt-get install python3
CMD echo "Hello world"
ENTRYPOINT echo "Hello world"
当指令执行时shell 格式底层会调用 /bin/sh -c
例如下面的 Dockerfile 片段
ENV name Cloud Man
ENTRYPOINT echo "Hello, $name"
执行 docker run
Hello, Cloud Man
注意环境变量 name
已经被值 Cloud Man
替换。
下面来看 Exec 格式。
Exec 格式
例如
RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello world"]
ENTRYPOINT ["/bin/echo", "Hello world"]
当指令执行时会直接调用
例如下面的 Dockerfile 片段
ENV name Cloud Man
ENTRYPOINT ["/bin/echo", "Hello, $name"]
运行容器将输出
Hello, $name
注意环境变量“name”没有被替换。
如果希望使用环境变量照如下修改
ENV name Cloud Man
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
运行容器将输出
Hello, Cloud Man
CMD 和 ENTRYPOINT 推荐使用 Exec 格式因为指令可读性更强更容易理解。RUN 则两种格式都可以。
RUN
RUN 指令通常用于安装应用和软件包。
RUN 在当前镜像的顶部执行命令并通过创建新的镜像层。Dockerfile 中常常包含多个 RUN 指令。
RUN 有两种格式
Shell 格式RUN
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 创建的镜像层而这一层可能是很久以前缓存的。
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 [p_w_picpath] 将输出
Hello world
但当后面加上一个命令比如 docker run -it [p_w_picpath] /bin/bashCMD 会被忽略掉命令 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"]
CMD ["world"]
当容器通过 docker run -it [p_w_picpath] 启动时输出为
Hello world
而如果通过 docker run -it [p_w_picpath] CloudMan 启动则输出为
Hello CloudMan
Shell 格式
ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。
最佳实践
使用 RUN 指令安装应用和软件包构建镜像。
如果 Docker 镜像的用途是运行应用程序或服务比如运行一个 MySQL应该优先使用 Exec 格式的 ENTRYPOINT 指令。CMD 可为 ENTRYPOINT 提供额外的默认参数同时可利用 docker run 命令行替换默认参数。
如果想为容器设置默认的启动命令可使用 CMD 指令。用户可在 docker run 命令行中替换此默认命令。
到这里我们已经具备编写 Dockerfile 的能力了。如果大家还觉得没把握推荐一个快速掌握 Dockerfile 的方法去 Docker Hub 上参考那些官方镜像的 Dockerfile。
好了我们已经学习完如何创建自己的 p_w_picpath下一节讨论如何分发 p_w_picpath。