在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.2
join它们:
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
添加了一些东西,我们需要考虑几点:
-
图层应该只是添加比前一个更高的差异,所以如果后面的层不删除以前的一个添加的东西,两个方法之间应该没有太多的磁盘空间节省的优势…
-
从Docker Hub中并行拉取图层,所以
Dockerfile.1
虽然可能稍微大一些,但理论上可以更快下载。 -
如果添加第四个句子(即
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 COPY
, ADD
和RUN
语句添加一个新的图层到您的图像。 使用这些语句时要小心。 尝试将命令合并到单个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/
回答你的观点:
-
是的,图层有点像差异。 如果没有绝对的变化,我认为不会添加图层。 问题是,一旦你安装/下载第二层的东西,你不能删除它#3层。 所以一旦在一个图层中写入东西,图像的大小就不能再减less了。
-
尽pipe图层可以并行拖动,但可能会更快,但每个图层无疑会增加图像的大小,即使它们正在移除文件。
-
是的,如果你正在更新你的docker文件,caching是有用的。 但它在一个方向上起作用。 如果你有10层,而你改变层#6,你仍然需要重build从#6-#10层的所有东西。 所以并不经常会加快构build过程,但是保证不必要地增加图像的大小。
感谢@Mohan提醒我更新这个答案。
看来上面的答案已经过时了。 文档说明:
在Docker 17.05之前,甚至在Docker 1.10之前,重要的是尽量减less图像中的图层数量。 以下改进缓解了这种需求:
[…]
Docker 17.05及更高版本增加了对多阶段构build的支持,允许您只将需要的构件复制到最终的图像中。 这允许您在中间构build阶段包含工具和debugging信息,而不增加最终图像的大小。
和
注意这个例子还使用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议是删除不是很有用,只有当它发生在与添加/安装行动相同的图层。