Docker镜像优化指南:如何有效地压缩镜像体积并缩短构建时间?

简介:

时至今日,大家已经能够从多种Docker支持的存储驱动程序中做出选择,从而确保其与我们的实际环境以及用例切实吻合——然而,除非深入理解镜像层(更不用提镜像与容器本身),否则一般用户根本不会考虑这方面问题。很明显,这些简单而且缺乏吸引力的技术元素层虽然是构成镜像的基本条件,但却往往得不到高度关注——因为闪亮的新型工具往往比基本信息更能抓人眼球。

在今天的文章中,我们将探讨镜像体积及构建时间方面的话题——而这两项工作也已经成为用户们迫切需要实现的目标。

让我们首先着眼于镜像与层,对其概念加以阐述:

Docker镜像是一套由只读层外加部分元数据构成的标签化结构。

每个层都拥有自己的UUID,而且每个连续层都建立在其下的层之上。

每个Dockerfile指令都会生成一个新层。

看起来基本理念非常简单,且不需要再做过多解释,不过我曾经遇上过这样一个让人如坠雾里的Dockerfile:

FROM centos:7.1.1503

RUN yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64

RUN yum clean all

这个Dockerfile到底出了什么问题?这个嘛,其中第二个RUN命令并没能真正影响到镜像体积——虽然它看起来确实应该有效削减镜像大小。让我们重新审视Docker镜像与层的定义,并着重强调其中的语法表达:

Docker镜像是一套由只读层外加部分元数据构成的标签化结构。

每个层都拥有自己的UUID,而且每个连续层都建立在其下的层之上。

每个Dockerfile指令都会生成一个新层。

现在,可以明确看到该Dockerfile并没能优化镜像体积。无论如何,让我们进行深入探讨,看看这些yum缓存是如何被从镜像层当中移除出去的。

不过为了保证文章浅显易懂的特性,我们首先将关注范畴限定在AUFS存储驱动程序身上。AUFS存储驱动程序能够添加一个疏排文件以覆盖镜像中底部只读层内文件的存在,从而将其从层内删除出去。除此之外,大家可以推断出镜像的大小相当于各层体积的总和,而添加到Dockerfile中的每条附加指令都会进一步增加镜像的体积。

只要将两项RUN指令加以合并,我们就能轻松对上面提到的Dockfile进行修复:

FROM centos:7.1.1503

RUN yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64 &&

yum clean all

下面让我们构建并检查这两套镜像以证明其瘦身效果。大家需要执行docker build -t 以在Dockerfile所容纳的目录当中构建一套镜像。在此之后,我们会发现两套镜像的体积有所不同:

$ docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE

combined-layers latest defa7a199555 4 seconds ago 407 MB

separate-layers latest b605eff36c7b About a minute ago 471.8 MB

大家应该能够轻松判断哪套镜像是通过哪个Dockerfile构建而成,但让我们进一步查看两套镜像各自包含的层:

$ docker history --no-trunc separate-layers

IMAGE CREATED CREATED BY SIZE

b605eff36c7b418aa30e315dc0a1d809d08a1ebb8e574e934b11f5ad7cd490dc 2 minutes ago /bin/sh -c yum clean all 2.277 MB

4c363330dc9057ce6285be496aa02212759ecfc75c4ad3a9a74e6e2f3dacb1dd 2 minutes ago /bin/sh -c yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64 257.5 MB

173339447b7ec3e8cb93edc61f3815ff754ec66cfadf48f1953ab3ead6a754c5 8 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B

4e1d113aa16e0631795a4b31c150921e35bd1a3d4193b22c3909e29e6f7c718d 8 weeks ago /bin/sh -c #(nop) ADD file:d68b6041059c394e0f95effd6517765405402b4302fe16cf864f658ba8b25a97 in / 212.1 MB

a2c33fe967de5a01f3bfc3861add604115be0d82bd5192d29fc3ba97beedb831 7 months ago /bin/sh -c #(nop) MAINTAINER The CentOS Project - ami_creator 0 B

$ docker history --no-trunc combined-layers

IMAGE CREATED CREATED BY SIZE

defa7a199555834ac5c906cf347eece7fa33eb8e90b30dfad5f9ab1380988ade 48 seconds ago /bin/sh -c yum -y install java-1.8.0-openjdk-devel-1:1.8.0.65-2.b17.el7_1.x86_64 && yum clean all 195 MB

173339447b7ec3e8cb93edc61f3815ff754ec66cfadf48f1953ab3ead6a754c5 8 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B

4e1d113aa16e0631795a4b31c150921e35bd1a3d4193b22c3909e29e6f7c718d 8 weeks ago /bin/sh -c #(nop) ADD file:d68b6041059c394e0f95effd6517765405402b4302fe16cf864f658ba8b25a97 in / 212.1 MB

a2c33fe967de5a01f3bfc3861add604115be0d82bd5192d29fc3ba97beedb831 7 months ago /bin/sh -c #(nop) MAINTAINER The CentOS Project - ami_creator 0 B

这清楚地表明链式命令能帮助我们在对各层进行提交前进行清理工作,不过这并不意味着我们应当将一切都塞进单一层当中。如果大家重新审视以上命令的输出结果,就会发现两套镜像的3个最底层拥有着同样的UUID,这意味着两套镜像共享这些层(有鉴于此,docker images命令才能够报告各镜像的虚拟体积)。

有人可能会说,如今磁盘空间的使用成本已经如此低廉,我们真的有必要如此纠结于所谓镜像体积吗?但除了缓存镜像层之外,大家还有其它方面需要关注。其中最重要的一点在于镜像的构建时间。简单来讲,如果大家能够复用某个层,则无需对其进行重新构建。另外,如果大家的镜像需要通过网络进行传输(例如在流程中分阶段进行构建推进),那么更为袖珍的镜像体积与缓存层运用能够显著节约传输时长(并降低网络流量负载),因为需要实际传输的镜像层中有相当一部分已经被整合在新版本镜像当中。

现在结论已经显而易见,大家应当尽可能减少Dockerfile顶层的指令数量,从而提高缓存复用比例并努力着眼于底层对Dockerfile进行变更。

考虑到以上各项因素,有些朋友可以认为优化程度最高的解决方案应当是将全部发生变更的元素塞进各自不同的层当中,从而清理每个层的执行流程; 但一般来讲,这种作法并不会简化工作负担。首先,Docker对层数做出了限制,即不可超过127个,而且大家最好与上限之间保持一定距离。包含有大量层的Dockerfile既不易于后期维护,也不太可能排除那些不必要的数据; 相反,其结果是我们会面对一套非常臃肿的镜像,且最好将其拆分成多个不同镜像。而更重要的是,层的实现并非毫无成本,具体取决于我们使用的存储驱动程序,由此造成的额外负担也有所区别。举例来说,在AUFS当中,每个层都会在面向镜像层堆栈中各现有文件的首次写入时给容器写入性能造成延迟,特别在文件体积庞大且存在于大量镜像层之下的情况当中。

因此在文章最后,我们需要再次强调:要想切实提升镜像体积优化效果并压缩构建时间,大家必须了解自己想要优化什么,并有意识地做出必要妥协。

1. 如果大家需要了解或者深入掌握容器中的层概念,请点击此处查看Docker说明文档。

2. 查看存储驱动程序说明文档以了解与其性能表现相关的各项细节信息。

3. 如果大家发现自己需要处理高强度写入工作负载,可以考虑使用数据分卷(它们会绕开存储驱动程序)。


本文转自d1net(转载)

相关文章
|
28天前
|
运维 Kubernetes Docker
利用Docker和Kubernetes构建微服务架构
利用Docker和Kubernetes构建微服务架构
|
11天前
|
Docker 容器
将本地的应用程序打包成Docker镜像
将本地的应用程序打包成Docker镜像
|
11天前
|
数据库 Docker 容器
Docker在现代软件开发中扮演着重要角色,通过Dockerfile自动化构建Docker镜像,实现高效、可重复的构建过程。
Docker在现代软件开发中扮演着重要角色,通过Dockerfile自动化构建Docker镜像,实现高效、可重复的构建过程。Dockerfile定义了构建镜像所需的所有指令,包括基础镜像选择、软件安装、文件复制等,极大提高了开发和部署的灵活性与一致性。掌握Dockerfile的编写,对于提升软件开发效率和环境管理具有重要意义。
28 9
|
11天前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
41 7
|
1月前
|
缓存 Linux 网络安全
docker的镜像无法下载如何解决?
【10月更文挑战第31天】docker的镜像无法下载如何解决?
1287 29
|
11天前
|
存储 缓存 运维
Docker镜像采用分层存储,每层代表镜像的一部分,如基础组件或应用依赖,多层叠加构成完整镜像
Docker镜像采用分层存储,每层代表镜像的一部分,如基础组件或应用依赖,多层叠加构成完整镜像。此机制减少存储占用,提高构建和传输效率。Docker还通过缓存机制提升构建和运行效率,减少重复工作。文章深入解析了Docker镜像分层存储与缓存机制,包括具体实现、管理优化及实际应用案例,帮助读者全面理解其优势与挑战。
29 4
|
27天前
|
缓存 5G 开发者
【提效】docker镜像构建优化-提速10倍
本文主要记录了自己通过查阅相关资料,一步步排查问题,最后通过优化Docerfile文件将docker镜像构建从十几分钟降低到1分钟左右,效率提高了10倍左右。
|
23天前
|
机器学习/深度学习 数据采集 Docker
Docker容器化实战:构建并部署一个简单的Web应用
Docker容器化实战:构建并部署一个简单的Web应用
|
28天前
|
存储 关系型数据库 Linux
【赵渝强老师】什么是Docker的镜像
Docker镜像是一个只读模板,包含应用程序及其运行所需的依赖环境。镜像采用分层文件系统,每次修改都会以读写层形式添加到原只读模板上。内核bootfs用于加载Linux内核,根镜像相当于操作系统,上方为应用层。镜像在物理存储上是一系列文件的集合,默认存储路径为“/var/lib/docker”。
|
1月前
|
存储 监控 Linux
docker构建镜像详解!!!
本文回顾了Docker的基本命令和管理技巧,包括容器和镜像的增删改查操作,容器的生命周期管理,以及如何通过端口映射和数据卷实现容器与宿主机之间的网络通信和数据持久化。文章还详细介绍了如何使用Docker部署一个简单的Web应用,并通过数据卷映射实现配置文件和日志的管理。最后,文章总结了如何制作自定义镜像,包括Nginx、Python3和CentOS镜像,以及如何制作私有云盘镜像。
129 2