Dockerfile 中,把多个 RUN 合并在一起,能减少镜像尺寸吗?

先说结论:
有些时候能,有些时候不能,但你要明白原理 – Docker 使用 UnionFS,镜像尺寸随着层数增多,是单调非减的。

问题

看到一个 Dockerfile:

FROM python:3.17.7-alpine3.20
RUN pip3 install pillow
RUN pip3 install django
RUN pip3 install jieba
RUN pip3 install nltk
RUN pip3 install colormap

有人建议,把这几个 pip3 install 合并成一个 pip3 install -r requirements.txt,可以减小最终打包出来的镜像尺寸,真得是这样吗?

实验一:一次 pip 安装 vs 多次 pip 安装

多次 pip 安装

我们把上面这个最初始的 Dockerfile 打包出来的镜像起名为 temp:multi。
通过 docker image ls temp:multi 看到,这个包的大小为 396MB
然后,通过 docker history temp:multi 看到这个包的层级如下:

IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
a934e19243c7   2 hours ago   RUN /bin/sh -c pip3 install colormap   -i ht…   177MB     buildkit.dockerfile.v0
<missing>      2 hours ago   RUN /bin/sh -c pip3 install nltk       -i ht…   22.8MB    buildkit.dockerfile.v0
<missing>      2 hours ago   RUN /bin/sh -c pip3 install django     -i ht…   39.1MB    buildkit.dockerfile.v0
<missing>      2 hours ago   RUN /bin/sh -c pip3 install jieba      -i ht…   83.5MB    buildkit.dockerfile.v0
<missing>      2 hours ago   RUN /bin/sh -c pip3 install pillow     -i ht…   27.1MB    buildkit.dockerfile.v0
<missing>      2 weeks ago   CMD ["python3"]                                 0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   RUN /bin/sh -c set -eux;  for src in idle3 p…   36B       buildkit.dockerfile.v0
<missing>      2 weeks ago   RUN /bin/sh -c set -eux;   apk add --no-cach…   38.1MB    buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV PYTHON_VERSION=3.12.7                       0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV GPG_KEY=7169605F62C751356D054A26A821E680…   0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   RUN /bin/sh -c set -eux;  apk add --no-cache…   999kB     buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV LANG=C.UTF-8                                0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV PATH=/usr/local/bin:/usr/local/sbin:/usr…   0B        buildkit.dockerfile.v0
<missing>      5 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      5 weeks ago   /bin/sh -c #(nop) ADD file:5758b97d8301c84a2…   7.8MB

可以看到,pip 安装的这些包,总大小应该在 350MB 左右,加上 python-alpine 原来46MB 的大小,整好是在 396 MB

一次 pip 安装

创建 requirements.txt 文件,用于 pip 集中安装:

-i https://pypi.tuna.tsinghua.edu.cn/simple
pillow
django
jieba
nltk
colormap

修改 Dockerfile

FROM python:3.12.7-alpine3.20
COPY requirements.txt /root/
RUN pip install -r /root/requirements.txt

使用 docker build 打包镜像 temp:one。 可以看到,temp:one 的大注也是 396 MB。
使用 docerk history temp:one 查看:

IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
a802510b2faa   2 hours ago   RUN /bin/sh -c pip3 install -r /home/require…   349MB     buildkit.dockerfile.v0
<missing>      2 hours ago   COPY requirements.txt /home/ # buildkit         79B       buildkit.dockerfile.v0
<missing>      2 weeks ago   CMD ["python3"]                                 0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   RUN /bin/sh -c set -eux;  for src in idle3 p…   36B       buildkit.dockerfile.v0
<missing>      2 weeks ago   RUN /bin/sh -c set -eux;   apk add --no-cach…   38.1MB    buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV PYTHON_VERSION=3.12.7                       0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV GPG_KEY=7169605F62C751356D054A26A821E680…   0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   RUN /bin/sh -c set -eux;  apk add --no-cache…   999kB     buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV LANG=C.UTF-8                                0B        buildkit.dockerfile.v0
<missing>      2 weeks ago   ENV PATH=/usr/local/bin:/usr/local/sbin:/usr…   0B        buildkit.dockerfile.v0
<missing>      5 weeks ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      5 weeks ago   /bin/sh -c #(nop) ADD file:5758b97d8301c84a2…   7.8MB

可以看到,把所有 pip 安装一个 requirements 里安装,实际上并没有减小镜像包的尺寸

使用 pip --no-cache-dir 实际能减少大小

在一次性 pip 安装的基础上,给 Dockerfile 的 RUN pip 命令加上 --no-cache-dir 的参数

FROM python:3.12.7-alpine3.20
COPY requirements.txt /root/
RUN pip install -r /root/requirements.txt

添加这个参数之后,最后生成的镜像大小为 300MB。

实验二:添加一个文件,然后删除

再看这个 Dockerfile

FROM alpine
COPY bigfile /home
RUN rm -f /home/bigfile

这里,我们基于 alpine 镜像,先往里面拷贝了一个 6.4 MB 的大文件,然后又把它给删除了。相当于什么都没做。
最理想的结果,是打包出来的镜像(起名为 temp:add_remove),大小和 alpine 差不多,也应该是 7.8MB 左右的样子。
但实际结果不是这样。
通过 docker image ls temp:add_remove 可以看到,镜像大小为 14.4MB
而用 docker history temp:add_remove 看,结果如下:

IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
81de752d816c   55 minutes ago   RUN /bin/sh -c rm -f /home/bigfile # buildkit   0B        buildkit.dockerfile.v0
<missing>      56 minutes ago   COPY bigfile /home/ # buildkit                  6.62MB    buildkit.dockerfile.v0
<missing>      47 hours ago     RUN /bin/sh -c adduser -D dot # buildkit        3.03kB    buildkit.dockerfile.v0
<missing>      5 weeks ago      /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      5 weeks ago      /bin/sh -c #(nop) ADD file:5758b97d8301c84a2…   7.8MB

可以看到,原来 COPY 的内容实际仍然在打包的镜像里面。为什么删除没有效果呢?

原因是 Docker 的 Union FS

Docker 使用 Union FS 来管理文件系统。
它允许将多个目录挂载到同一个挂载点上,这些目录在挂载点处表现为一个连贯的文件系统。
在 Docker 的上下文中,这意味着可以创建含多个只读层的堆叠,并在顶部添加一个可写层。
Union FS支持层叠多个目录,其中每个目录都可以被视为一个独立的层。
这允许 Docker 镜像由多个只读层组成,每个层代表一个 Dockerfile 指令的结果。

Docker 早期使用的是 AUFS,后来改为使用 overlay2。目前的 Linux 内核,决大多数都支持 oerverlay2。

Overlay2 的一个持点就是:写时复制(Copy-on-Write)
当容器尝试修改一个文件时,overlay2 会检查该文件是否存在于下面的只读层中。
如果是,overlay2 会在可写层创建该文件的副本并进行修改,保持原始只读层不变。

所以,每次 RUN 操作,实际上就是对原始记录加了一层。
如果两个动作如果没有重叠,就像用多个 pip install 不同的包,产生的多层和合在一个 pip 安装产生的一层的大小差不多。
但是,对于先添加,又删除,添加的那层文件始终是在的,只是之后又被删除动作在新的一层上标记为删除。

相当于一个本子上先写了一笔,然后又划掉了(而不是用橡皮擦掉),并不能使本子恢复空白。

最根本的原因,是 overlay2,除了最上层的读写层之外,底下的每一层都是只读的。

打个比方 – 就像钢笔写字,不能擦除

每一次 RUN 操作执行完成后,就生成一层。这层生成之后,就是只读的,不可修改的。
对于下一个操作,以前 RUN 的结果生成的各层,就象是一份份由钢笔书写的文件。
就算是我想修改以前的内容,也只能是做一个标记(就像用钢笔把以前某个文件上的某句话划掉),然后在最新的文件上加上要修改的内容。
总而言之,每新做一个操作,会添加一层。总体的镜像尺寸随着层数增多,是单调非减的。

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

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

相关文章

Unity3D 同步怪物实体到客户端详解

在游戏开发中&#xff0c;经常会遇到需要同步怪物实体到客户端的需求。本文将详细介绍如何在Unity3D中实现怪物实体的同步&#xff0c;并给出技术详解以及代码实现。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;大家可以点击进来一起交流一下开发经验呀&#xff…

Request2:Post请求和Json

百度翻译拿到自己想看的数据&#xff0c;下图查看请求到数据的请求 preview提前看下 取出对应的RequestUrl &#xff0c;看出来要使用的话得用post请求 #!/usr/bin/env python # -*- coding:utf-8 -*- import requests import json if __name__ "__main__":#1.指定…

SAP物料凭证报表字段调整

业务场景&#xff1a; 报表MB51的输入和输出字段调整&#xff1a; 输入&#xff08;选择界面&#xff09; 输出界面 可以看到在这是没有布局调整的 后台路径&#xff1a; SPRO-物料管理-库存管理和实际库存-报表-定义物料凭证列表的字段选择 事务码&#xff1a;SM30-V_MMI…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,1-10

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

FPGA学习(6)-基础语法参数化设计阻塞与非阻塞

目录 1.两种参数化不改变源文件&#xff0c;只改仿真文件的值 2.参数化设计实现模块的重用 2.1不用参数化方法 2.1.1源文件 2.1.2仿真文件 2.1.3仿真波形及实验 2.2 用参数方法 2.2.1调用之前写的led灯闪烁模块&#xff0c;在本源函数中&#xff0c;例化4次调用之前的模…

ip a查看网卡接口信息

ip a命令是用于查看和管理网络接口信息的命令。通过执行ip a命令&#xff0c;可以查看当前系统上所有网络接口的配置信息&#xff0c;包括IP地址、子网掩码、网关、MAC地址等。该命令还可以用于配置网络接口的参数&#xff0c;如设置IP地址、启用或禁用接口等操作。 # ip a 1:…

史上最烂 spring transaction 原理分析

史上最烂 spring transaction 原理分析 事务定义、事务作用、事务特性、生命周期、数据库事务三种运行模式、数据库事务控制、并发事务问题、隔离级别、数据库事务实现原理、spring 事务传播行为、spring 事务核心组件、spring boot 事务相关组件、事务嵌套原理、编程式事务与声…

51单片机的万年历【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块时钟模块按键蜂鸣器等模块构成。适用于电子万年历、数字时钟万年历等相似项目。 可实现功能: 1、LCD1602实时显示年月日星期和北京时间&#xff0c;具备闰年判断功能 2、按键可设置闹钟时间 3、按键可修改当前时…

MAL-PEG-SVA MW2000 丙烯酰氯基 共轭反应 靶向传递 共价结合

MAL-PEG-SVA是一种带有丙烯酰氯基和马来酰亚胺的聚乙二醇衍生物。它具有良好的溶解性、生物相容性和化学稳定性&#xff0c;因此在许多领域都有广泛的应用。 以下是MAL-PEG-SVA的一些主要应用和相应的例子&#xff1a; 1. 蛋白质修饰&#xff1a;MAL-PEG-SVA可用于蛋白质的修饰…

CVTE Android面试题及参考答案(100道题)

目录 插件化 组件化 合并相似接口 抽象通用方法 使用接口代理 引入设计模式 编写源代码 资源文件准备 编译资源文件 编译源代码 生成 dex 文件 打包 APK 文件 技术能力提升 项目经验积累 职业发展 知识分享与团队协作 建立良好的沟通机制 明确团队目标和职责…

深度学习代码学习笔记2

1、torch.max correct 0 total 0 for xb,yb in valid_dl:outputs model(xb)_,predicted torch.max(outputs.data,1)total yb.size(0) #yb.size(0) 返回的是张量 yb 在第 0 维的大小&#xff0c;也就是 yb 中的样本数量。correct (predicted yb).sum().item() print(…

[Halcon矩阵] 通过手眼标定矩阵计算相机旋转角度

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

C++服务端的配置文件库介绍

文章目录 1. inih 库原理使用方法 2. Boost.PropertyTree 库原理使用方法 3. jsoncpp 库原理使用方法 在 C 项目中&#xff0c;灵活地读取用户配置是提升软件可用性的重要部分。本文将介绍几种常见的 C 配置库&#xff0c;包括它们的原理和使用方法。 1. inih 库 原理 inih …

桥接模式、NAT模式 和 主机模式(Host-Only)区别

在虚拟化和网络配置中&#xff0c;桥接模式、NAT模式 和 主机模式&#xff08;Host-Only&#xff09;是虚拟机常用的网络连接模式。它们各自的网络特性和使用场景不同。下面详细分析它们的区别和适用场景。 1. 桥接模式 (Bridged Mode) 原理&#xff1a; 虚拟机的网卡会直接与…

06.队列介绍+实现

目录 一、队列的概念 二、队列的实现 1、头文件定义 2、功能函数实现 3、主函数测试 一、队列的概念 队列就像吃饭排队类似&#xff0c;先来先吃&#xff0c;先进先出。 队头&#xff1a;队列的头部。 队尾&#xff1a;队列的尾部。 入队&#xff1a;在队尾操作。 出队&…

根据输入的速度梯度张量A和涡量w计算得到李太克斯(Liutex)相关的量,包括r(可能是一个向量)、R和Omega

这段代码是用一种可能类似于 MATLAB 的语言编写的函数&#xff0c;其主要功能是根据输入的速度梯度张量A和涡量w计算得到李太克斯&#xff08;Liutex&#xff09;相关的量&#xff0c;包括r&#xff08;可能是一个向量&#xff09;、R和Omega。 具体步骤如下&#xff1a; 首先&…

SpringBoot实现的物流优化策略

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理智能物流管理系统的相关信息成为必然。开发…

STM32—BKP备份寄存器RTC实时时钟

1.BKP简介 BKP(Backup Registers)备份寄存器BKP可用于存储用户应用程序数据。当VDD&#xff08;2.0~3.6V&#xff09;电源被切断&#xff0c;他们仍然由VBAT(1.8~3.6V)维持供电。当系统在待机模式下被唤醒&#xff0c;或系统复位或电源复位时&#xff0c;他们也不会被复位TAMP…

如果用Java设计MySQL中表级锁、行级锁和间歇锁会是怎么的?

在 MySQL 中&#xff0c;锁机制是确保数据一致性和并发控制的重要手段。MySQL 支持多种锁类型&#xff0c;包括表级锁、行级锁等&#xff0c;每种锁的适用场景、影响范围和实现机制各不相同。我们将逐一介绍它们&#xff0c;并通过模拟代码展示不同锁的实现。 1. 锁类型及其影…

【GAMES101笔记速查——Lecture 16 Ray Tracing4】

上节课的内容&#xff1a;辐射度量学、光线传播、反射方程、渲染方程、全局光照、概率论复习 这节课要介绍一种真实的渲染方法-蒙特卡洛路径追踪 目录 1 简单回顾 1.1 渲染方程&#xff08;The Rendering Equation&#xff09; 1.2 概率 2 蒙特卡洛积分&#xff08;Monte…