目录
- RUN
- CMD
- ENTRYPOINT
- 总结
- 参考
本文主要是笔者个人对 Dockerfile 中
RUN
、
CMD
以及
ENTRYPOINT
这三个易混淆的指令的异同的理解, 并进行了一个简单的总结说明, 如有纰漏欢迎指正.
若想要直接看三者异同的总结可以直接转到
文章的总结部分.
RUN
-
RUN <command>
(shell 模式) -
RUN ["executable", "param1", "param2"]
(exec 模式)
RUN
指令将在当前镜像之上的新层中执行任何命令并提交(commit)结果. 生成的提交镜像将用于 Dockerfile 中的下一步. RUN
指令的重点在于是在构建镜像过程的命令, 一个 Dockerfile 可以有多个 RUN
命令并能依次执行, 最终运行的命令会反应到镜像上.
CMD
-
CMD ["executable","param1","param2"]
(exec 模式, 推荐使用) -
CMD command param1 param2
(shell 模式) -
CMD ["param1","param2"]
(作为ENTRYPOINT
指令的参数)
CMD
指令是在容器启动时自动执行的指令, 只能有一个 RUN
, 多个时仅最后一个生效. 这与 Docker 的 run
指令 docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
的 [COMMAND]
选项是等效的. 只不过 docker run
中的可以覆盖 Dockerfile 中的 CMD
指令.
此外, CMD
还可以作为 ENTRYPOINT
的参数. 在下文具体讲述.
如下的 Dockerfile:
FROM ubuntu
CMD ["hostname"]
当我们运行 docker build -t=cmdimg .
后使用 docker run --name=cmdtmp --hostname=cmdtmp -it cmdimg
运行后可以看到如下结果:
即运行容器时自动执行了 hostname
指令.
若使用 docker run --name=cmdtmp --hostname=cmdtmp -it cmdimg ls
运行后可以看到 ls
命令的输出, 而此时 Dockerfile 中 CMD
指令指定的 hostname
命令便被覆盖了.
CMD
指令与 RUN
指令直观而言, 若 Dockerfile 中有 CMD ["apt-get", "install" "golang" "-y"]
, 则会在每次启动容器时都会尝试下载安装 golang; 而如若以该 Dockerfile 生成的镜像作为基础镜像构建新的镜像, 则会发现新镜像中没有安装 golang. 而 RUN apt-get install golang -y
命令则不同, 它会在构建镜像时就将 golang 下载安装好, 且以此为基础镜像的新镜像也无需再安装 golang.
ENTRYPOINT
-
ENTRYPOINT ["executable","param1","param2"]
(exec 模式, 推荐使用) -
ENTRYPOINT command param1 param2
(shell 模式)
从功能上来讲, ENTRYPOINT
指令与 CMD
指令几乎一样, 都是在容器启动时自动执行的指令, 且只有最后一个有效.
而 ENTRYPINT
与 CMD
的不同, 笔者认为主要体现在两方面: 一是执行的命令不易被覆盖, 二是用于用户传参. docker run
中的 [COMMAND]
选项不会覆盖 ENTRYPOINT
指令, 而此时 [COMMAND]
选项便会成为 ENTRYPOINT
指令的参数, 进一步 Dockerfile 中的 CMD
指令的内容便会成为 ENTRYPOINT
的默认参数, 也就是上述 CMD
指令的第三种用法. 从实际应用来看, 若我们容器中部署了一个需要用户输入参数的程序并在容器启动时自动执行, 此时便只能选择 ENTRYPOINT
指令从而让用户可以在启动容器时输入参数.
如下的 Dockerfile:
FROM ubuntu
ENTRYPOINT ["ls"]
当我们运行 docker build -t=entimg .
后使用 docker run --name=enttmp -it entimg
运行后可以看到如下结果:
而我们在 Dockerfile 中追加 CMD ["-a"]
指令后重新构建镜像并运行, 输出结果如下, 可以看到 CMD
指令的 -a
作为了 ENTRYPOINT
指令中 ls
的参数, 输出中多了 .
, ..
等因此文件:
而如若我们使用 docker run --name=enttmp -it entimg -l
指令运行容器, 则会发现 -l
选项覆盖率 CMD
指令的 -a
选项成为了 ENTRYPOINT
的 ls
的参数.
进一步, 要需要覆盖 Dockerfile 中的 ENTRYPOINT
指令, 则需要在 docker run
中使用 --entrypoint
选项. 比如我们使用 docker run --name=enttmp -it --entrypoint=ps entimg
指令运行容器, 则会将原本的 ls
指令覆盖为 ps
指令:
总结
-
RUN
、CMD
和ENTRYPOINT
指令都可以用来执行具体的命令. -
RUN
指令是在 Docker 镜像构建时发挥作用, 可以使用多个该命令, 且执行结果会记录到镜像中. -
CMD
和ENTYPOINT
指令是在容器启动时自动执行, 均只有最后一个该指令有效, 且均可以在docker run
中被覆盖. -
ENTRYPOINT
指令和CMD
的区别在于使用ENTRYPOINT
时CMD
指令会被作为其默认参数, 而用户也可以在启动容器时通过覆盖CMD
指令来输入参数; 此外, 这也意味着ENTRYPOINT
指令的内容不易被用户命令覆盖.
参考