我们在构建Docker镜像的时候,可以通过定义Dockerfile文件的方式,文件中包含了一系列命令参数,而这些参数正是包含了 CMD,、RUN、COPY、ADD 和 ENTRYPOINT 等一系列指令。这篇文章我们一起来学习下其中RUN、CMD、ENTRYPOINT三个命令参数的作用及区别。文章涉及到实际操作,截图多,篇幅长,可以直接跳转到最后看总结了解。
Shell 和 Exec命令格式
无论是RUN、CMD还是ENTRYPOINT运行的命令,都可以Shell或者Exec格式的,两者有一定的区别。总的来说,Shell格式的命令,在底层调用时,会在命令前加上/bin/sh
,那么命令中的用$
修饰的参数会被替换;而Exec格式的命令,更像是字符串数组类型,如果自己没有添加/bin
,就不会进行参数替换。话不多说,看例子。
先创建一个空目录,目录中新建一个Dockerfile文件,文件内容如下:
FROM ubuntu:latestENV name UltramanRUN echo "RUN shell Hello $name!"#CMD echo "CMD shell Hello $name!"#ENTRYPOINT echo "ENTRYPOINT shell Hello $name!"#RUN ["echo","RUN exec Hello $name!"]#CMD ["echo","CMD exec Hello $name!"]#ENTRYPOINT ["echo", "ENTRYPOINT exec Hello $name!"]
注意这里#
是注释,方便一条一条命令做演示调试。
Shell
这里我们用RUN命令来做演示。接着我们根据此Dockerfile来构建镜像。
RUN命令其实是在构建镜像的时候会被执行,看图中框起来的部分可以看到RUN命令被正确执行,并且name
参数也成功替换了。这是Shell方式,我们再来看看Exec方式。
Exec
修改Dockerfile文件,并且构建,可以看到构建过程中有打印,但是name
变量没有被正确替换,如果想要使用Exec格式,需要修改下命令,例如:
RUN
我们在了解了两种命令格式之后,来看下三个命令的使用及区别。
RUN
命令是在构建镜像的时候被执行的,所以一般用来安装应用和软件包,一个Dockerfile中可以包含多个RUN命令来安装多个应用或者软件包。另外,我们知道Docker的镜像常常是由多个镜像层layer组成的,一个RUN命令就会在镜像的顶部可写层创建一个新的镜像层。
例如:
RUN yum update && yum install -y \ bzr \ cvs
注意 通常我们把yun update 和 yum install 命令会放在一个RUN指令中一起执行,这样可以保证每次安装的包都是最新版本的。如果我们分开来执行,比如放在两个RUN命令中,那么我们无法保证每次下载的都是最新的包,因为一个RUN命令对于Docker来说会生成一个新的layer层,有可能在之前就已经创建过yum update的层,有缓存。这个在了解了Docker的原理之后会更好的理解。
CMD
CMD
命令设置容器启动后默认执行的命令及其参数,但CMD能够被docker run后面跟的命令行参数替换。例如:
我们在Dockerfile中加上一段cmd echo "Hello world"
,然后docker build成镜像,并且执行docker run -it <imagename>
启动一个容器,看到输出
我们这时候修改启动命令,在启动命令后面加上一个命令,可以看到CMD的内容被覆盖了
另外,需要注意的是,如果Dockerfile中有多个CMD,那么只会有最后一个生效,如果容器启动时没有指定要执行的命令,那么就会执行最后一个。
ENTRYPOINT
ENTYRYPOINT
命令也可以用来当作启动容器时执行的命令,与CMD
不同的是,ENTRYPOINT
命令始终会被使用,也不会轻易的被docker启动命令覆盖。可以使用docker run --entrypoint参数覆盖。
举个例子说明:
ENTRYPOINT ["/bin/sh"]CMD ["-c","java $JAVA_OPTS -jar /demo.jar","--server.port=8000"]
很多官方的基础镜像的启动命令都是/bin/bash,容器启动就会进入终端命令行。
两种命令格式:
如果是ENTRYPOINT + [Exec]格式命令,那么CMD的参数会被追加在后面;
如果是ENTRYPOINT + shell格式命令,那么就会忽略CMD以及启动参数,当然此命令依然会被执行。
所以一般ENTRYPOINT用来当作Dockerfile的最后一行,CMD用于指定容器启动时要执行的命令参数,而ENTRYPOINT用于指定容器启动时要执行的可执行文件。可以将ENTRYPOINT看作是容器的默认入口点,而CMD则是对ENTRYPOINT指定的可执行文件的参数进行补充。
最佳实践就是:ENTRYPOINT命令用来定义主命令,CMD命令常用来给ENTRYPOINT命令提供参数。