我不知道什么时候应该使用CMD vs RUN。例如,要执行bash/shell命令(即ls -la),我总是使用CMD,或者有一种情况下我会使用RUN?试图理解这两个类似Dockerfile指令的最佳实践。


当前回答

现有的答案涵盖了任何关注这个问题的人所需要的大部分内容。因此,我将只覆盖CMD和RUN的一些利基区域。

CMD:允许复制,但浪费

GingerBeer指出了一个重要的观点:如果你输入多个CMD,你不会得到任何错误——但这样做是浪费的。我想用一个例子来说明:

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

如果将其构建到映像中并在该映像中运行容器,那么正如GingerBeer所述,只会注意到最后一个CMD。因此,该容器的输出将是:

执行CMD 2

我认为它的方式是“CMD”是为正在构建的整个图像设置一个全局变量,因此连续的“CMD”语句只是覆盖之前对该全局变量的任何写入,并且在最终构建的图像中,最后写入的一个胜出。因为Dockerfile是按照从上到下的顺序执行的,所以我们知道最下面的CMD是得到最后一个“写”的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

其他回答

注意:不要混淆RUN和CMD。RUN实际上运行一个命令和 提交结果;CMD在构建时不执行任何东西,但是 指定映像的预期命令。

来自docker文件引用

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

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

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

RUN -安装Python,你的容器现在已经将Python烧录到它的映像中 CMD - python hello.py,运行您最喜欢的脚本

现有的答案涵盖了任何关注这个问题的人所需要的大部分内容。因此,我将只覆盖CMD和RUN的一些利基区域。

CMD:允许复制,但浪费

GingerBeer指出了一个重要的观点:如果你输入多个CMD,你不会得到任何错误——但这样做是浪费的。我想用一个例子来说明:

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

如果将其构建到映像中并在该映像中运行容器,那么正如GingerBeer所述,只会注意到最后一个CMD。因此,该容器的输出将是:

执行CMD 2

我认为它的方式是“CMD”是为正在构建的整个图像设置一个全局变量,因此连续的“CMD”语句只是覆盖之前对该全局变量的任何写入,并且在最终构建的图像中,最后写入的一个胜出。因为Dockerfile是按照从上到下的顺序执行的,所以我们知道最下面的CMD是得到最后一个“写”的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

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

运行- RUN指令允许您安装应用程序和包 这是必需的。它在当前图像上执行任何命令 并通过提交结果创建一个新层。通常你会发现 Dockerfile中的多个RUN指令。

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