Dockerfile 中 RUN 和 CMD 的区别

Posted

技术标签:

【中文标题】Dockerfile 中 RUN 和 CMD 的区别【英文标题】:Difference between RUN and CMD in a Dockerfile 【发布时间】:2016-09-24 12:25:35 【问题描述】:

我对何时应该使用 CMDRUN 感到困惑。例如,要执行 bash/shell 命令(即ls -la),我将始终使用CMD,或者是否存在使用RUN 的情况?试图了解这两个相似的Dockerfile 指令的最佳实践。

【问题讨论】:

docs.microsoft.com/en-us/virtualization/windowscontainers/… 【参考方案1】:

RUN 是一个镜像构建步骤,RUN 命令之后的容器状态将提交给容器镜像。一个 Dockerfile 可以有许多 RUN 步骤,这些步骤相互叠加以构建映像。

CMD 是启动构建镜像时容器默认执行的命令。一个 Dockerfile 只会使用最终定义的CMD。使用docker run $image $other_command 启动容器时,可以覆盖CMD

ENTRYPOINT也与CMD密切相关,可以修改容器启动镜像的方式。

【讨论】:

你做了所有RUN需要设置你的环境,并且你的(唯一的)CMD启动在你的容器中运行的进程,例如,对于nginx,从github.com/nginxinc/docker-nginx/blob/…中提取你看到@行987654332@ “一个 Dockerfile 只能有一个 CMD”——从技术上讲并不正确,但实际上除了一个之外的所有命令都将被忽略。查看 GingerBeer 的答案。 “一个 Dockerfile 将只使用最终定义的 CMD”?实际上,最终定义的 CMD 将用于启动镜像作为容器,对吧? 是的@paulcheung dockerfile 中的最终命令被写入镜像,并且是容器在启动构建镜像时默认执行的命令。 "一个 Dockerfile 将只使用最终定义的 CMD。" ——我只是浪费了过去的一个小时,因为我没有意识到这一点。如果他们要忽略这些,为什么他们至少不会给你一个警告?【参考方案2】:

RUN - 在我们构建 docker 镜像时触发命令。

CMD - 在我们启动创建的 docker 镜像时触发命令。

【讨论】:

【参考方案3】:

我发现this 的文章对理解它们之间的区别很有帮助:

运行 - RUN 指令允许您安装应用程序和软件包 需要它。它在当前图像之上执行任何命令 并通过提交结果创建一个新层。很多时候你会发现 Dockerfile 中有多个 RUN 指令。

CMD - CMD 指令允许您设置默认命令,该命令将是 仅当您在不指定命令的情况下运行容器时执行。 如果 Docker 容器使用命令运行,则默认命令为 忽略。如果 Dockerfile 有多个 CMD 指令,除了最后一个 CMD 指令被忽略。

【讨论】:

那个链接太棒了!【参考方案4】:

RUN - 安装 Python,你的容器现在已经将 python 烧入了它的镜像 CMD - python hello.py,运行你喜欢的脚本

【讨论】:

CMD - 安装 Python,我的容器现在没有将 python 烧入它的镜像吗? RUN会创建python的镜像层,CMD只会执行命令不创建镜像【参考方案5】:

现有答案涵盖了查看此问题的任何人所需的大部分内容。所以我只会介绍 CMD 和 RUN 的一些小众领域。

CMD:允许重复但很浪费

GingerBeer 提出了一个重要的观点:如果您输入多个 CMD,则不会出现任何错误 - 但这样做很浪费。我想用一个例子来详细说明:

FROM busybox
CMD echo "Executing CMD"
CMD echo "Executing CMD 2"

如果你将它构建到一个镜像中并在这个镜像中运行一个容器,那么正如 GingerBeer 所说,只有最后一个 CMD 会被注意。所以该容器的输出将是:

执行 CMD 2

我的想法是“CMD”正在为正在构建的整个图像设置一个全局变量,因此连续的“CMD”语句只会覆盖之前对该全局变量的任何写入,并在最终图像中那是最后一个写胜利的人。由于 Dockerfile 是按从上到下的顺序执行的,我们知道最底层的 CMD 是获得这个最终“写入”的命令(比喻地说)。

RUN:如果图像被缓存,命令可能不会执行

关于 RUN 需要注意的一个微妙点是,即使存在副作用,它也会被视为纯函数,因此会被缓存。这意味着如果 RUN 有一些不会改变结果图像的副作用,并且该图像已经被缓存,则 RUN 将不会再次执行,因此副作用不会在后续构建中发生。例如,以这个 Dockerfile 为例:

FROM busybox
RUN echo "Just echo while you work"

第一次运行时,你会得到这样的输出,带有不同的字母数字 ID:

docker build -t example/run-echo .
Sending build context to Docker daemon  9.216kB
Step 1/2 : FROM busybox
 ---> be5888e67be6
Step 2/2 : RUN echo "Just echo while you work"
 ---> Running in ed37d558c505
Just echo while you work
Removing intermediate container ed37d558c505
 ---> 6f46f7a393d8
Successfully built 6f46f7a393d8
Successfully tagged example/run-echo:latest

请注意,上面执行了 echo 语句。第二次运行它,它使用缓存,你不会在构建的输出中看到任何回显:

docker build -t example/run-echo .
Sending build context to Docker daemon  9.216kB
Step 1/2 : FROM busybox
 ---> be5888e67be6
Step 2/2 : RUN echo "Just echo while you work"
 ---> Using cache
 ---> 6f46f7a393d8
Successfully built 6f46f7a393d8
Successfully tagged example/run-echo:latest

【讨论】:

【参考方案6】:

注意:不要将 RUN 与 CMD 混淆。 RUN 实际上运行一个命令并且 提交结果; CMD 在构建时不执行任何操作,但是 指定图像的预期命令。

来自 docker 文件参考

https://docs.docker.com/engine/reference/builder/#cmd

【讨论】:

【参考方案7】:

运行命令: RUN 命令基本上会在我们构建映像时执行默认命令。它还将为下一步提交图像更改。

可以有多个 RUN 命令,以帮助构建新映像。

CMD 命令: CMD 命令只会为新容器设置默认命令。这不会在构建时执行。

如果一个 docker 文件有超过 1 个 CMD 命令,那么除了最后一个之外,所有的命令都会被忽略。因为这个命令不会执行任何东西,只是设置默认命令。

【讨论】:

【参考方案8】:

RUN:可以是多个,用于build过程,例如安装多个库

CMD:只能有 1 个,这是您的执行起点(例如["npm", "start"]["node", "app.js"]

【讨论】:

【参考方案9】:

RUNCMD 上已有足够的答案。我只想在 ENTRYPOINT 上补充几句。 CMD 参数可以被命令行参数覆盖,而 ENTRYPOINT 参数总是被使用。

This article 是一个很好的信息来源。

【讨论】:

那个链接太棒了!

以上是关于Dockerfile 中 RUN 和 CMD 的区别的主要内容,如果未能解决你的问题,请参考以下文章

Dockerfile的CMD总结

Dockerfile中CMD命令和ENTRYPOINT 命令的说明

使用 docker run 命令将参数传递给 Dockerfile 中的 CMD

CMD Dockerfile 中的转义命令

4,dockerfile的基本使用

Dockerfile里指定执行命令用ENTRYPOING和用CMD有何不同?