本文主要是笔者个人对 Dockerfile 中 RUNCMD 以及 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 指令几乎一样, 都是在容器启动时自动执行的指令, 且只有最后一个有效.
ENTRYPINTCMD 的不同, 笔者认为主要体现在两方面: 一是执行的命令不易被覆盖, 二是用于用户传参. 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 选项成为了 ENTRYPOINTls 的参数.
在这里插入图片描述
进一步, 要需要覆盖 Dockerfile 中的 ENTRYPOINT 指令, 则需要在 docker run 中使用 --entrypoint 选项. 比如我们使用 docker run --name=enttmp -it --entrypoint=ps entimg 指令运行容器, 则会将原本的 ls 指令覆盖为 ps 指令:
在这里插入图片描述

总结

  • RUNCMDENTRYPOINT 指令都可以用来执行具体的命令.
  • RUN 指令是在 Docker 镜像构建时发挥作用, 可以使用多个该命令, 且执行结果会记录到镜像中.
  • CMDENTYPOINT 指令是在容器启动时自动执行, 均只有最后一个该指令有效, 且均可以在 docker run 中被覆盖.
  • ENTRYPOINT 指令和 CMD 的区别在于使用 ENTRYPOINTCMD 指令会被作为其默认参数, 而用户也可以在启动容器时通过覆盖 CMD 指令来输入参数; 此外, 这也意味着 ENTRYPOINT 指令的内容不易被用户命令覆盖.

参考

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐