在Dockerfile中多个RUN与单链运行,哪个更好?

Dockerfile.1执行多个RUN

 FROM busybox RUN echo This is the A > a RUN echo This is the B > b RUN echo This is the C > c 

Dockerfile.2join它们:

 FROM busybox RUN echo This is the A > a &&\ echo This is the B > b &&\ echo This is the C > c 

每个RUN创build一个图层,所以我总是认为更less的图层更好,因此Dockerfile.2更好。

当一个RUN删除之前RUN某个东西(例如yum install nano && yum clean all )时,这显然是正确的,但是如果每个RUN添加了一些东西,我们需要考虑几点:

  1. 图层应该只是添加比前一个更高的差异,所以如果后面的层不删除以前的一个添加的东西,两个方法之间应该没有太多的磁盘空间节省的优势…

  2. 从Docker Hub中并行拉取图层,所以Dockerfile.1虽然可能稍微大一些,但理论上可以更快下载。

  3. 如果添加第四个句子(即echo This is the D > d )并且在本地重新构build, Dockerfile.1会因为caching而更快地构build,但是Dockerfile.2将不得不再次运行所有4个命令。

所以,问题是: 哪个是更好的方式来做一个Dockerfile?

我个人根据自己在其他图像中重复使用的潜力和预期的caching使用情况来分层。 如果我有4张图片,都有相同的基本图片(例如debian),我可以将大部分这些图片的一些常用工具集合到第一个运行命令中,以便其他图像从caching中受益。

接下来,我将着眼于任何非常罕见的组件,可能只有当基础镜像更新并将其放在Dockerfile中时才会更新。 在Dockerfile的末尾,我包含了所有可以快速运行并且可能会频繁更改的命令,例如,添加具有主机特定UID的用户或创build文件夹并更改权限。 如果容器包含正在积极开发的解释代码(例如JavaScript),则会尽可能晚地添加,以便重build只执行单一更改。

在这些变化的每一组中,我尽可能地整合以最小化层次。 所以,如果有4个不同的源代码文件夹,那么这些文件夹被放置在一个文件夹中,因此可以添加一个命令。 从apt-get之类的任何软件包安装都会被合并到一个单独的RUN中,以尽可能减less软件包pipe理器的开销(更新和清理)。

正式答案列在他们的最佳实践(官方图像必须坚持这些)

最小化层数

您需要findDockerfile的可读性(以及长期可维护性)与最小化其使用的层数之间的平衡。 对您使用的图层数量要保持战略性和谨慎。

由于docker1.10 COPYADDRUN语句添加一个新的图层到您的图像。 使用这些语句时要小心。 尝试将命令合并到单个RUN语句中。 只有在需要可读性的情况下才能分开。

更多信息: https : //docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/minimize-the-number-of-layers

更新:docker多阶段> 17.05

使用多阶段构build,您可以在Dockerfile中使用多个FROM语句。 每个FROM语句都是一个阶段,可以有自己的基础图像。 在最后阶段,您使用像阿尔派这样的最基本的图像,复制之前阶段的构build工件并安装运行时需求。 这个阶段的最终结果是你的形象。 所以这就是你如前所述担心图层的地方。

像往常一样,docker在多阶段构build方面有很好的文档 。 这是一个快速摘录:

使用多阶段构build,您可以在Dockerfile中使用多个FROM语句。 每个FROM指令可以使用不同的基础,并且每个指令都开始构build的新阶段。 您可以select性地将文物从一个阶段复制到另一个阶段,在最终图像中留下您不需要的任何内容。

一个伟大的博客文章可以在这里find: https : //blog.alexellis.io/mutli-stage-docker-builds/

回答你的观点:

  1. 是的,图层有点像差异。 如果没有绝对的变化,我认为不会添加图层。 问题是,一旦你安装/下载第二层的东西,你不能删除它#3层。 所以一旦在一个图层中写入东西,图像的大小就不能再减less了。

  2. 尽pipe图层可以并行拖动,但可能会更快,但每个图层无疑会增加图像的大小,即使它们正在移除文件。

  3. 是的,如果你正在更新你的docker文件,caching是有用的。 但它在一个方向上起作用。 如果你有10层,而你改变层#6,你仍然需要重build从#6-#10层的所有东西。 所以并不经常会加快构build过程,但是保证不必要地增加图像的大小。


感谢@Mohan提醒我更新这个答案。

看来上面的答案已经过时了。 文档说明:

在Docker 17.05之前,甚至在Docker 1.10之前,重要的是尽量减less图像中的图层数量。 以下改进缓解了这种需求:

[…]

Docker 17.05及更高版本增加了对多阶段构build的支持,允许您只将需要的构件复制到最终的图像中。 这允许您在中间构build阶段包含工具和debugging信息,而不增加最终图像的大小。

https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#minimize-the-number-of-layers

注意这个例子还使用Bash &&操作符人为地压缩两个RUN命令,以避免在图像中创build一个额外的图层。 这很容易失败,很难维护。

https://docs.docker.com/engine/userguide/eng-image/multistage-build/

最佳实践似乎已经改为使用多级构build并保持Dockerfile的可读性!

这取决于你包括在你的图像层。

关键是尽可能多的分享层次:

不好的例子:

Dockerfile.1

 RUN yum install big-package && yum install package1 

Dockerfile.2

 RUN yum install big-package && yum install package2 

好例子:

Dockerfile.1

 RUN yum install big-package RUN yum install package1 

Dockerfile.2

 RUN yum install big-package RUN yum install package2 

另一个build议是删除不是很有用,只有当它发生在与添加/安装行动相同的图层。