如今,您可以从各种Docker支持的存储驱动程序中进行选择,以确保它真正符合您的实际环境和用例——但是,除非您对镜像层有深入的了解(更不用说镜像和容器本身了),否则一般用户根本不会考虑这方面。很明显,这些简单且没有吸引力的技术元素层虽然对镜像至关重要,但通常不会引起太多关注——因为闪亮的新工具通常比基本信息更引人注目。在今天的文章中,我们将探讨图像大小和构建时间的主题——这两者都已成为用户的紧迫目标。让我们首先关注图像和层,并解释它们的概念:Docker图像是一组由只读层和一些元数据组成的标记结构。每一层都有自己的UUID,每个连续的层都建立在它下面的层之上。每个Dockerfile指令都会生成一个新层。看起来基本概念很简单,不需要过多解释,但是我遇到过这样一个令人困惑的Dockerfile:FROMcentos:7.1.1503RUNyum-yinstalljava-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64RUNyumcleanall这个Dockerfile有什么问题?好吧,第二个RUN命令并没有真正影响图像的大小——尽管它看起来确实应该减小图像的大小。让我们重新审视一下Docker镜像和层的定义,强调语法表达:Docker镜像是由只读层和一些元数据组成的一组标记结构。每一层都有自己的UUID,每个连续的层都建立在它下面的层之上。每个Dockerfile指令都会生成一个新层。现在,可以清楚地看到Dockerfile没有优化图像大小。不管怎样,让我们??深入了解一下这些yum缓存是如何从镜像层中移除的。不过,为了保证文章的通俗易懂,我们先将关注点限定在AUFS存储驱动上。AUFS存储驱动程序可以添加一个dredge文件来覆盖映像底部只读层中文件的存在,从而将其从该层中删除。除此之外,可以推断图像的大小等于层体积的总和,并且向Dockerfile添加的每条额外指令都会进一步增加图像的大小。只要将两条RUN指令结合起来,我们就可以轻松修复上面提到的Dockfile:FROMcentos:7.1.1503RUNyum-yinstalljava-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1。x86_64&&\yumcleanall让我们构建并检查这两组图像以证明它们的瘦身效果。您需要执行dockerbuild-t在Dockerfile包含的目录中构建一组镜像。之后,我们会发现两组镜像的体积是不一样的:层:$dockerhistory--no-truncseparate-layersIMAGECREATEDCREATEDBYSIZEb605eff36c7b418aa30e315dc0a1d809d08a1ebb8e574e934b11f5ad7cd490dc2minutesago/bin/sh-cyumcleanall2.277MB4c363330dc9057ce6285be496aa02212759ecfc75c4ad3a9a74e6e2f3dacb1dd2minutesago/bin/sh-cyum-yinstalljava-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64257.5MB173339447b7ec3e8cb93edc61f3815ff754ec66cfadf48f1953ab3ead6a754c58weeksago/bin/sh-c#(nop)CMD["/bin/bash"]0B4e1d113aa16e0631795a4b31c150921e35bd1a3d4193b22c3909e29e6f7c718d8weeksago/bin/sh-c#(nop)ADDfile:d68b6041059c394e0f95effd6517765405402b4302fe16cf864f658ba8b25a97in/212.1MBa2c33fe967de5a01f3bfc3861add604115be0d82bd5192d29fc3ba97beedb8317monthsago/bin/sh-c#(nop)MAINTAINERTheCentOSProject-ami_creator0B$dockerhistory--no-trunccombined-layersIMAGECREATEDCREATEDBYSIZEdefa7a199555834ac5c906cf347eece7fa33eb8e90b30dfad5f9ab1380988ade48secondsago/bin/sh-cyum-yinstalljava-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64&&yumcleanall195MB173339447b7ec3e8cb93edc61f3815ff754ec66cfadf48f1953ab3ead6a754c58weeksago/bin/sh-c#(nop)CMD["/bin/bash"]0B4e1d113aa16e0631795a4b31c150921e35bd1a3d4193b22c3909e29e6f7c718d8weeksago/bin/sh-c#(nop)ADDfile:d68b6041059c394e0f95effd6517765405402b4302fe16cf864f658ba8b25a97in/212.1MBa2c33fe967de5a01f3bfc3861add604115be0d82bd5192d29fc3ba97beedb8317monthsago/bin/sh-c#(nop)MAINTAINERTheCentOSProject-ami_creator0B这清楚地表明链式命令能帮助我们在对各层commit之前清理,但这并不意味着我们应该把所有东西都塞进一个层如果你重新检查上面命令的输出,你会发现两个镜像的3***层具有相同的UUID,这意味着两组图像共享这些层(这就是为什么dockerimages命令能够重新移植每个图像的虚拟卷)。可能有人会说,现在磁盘空间的使用成本这么低,我们真的需要那么执着于所谓的镜像卷吗?但是除了缓存图片层之外,还有其他方面需要注意。最重要的一点是图像的构建时间。简单地说,如果你可以重用一个层,你就不需要重建它。另外,如果你的图片需要通过网络传输(比如在构建和推广过程中分阶段进行),那么更紧凑的图片尺寸和缓存层的使用可以显着节省传输时间(并减少网络流量负载),因为实际有相当一部分传输镜像层已经集成到新版本镜像中。现在结论很明显了,应该尽量减少Dockerfile顶层的指令条数,以提高缓存的复用率,尽量着眼于底层修改Dockerfile。考虑到以上因素,有些朋友可能会认为最优化的方案应该是将所有变化的元素塞进不同的层,从而清理每一层的执行过程;但总的来说,这种做法并没有简化工作量。首先,Docker对层数有限制,就是不能超过127,大家要和上限保持一定的距离。层数过多的Dockerfile,既不利于后期维护,也无法排除不需要的数据;相反,结果是我们面对的是一个非常臃肿的镜像,最好将它拆分成多个不同的镜像。而且更重要的是,层的实现并不是免费的,根据我们使用的存储驱动不同,由此带来的额外负担也是不同的。例如,在AUFS中,对于镜像层堆栈中每个现有文件的最后一次写入,每一层都会导致容器写入性能延迟,特别是如果文件很大并且存在于大量镜像层下的情况。因此,在文末,我们需要再次强调:为了有效提升镜像大小优化效果,缩短构建时间,你必须明白自己要优化什么,并有意识地做出必要的妥协。1.如果您需要了解或深入了解容器中的层概念,请点击此处查看Docker文档。2.查看存储驱动程序文档以了解有关其性能的详细信息。3.如果您发现自己在处理写入密集型工作负载,请考虑使用数据卷(它们绕过存储驱动程序)。原标题:针对镜像大小和构建时间优化Docker镜像
