docker镜像体积优化攻略参考—— 筑梦之路

news2024/11/16 13:22:57

简单介绍

镜像的本质是镜像层和运行配置文件组成的压缩包,构建镜像是通过运行 Dockerfile 中的 RUN 、COPY 和 ADD 等指令生成镜像层和配置文件的过程。

和镜像体积大小有关的关键点:

  • RUNCOPY 和 ADD 指令会在已有镜像层的基础上创建一个新的镜像层,执行指令产生的所有文件系统变更会在指令结束后作为一个镜像层整体提交。

  • • 镜像层具有 copy-on-write 的特性,如果去更新其他镜像层中已存在的文件,会先将其复制到新的镜像层中再修改,造成双倍的文件空间占用

  • • 如果去删除其他镜像层的一个文件,只会在当前镜像层生成一个该文件的删除标记,并不会减少整个镜像的实际体积

验证上面的结论过程如下:

cat Dockerfile

FROM alpine:latest
COPY resource.tar /
RUN touch /resource.tar
RUN rm -f /resource.tar
ENTRYPOINT ["/bin/ash"]

# 构建镜像,并查看层

$ docker build -t test-image -f Dockerfile .
$ docker history test-image:latest
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
95f1695b2904   About a minute ago   /bin/sh -c #(nop)  ENTRYPOINT ["/bin/ash"]      0B
1780448c656f   About a minute ago   /bin/sh -c rm -f /resource.tar                  0B
a85d29bf7738   About a minute ago   /bin/sh -c touch /resource.tar                  135MB
6dac335fa653   4 minutes ago        /bin/sh -c #(nop) COPY file:66065d6e23e0bc52…   135MB
e66264b98777   7 weeks ago          /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      7 weeks ago          /bin/sh -c #(nop) ADD file:8e81116368669ed3d…   5.53MB

在 docker history 的输出结果中可以看到:

  • RUN touch /resource.tar 指令只是修改了文件的元信息,但依然将整个文件拷贝到了新的镜像层中。

  •  RUN rm -f /resource.tar 指令虽然删除了文件,并且该文件在运行容器时不可见,但依然在前两个镜像层中以及最终的镜像中存在。

常用分析工具

1. docker history

docker 自带的 docker history 命令,该命令可以展示所有镜像层的创建时间、指令以及体积等较为基础的信息,但对于复杂的镜像则有些乏力。

2. dive

第三方的 dive 工具,该工具可以分析镜像层组成,并列出每个镜像层所包含的文件列表,可以很方便地定位到影响镜像体积的构建指令以及具体文件 

 

如何优化

1. 分阶段构建与从零构建

分阶段构建(multi-stage builds)和从零构建(build from scratch)是优化镜像体积的基本手段和必备技巧。该技巧将镜像构建过程区分为构建和运行环境,在构建环境安装编译器等依赖并编译所需的二进制包,然后将其复制到仅包含必要运行依赖的运行环境中。

对 golang 这类能够编译静态二进制文件的语言来说分阶段构建的效果尤为明显,我们可以将编译产生的二进制文件放到 scratch 镜像中运行(scratch 是一个特殊的空镜像)

FROM golang
COPY hell0.go .
ENV CGO_ENABLED=0
RUN go build hello.go

FROM scratch
COPY --from=0 /go/hello .
CMD ["./hello"]

如果直接使用 golang 镜像作为运行环境,其镜像体积通常接近 1 个 G,其中大部分文件都不是在运行容器时所必要的。将编译结果拷贝到运行环境后,体积只有几十 kb~mb 不等,如果需要在运行容器中保留基本的系统工具,可以考虑使用 alpine 镜像作为运行环境。

关于分阶段构建和从零构建的更多细节可参考 Docker 官方文档中的 Use multi-stage builds 和 Create a simple parent image using scratch。

2. 避免产生无用的文档或缓存

 

docker 镜像不应该包含文档、缓存等对运行容器没有作用的内容。

  1. 避免在本地保留安装缓存。大部分包管理器会在安装时缓存下载的资源以备之后使用,以 pip 为例,会将下载的响应和构建的中间文件保存在 ~/.cache/pip 目录,应使用 --no-cache-dir 选项禁用默认的缓存行为。

  2.  避免安装文档。部分包管理器提供了选项可以不安装附带的文档,如 dnf 可使用 --nodocs 选项。

  3.  避免缓存包索引。部分包管理器在执行安装之前,会尝试查询所有已启用仓库的包列表、版本等元信息缓存在本地作为索引。个别仓库的索引缓存可达到 150 M 以上。我们应该仅在安装包时查询索引,并在安装完成后清理,不应该在单独的指令中执行 yum makecache 这类缓存索引的命令

3. 及时清理不需要的文件

运行容器时不需要的文件,一定要在创建的同一层清理,否则依然会保留在最终的镜像中。

通过包管理安装包,通常会产生大量的缓存文件,一定要在同一 RUN 指令的结尾处立刻清理。在安装依赖数量较多时,可以节省大量的缓存空间。

# dnf
RUN dnf install -y --nodocs <PACKAGES> \
  && dnf clean all \
  && rm -rf /var/cache/dnf

# apt
RUN apt-get update \
  && apt-get install -y <PACKAGES> \
  && rm -rf /var/lib/apt/lists/*
# 官方的 ubuntu/debian 镜像 apt-get 会在安装后自动执行 clean 命令

3.  合并多个镜像层

应该避免在不同镜像层中更新文件而造成额外的体积占用。当构建的层数很多且执行指令较复杂时,很难避免在不同的镜像层中更新文件,可通过以下手段精简这部分额外体积

1) 在最终生成镜像时将所有镜像层合并成一层,在 docker build 命令中使用 —squash 即可实现(需要开启 docker daemon 的实验性功能)

docker build -t squash-image --squash -f Dockerfile .

最终生成的镜像只有一个镜像层,包含最后实际存在的文件系统,在合并所有镜像层的过程中,相当于禁用了 copy-on-write 特性。这种做法的坏处在于,镜像在保存和分发时是可以复用镜像层的,推送镜像时会跳过镜像仓库已存在的镜像层,拉取镜像时会跳过本地已拉取过的镜像层,而合并成一层后则失去了这种优势 

2) 分阶段构建,将部分中间镜像层压缩成一层作为基础镜像。 在开发团队内部,我们往往会在官方镜像的基础上添加或更新部分依赖,然后作为团队内部统一使用的基础镜像,这种复用方式可以大大减少实际占用的镜像体积。更进一步,我们可以将这类基础镜像压缩成一层

FROM golang:1.16 as base

FROM scratch
COPY --from=base / /
ENTRYPOINT ["/bin/bash"]

压缩成一层后,golang:1.16 的镜像体积从 919MB 变成 913MB,官方镜像已经做了很多优化所以节省空间十分有限,但对于开发团队内部制作的基础镜像,这种优化往往会带来意外惊喜

4. 复制文件的同时修改元信息

先将文件添加到镜像内,然后再修改文件的执行权限和所属用户,这类 COPY-RUN 指令在 Dockerfile 中十分常见:

COPY output/hello /usr/bin/hello
RUN chmod +x /usr/bin/hello && chown normal:normal /usr/bin/hello

 但修改文件元信息也会将文件复制到新的镜像层,以上指令会产生两份相同的文件。在文件体积较大时,会显著增加整个镜像的体积。事实上,我们可以在复制文件的同时完成对文件元信息的修改,COPY 和 ADD 指令都提供了修改元信息的 --chmod 和 --chown 选项:

COPY --chmod=755 --chown=normal:normal output/hello /usr/bin/hello

--chmod 特性目前还未添加到官方文档,使用前需要开启 docker 的 buildkit 特性(在 docker build 命令前添加 DOCKER_BUILDKIT=1 即可),目前只支持 --chmod=755 和 --chmod=0755 这种设置方法,不支持 --chmod=+x

注:经测试,当使用 ADD 指令且源文件为下载链接时 --chmod 选项不起作用,不清楚这是 docker 的 bug 还是 feature。解决方案是直接使用 RUN 指令 wget + chmod 来替代 ADD

本文搜集来自镜像体积从1000M到10M,几招就做到,仅供参考。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1717182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马es集群

1、为什么要做es集群 单机的elasticsearch做数据存储&#xff0c;必然面临两个问题:海量数据存储问题、单点故障问题 海量数据存储问题:将索引库从逻辑上拆分为N个分片(shard)&#xff0c;存储到多个节点 单点故障问题:将分片数据在不同节点备份(replica) 2、搭建es集群 1、用…

【技术实操】银河高级服务器操作系统实例分享,数据库日志文件属主不对问题分析

1. 问题现象描述 2023 年 06 月 30 日在迁移数据库过程中&#xff0c;遇到数据库 crash 的缺陷&#xff0c;原因如下&#xff1a;在数据库启动时候生成的一组临时文件中&#xff0c;有 owner 为 root 的文件&#xff0c; 文件权限默认为 640&#xff0c; 当数据库需要使用的时…

重生奇迹mu魔剑职业技能一览

1、火剑袭&#xff1a;魔剑士三转前4个技能中&#xff0c;核心技能火剑袭为buff技能&#xff0c;可在短时间内大幅提升自身伤害输出能力&#xff0c;由于提升的数值依赖于技能等级&#xff0c;因此一个高技能等级魔剑士的爆发能力相当恐怖。 2、天雷闪&#xff1a;是剩余技能中…

Sapphire开发日志(四)

任务描述 本周任务&#xff0c;将模型进行封装&#xff0c;实现模型推理的基本API&#xff0c;从而可以根据输入数据输出蒙版数据。 模型输入数据处理 首先进行模型输入数据的预处理。首先支持点击和框选两种模式&#xff0c;所以这里需要对这两种模式进行适配。 modelData…

微信小程序蓝牙连接部分Android14调用wx.setBLEMTU协商低功耗最大传输单元失败解决方案(部分安卓14设置超过23就会报错)

1.解决方案的核心内容&#xff1a;第一次设置失败不要管&#xff0c;在complate函数里面继续往下连接&#xff0c;然后设置一个定时器每1秒钟在重新设置一次&#xff0c;肯定会成功的&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&am…

使用compile_commands.json配置includePath环境,解决vscode中引入头文件处有波浪线的问题

通过编译时生成的 compile_commands.json 文件自动完成对 vscode 中头文件路径的配置&#xff0c;实现 vscode 中的代码的自动跳转。完成头文件路径配置后&#xff0c;可以避免代码头部导入头文件部分出现波浪线&#xff0c;警告说无法正确找到头文件。 步骤 需要在 vscode 中…

Java Sort 方法的使用(包含Arrays.sort(),Collections.sort()以及Comparable,Comparator的使用 )

目录 Comparable && Comparator的使用&#xff1a; Comparable: Comparator: Arrays.sort()的使用: 升序排序&#xff1a; 降序排序&#xff1a; 自定义排序方法&#xff1a; 在日常的刷题或开发中&#xff0c;很多时候我们需要对数据进行排序&#xff0c;以达到我…

【PHP项目实战训练】——laravel框架的实战项目中可以做模板的增删查改功能(1)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

【银河麒麟V10服务器OS-系统根分区扩容】指导教程手册

【银河麒麟V10服务器OS-系统根分区扩容】指导教程手册 环境信息&#xff1a;VMware虚拟软件16.0 首先查看KylinOS服务器版本&#xff1a;nkvers 备注&#xff1a; (Tercel) 版本是 V10 SP1 版本&#xff0c; (Sword) 版本是 V10 SP2 版本&#xff0c; (Lance) 版本是 V10 …

开源与闭源 AI 模型:发展路径的比较与前瞻

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

super().__init__() 来自于哪个PEP

super() 的简化用法&#xff08;即不带参数的 super()&#xff09;是在 Python 3 中引入的。这个改进是由 PEP 3135 规范化的。 PEP 3135: New Super PEP 3135 标题为 “New Super”。它介绍了一种新的方式来调用超类的方法&#xff0c;即不再需要显式地传递当前类和实例&…

前端Vue小兔鲜儿电商项目实战Day04

一、二级分类 - 整体认识和路由配置 1. 配置二级路由 ①准备组件模板 - src/views/SubCategory/index.vue <script setup></script><template><div class"container"><!-- 面包屑 --><div class"bread-container">…

软件需求规格说明书(Word原件@配套软件全资料)

软件需求规格说明书编写规范编写规范 1.项目背景 2.项目目标 3.系统架构 4.总体流程 5.名称解释 6.功能模块 软件项目相关全套精华资料包获取方式①&#xff1a;点我获取 获取方式②&#xff1a;本文末个人名片直接获取。 软件资料清单列表部分文档清单&#xff1a;工作安排任…

python操作mongodb底层封装并验证pymongo是否应该关闭连接

一、pymongo简介 https://pymongo.readthedocs.io/en/stable/github地址&#xff1a;https://github.com/mongodb/mongo-python-driver mymongo安装命令&#xff1a;pip install pymongo4.7.2 mymongo接口文档&#xff1a;PyMongo 4.7.2 Documentation PyMongo发行版包含Py…

AI绘画Stable Diffusion【实战】一下给我整破防了!!SD制作商业海报——香水海报

大家好&#xff0c;我是画画的小强 今天给大家分享关于SD制作商业海报案例——香水海报。内容很干&#xff0c;但很实用&#xff0c;后续也会持续分享一些其他商业海报的制作案例&#xff0c;喜欢的朋友请持续关注 人“狠”话不多&#xff0c;大家请上车&#xff01;&#xff…

VB.net 进行CAD二次开发(二)

利用参考文献2&#xff0c;添加面板 执行treeControl New UCTreeView()时报一个错误&#xff1a; 用户代码未处理 System.ArgumentException HResult-2147024809 Message控件不支持透明的背景色。 SourceSystem.Windows.Forms StackTrace: 在 System.Windows…

Java流与链表:探索java.util.stream与LinkedList的交汇点

在现代Java开发中&#xff0c;流&#xff08;Streams&#xff09;和链表&#xff08;LinkedList&#xff09;都是强大且常用的数据处理工具。java.util.stream提供了高效的方式来处理数据流&#xff0c;而LinkedList则是java.util包中的经典集合实现。本文将探索它们的交汇点&a…

手拉手springboot整合kafka发送消息

环境介绍技术栈springbootmybatis-plusmysqlrocketmq软件版本mysql8IDEAIntelliJ IDEA 2022.2.1JDK17Spring Boot3.1.7kafka2.13-3.7.0 创建topic时&#xff0c;若不指定topic的分区(Partition主题分区数)数量使&#xff0c;则默认为1个分区(partition) springboot加入依赖kafk…

MySQL排序字段无法唯一标识一条数据,导致分页查询结果出现重复或遗漏问题

问题描述 MySQL排序字段无法唯一标识一条数据&#xff0c;且排序字段可能有多个&#xff0c;多个排序字段无法唯一标识一条数据&#xff0c;即排序字段标识的相同数据过多&#xff0c;可能导致分页查询出现重复或遗漏的情况。 具体说明&#xff1a;      如果在排序条件中…

Stable Diffusion AI绘画:从创意词汇到艺术图画的魔法之旅

文章目录 一、Stable Diffusion的工作原理二、从提示词到模型出图的过程三、Stable Diffusion在艺术创作中的应用《Stable Diffusion AI绘画从提示词到模型出图》内容简介作者简介楚天 目录前言/序言本书特色特别提示 获取方式 在科技的飞速发展中&#xff0c;Stable Diffusion…