在 Dockerfile 中,RUN、CMD 和 ENTRYPOINT 这三条指令看起来相似,它们的作用都是在 Docker 镜像构建过程中运行指定的命令。有时候很容易造成混淆,接下来我们来详细探讨它们之间的区别。
RUN、CMD、ENTRYPOINT的作用
-
RUN:执行命令并创建新的镜像层。当你需要在镜像构建过程中安装软件包或应用程序时,就会用到 RUN。每执行一次 RUN 就会在镜像上添加一个新的层。
-
CMD:设置容器启动后默认执行的命令及其参数。不过,CMD 指定的命令可以通过
docker run
命令行参数来覆盖。它主要用于为容器设定默认启动行为。如果 Dockerfile 中有多个 CMD 指令,只有最后一个生效。 -
ENTRYPOINT:配置容器启动时运行的命令,功能上与 CMD 类似,但有一个关键区别------即使在
docker run
时指定了其他命令,ENTRYPOINT 也不会被忽略,而是会与这些命令结合使用(除非使用--entrypoint
覆盖)。当容器作为应用程序或服务运行时,推荐使用 ENTRYPOINT,并且最好采用 Exec 格式。
我们可以通过两种方式指定 RUN、CMD 和 ENTRYPOINT 要运行的命令:Shell格式和Exec格式,这两种格式在使用上有一些微妙的差异。
Shell格式 和 Exec格式
Docker中的 RUN
, CMD
, 和 ENTRYPOINT
指令都可以采用两种不同的运行格式来指定要执行的命令:
Shell格式
Shell格式如下所示:
xml
<instruction> <command>
例如:
bash
RUN apt-get install python3
CMD echo "Hello world"
ENTRYPOINT echo "Hello world"
当执行命令时,Shell 格式的底层会使用 /bin/sh -c <command>
。当你以Shell格式运行命令时,由 ENV 命令定义的环境变量可以被获取到。
bash
ENV name Cloud Man
ENTRYPOINT echo "Hello, $name"
输出:
Hello, Cloud Man
Exec格式
Exec格式如下所示:
css
<instruction> ["executable", "param1", "param2", ...]
例如:
css
RUN ["apt-get", "install", "python3"]
CMD ["/bin/echo", "Hello world"]
ENTRYPOINT ["/bin/echo", "Hello world"]
当执行命令时,<command>
将被直接调用,不会被shell解析。在 ENV 中定义的环境变量也没法获取到。
bash
ENV name Cloud Man
ENTRYPOINT ["/bin/echo", "Hello, $name"]
输出:
bash
Hello, $name
推荐使用Exec格式进行CMD和ENTRYPOINT的指定,因为这样指令更易于阅读和理解。
RUN
RUN命令通常用于安装应用程序和软件包。RUN 在当前镜像之上执行命令,并通过创建一个新的镜像层。Dockerfile 通常包含多个RUN指令。
CMD
CMD指令允许用户指定容器默认执行的命令。当容器启动并且没有为docker run指定其他命令时,此命令将运行。
-
如果docker run指定了另一个命令,CMD指定的默认命令将被忽略。
-
如果Dockerfile中有多个CMD指令,只有最后一个CMD有效。
CMD有三种格式:
-
Exec格式:CMD ["executable","param1","param2"]
-
CMD ["param1", "param2"],这种格式与ENTRYPOINT结合使用,以提供额外的参数
-
Shell格式:CMD command param1 param2
推荐使用Exec格式,因为它提供了更好的可读性。
ENTRYPOINT
The ENTRYPOINT
directive allows the container to run as an application or service.
ENTRYPOINT在指定要执行的命令及其参数方面与CMD相似。不同之处在于,即使在运行docker run时指定了其他命令,ENTRYPOINT也不会被忽略,并且会被执行。
ENTRYPOINT有两种格式:
-
Exec格式:ENTRYPOINT ["executable", "param1", "param2"] 这是ENTRYPOINT推荐使用的格式。
-
Shell格式:ENTRYPOINT command param1 param2
ENTRYPOINT中的参数始终被使用,而CMD的额外参数可以在容器启动时动态替换。例如:
css
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
# Output
Hello world
请注意,ENTRYPOINT的Shell格式会忽略CMD或docker run提供的任何参数。
bash
FROM busybox
ENTRYPOINT echo hello
CMD world
hello
Docker中的 --entrypoint
命令行选项允许你在运行容器时覆盖Docker镜像中指定的ENTRYPOINT指令。
ini
$ docker run --entrypoint="path/to/custom/entrypoint" imagename
默认情况下,当你运行一个Docker容器时,会执行镜像中ENTRYPOINT指令指定的命令。但是,如果你想覆盖这种行为,可以使用 --entrypoint
选项来指定一个不同的入口点来使用。有时这对容器内部环境问题的调试很有帮助。
总结
-
使用 RUN 命令来安装应用程序和包,并创建新的镜像层。
-
如果Docker镜像的目的是运行一个应用程序或服务,例如运行 MySQL,那么应该优先使用 Exec 格式的 ENTRYPOINT 命令。CMD 可以为 ENTRYPOINT 提供额外的默认参数,并且这些默认参数可以被 docker run 命令行替换。
-
如果你想为容器设置默认的启动命令,可以使用 CMD 命令。用户可以在 docker run 命令行中覆盖这个默认命令。