【docker知识】联合文件系统(unionFS)原理

news2024/11/26 9:35:48

一、说明

        Docker CLI 操作起来比较简单——您只需掌握Create、Run、InspPull和Push容器和图像,但是谁想过Docker 背后的内部机制是如何工作的?在这个简单的表象背后隐藏着许多很酷的技术, UnionFS(统一文件系统)就是其中之一。

二、UnionFS是所有容器和镜像层背后的底层文件系统

2.1 联合文件系统是个理念

         联合挂载或联合文件系统是一种实现的概念,并不是一种具体的文件系统类型。

        联合挂载是一种文件系统,它是在不修改其原始(物理)源的情况下创建将多个目录的内容合并为一个逻辑目录。更有创意的用法是:能将相关的文件集存储在不同的磁盘或媒体中,但我们在单个视图中显示它们。总之,将多个、种类不同的数据源整合成一个逻辑数据源是有意义的。

        一个例子是来自远程 NFS 服务器的一堆用户的 /home 目录全部合并到一个目录中,另一个例子是:将拆分的 ISO 映像合并到一个完整的目录中。

        而对于Docker这种分层结构,需要将每一层的文件展示到用户观察的一个平面上,换句话就是要用户感觉不出分层的存在。

2.2 当今UnionFS观念下的产品

        在联合文件系统的概念之下,有许多具体产品。其中有的更快一些,有的更简单一些,总之,对于不同的目标或不同的成熟度都有对应的设计理念。因此,在我们开始深入研究细节之前,让我们快速浏览一下目前这些产品发展现状:

  •  UnionFS 

         最原始的可堆叠的统一文件系统,它可以合并多个目录(分支)的内容,同时保持它们的物理内容独立。 Unionfs 用于统一源码树管理、合并拆分光盘内容、合并单独的软件包目录、数据网格等。 Unionfs 允许任意混合只读和读写分支,以及在扇出的任何位置插入和删除分支。为了维护 unix 语义,Unionfs 处理消除重复项、部分错误条件等。 Unionfs 是更大的 FiST 项目的一部分,该项目包括 Wrapfs 包装器可堆叠文件系统。(2014年停止更新)

  • aufs

         原始 UnionFS 的改进版,添加了许多新功能,但被拒绝合并到主线 Linux 内核中。 Aufs 是 Ubuntu/Debian 上 Docker 的默认驱动程序,但已被 OverlayFS 取代(对于 Linux 内核 >4.0)。与 Docker 文档页面中描述的其他联合文件系统相比,它具有一些优势。

  • OverlayFS

        也是UnionFS的改进版,OverlayFS 自 3.18(2014 年 10 月 26 日)起包含在 Linux 内核中。这是 overlay2 Docker 驱动程序默认使用的文件系统(您可以使用 docker system info | grep Storage 进行验证)。它通常比 aufs 具有更好的性能,并且具有一些不错的功能,例如页面缓存共享。

  • ZFS - ZFS

        是由 Sun Microsystems(现为 Oracle)创建的联合文件系统。它具有一些有趣的功能,例如分层校验和、快照的本机处理和备份/复制或本机数据压缩和重复数据删除。但是,由 Oracle 维护,它具有非 OSS 友好许可证 (CDDL),因此不能作为 Linux 内核的一部分发布。但是,您可以使用 ZFS on Linux (ZoL) 项目,该项目在 Docker 文档中被描述为健康且成熟的......,但尚未准备好用于生产。

  • Btrfs

        另一款产品是 Btrfs,它是多家公司(包括 SUSE、WD 或 Facebook)的联合项目,在 GPL 许可下发布,是 Linux 内核的一部分。 Btrfs 是 Fedora 33 的默认文件系统。它还有一些有用的功能,例如块级操作、碎片整理、可写快照等等。如果您真的想经历为 Docker 切换到非默认存储驱动程序的麻烦,那么 Btrfs 及其功能和性能可能是您的不二之选。

        总之:在翻阅Docker资料中,统统将overlay2和UnionFS看成是一个概念,这一点是不会有错的。

三、采用UnionFS的理由

        我们用来启动容器的许多镜像都非常庞大,无论是大小为 72MB 的 ubuntu 还是大小为 133MB 的 nginx。每次我们想从这些图像创建一个容器时,分配那么多空间是非常昂贵的。多亏了联合文件系统,Docker 只需要在镜像之上创建薄层,其余部分可以在所有容器之间共享。这还提供了减少启动时间的额外好处,因为无需复制图像文件和数据。

        联合文件系统还提供隔离,因为容器对共享图像层具有只读访问权限。如果他们需要修改任何只读共享文件,他们会使用写时复制策略(稍后讨论)将内容复制到可以安全修改的顶层可写层。

四、UnionFS是如何工作的

        现在需要问一个重要的问题:UnionFS——它实际上是如何工作的?从上面描述的所有事情来看,整个联合文件系统在云里雾里,但也并非不能模拟。

        下面我们将用shell语句进行模拟这种UnionFS,假定有以下路径和文件:

.
├── upper
│   ├── code.py  # Content: `print("Hello Overlay!")`
│   └── script.py
└── lower
    ├── code.py  # Content: `print("This is some code...")`
    └── config.yaml
  •         在联合挂载术语中,这些目录称为分支。
  •         这些分支中的每一个都被分配了优先级。

        此优先级用于确定在多个源分支中存在同名文件的情况下哪个文件将显示在合并视图中。查看上面的文件和目录 - 很明显,如果我们尝试覆盖它们,就会产生这种冲突(code.py 文件)。

        那么,让我们试着下列shell代码后出现什么:

~ $ mount -t overlay \
    -o lowerdir=./lower,\
       upperdir=./upper,\
       workdir=./workdir \
    overlay /mnt/merged

~ $ ls /mnt/merged
code.py  config.yaml  script.py

~ $ cat /mnt/merged/code.py
print("Hello Overlay!")

        在上面的示例中,我们使用带有覆盖类型的 mount 命令将低级目录(只读;低优先级)和上级目录(读写;高优先级)合并到 /mnt/merged 中的合并视图中。我们还包含了 workdir=./workdir 选项,workdir在 lowerdir 和 upperdir 以原子操作移动到 /mnt/merged 之前用作准备 lowerdir 和 upperdir 的合并视图的地方。

1)读取数据

       查看上面 cat 命令的输出,我们可以看到上层目录中的文件内容确实在合并视图中优先。

        所以,现在我们知道如何合并 2 个目录以及如果存在冲突会发生什么,但是如果我们尝试从合并视图中修改某些文件会发生什么?这就是写时复制 (CoW) 发挥作用的地方。那么,它到底是什么? CoW 是一种优化技术,如果两个调用者请求相同的资源,您可以向他们提供指向相同资源的指针而无需复制它。仅当其中一个调用者尝试写入其“副本”时才需要复制 - 因此术语复制(首次尝试)写入。

2)写入数据

        在联合挂载的情况下,这意味着当我们尝试修改共享文件(或只读文件)时,它首先被复制到顶部可写分支(upperdir),它比只读的较低分支(lowerdir)具有更高的优先级。然后 - 当它在可写分支中时 - 它可以被安全地修改并且它的新内容将在合并视图中可见,因为顶层具有更高的优先级。

3)删除数据

        我们可能要执行的最后一个操作是删除文件。为了执行“删除”,在writeable 分支中创建一个whiteout 文件来清除我们要删除的文件。这意味着该文件实际上并未被删除,而是隐藏在合并视图中。

        我们讨论了很多关于 union mount 的一般工作原理,但它与 Docker 及其容器有何关系?为了将它们重新连接在一起,让我们看一下 Docker 分层架构。容器的沙箱由一些镜像分支——或者我们都知道的——层组成。这些层是合并视图的只读 (lowerdir) 部分,容器层是薄的可写顶部 (upperdir) 部分。

        除了这个架构术语,它实际上是一回事——你从Registry中提取的图像层是 lowerdir,当你运行一个容器时,upperdir 附加到镜像层的顶部,为你的容器提供可写的工作空间。听起来很简单,对吧?那么,让我们试试吧!

五、分层和优先级

  • 按照分层可以将读写成看成优先级最高。lowdir最低优先。
  • 在用户层面,将若干层投影到一个透明画布上,看起来大家都在一个目录展示。

六、在真实Docker中尝试

        为了演示 Docker 如何使用 OverlayFS,我们将尝试模拟 Docker 如何挂载容器和图像层。在动手做之前,我们首先需要清理我们的工作区并获取一个镜像来玩:

~ $ docker image prune -af
...
Total reclaimed space: ...MB
~ $ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a076a628af6f: Pull complete
0732ab25fa22: Pull complete
d7f36f6fe38f: Pull complete
f72584a26f32: Pull complete
7125e4df9063: Pull complete
Digest: sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

        我们有一个图像 (nginx) 可以玩,接下来,让我们检查它的层。我们可以通过对图像运行 docker inspect 并检查 GraphDriver 字段或通过存储所有图像层的 /var/lib/docker/overlay2 目录来检查图像层。所以,让我们两者都做,看看里面有什么:

~ $ cd /var/lib/docker/overlay2
~ $ ls -l
total 0
drwx------. 4 root root     55 Feb  6 19:19 3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd
drwx------. 3 root root     47 Feb  6 19:19 410c05aaa30dd006fc47d8c23ba0d173c6d305e4d93fdc3d9abcad9e78862b46
drwx------. 4 root root     72 Feb  6 19:19 685374e39a6aac7a346963bb51e2fc7b9f5e2bdbb5eac6c76ccdaef807abc25e
brw-------. 1 root root 253, 0 Jan 31 18:15 backingFsBlockDev
drwx------. 4 root root     72 Feb  6 19:19 d487622ece100972afba76fda13f56029dec5ec26ffcf552191f6241e05cab7e
drwx------. 4 root root     72 Feb  6 19:19 fb18be50518ec9b37faf229f254bbb454f7663f1c9c45af9f272829172015505
drwx------. 2 root root    176 Feb  6 19:19 l

~ $ tree 3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd/
3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd/
├── diff
│   └── docker-entrypoint.d
│       └── 20-envsubst-on-templates.sh
├── link
├── lower
└── work

~ $ docker inspect nginx | jq .[0].GraphDriver.Data
{
  "LowerDir": "/var/lib/docker/overlay2/fb18be50518ec9b37faf229f254bbb454f7663f1c9c45af9f272829172015505/diff:
    /var/lib/docker/overlay2/d487622ece100972afba76fda13f56029dec5ec26ffcf552191f6241e05cab7e/diff:
    /var/lib/docker/overlay2/685374e39a6aac7a346963bb51e2fc7b9f5e2bdbb5eac6c76ccdaef807abc25e/diff:
    /var/lib/docker/overlay2/410c05aaa30dd006fc47d8c23ba0d173c6d305e4d93fdc3d9abcad9e78862b46/diff",
  "MergedDir": "/var/lib/docker/overlay2/3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd/merged",
  "UpperDir": "/var/lib/docker/overlay2/3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd/diff",
  "WorkDir": "/var/lib/docker/overlay2/3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd/work"
}

        查看上面的输出,它看起来与我们使用 mount 命令看到的非常相似,对吧?进一步来说:

  • LowerDir:是只读镜像层的目录,用冒号隔开
  • MergedDir:图像和容器中所有层的合并视图
  • UpperDir:写入更改的读写层
  • WorkDir:Linux OverlayFS 用来准备合并视图的工作目录
  • 接下来,让我们更进一步,运行一个容器并检查它的层:
~ $ docker run -d --name container nginx
~ $ docker inspect container | jq .[0].GraphDriver.Data
{
  "LowerDir": "/var/lib/docker/overlay2/59bcd145c580de3bb3b2b9c6102e4d52d0ddd1ed598e742b3a0e13e261ee6eb4-init/diff:
    /var/lib/docker/overlay2/3d963d191b2101b3406348217f4257d7374aa4b4a73b4a6dd4ab0f365d38dfbd/diff:
    /var/lib/docker/overlay2/fb18be50518ec9b37faf229f254bbb454f7663f1c9c45af9f272829172015505/diff:
    /var/lib/docker/overlay2/d487622ece100972afba76fda13f56029dec5ec26ffcf552191f6241e05cab7e/diff:
    /var/lib/docker/overlay2/685374e39a6aac7a346963bb51e2fc7b9f5e2bdbb5eac6c76ccdaef807abc25e/diff:
    /var/lib/docker/overlay2/410c05aaa30dd006fc47d8c23ba0d173c6d305e4d93fdc3d9abcad9e78862b46/diff",
  "MergedDir": "/var/lib/docker/overlay2/59bcd145c580de3bb3b2b9c6102e4d52d0ddd1ed598e742b3a0e13e261ee6eb4/merged",
  "UpperDir": "/var/lib/docker/overlay2/59bcd145c580de3bb3b2b9c6102e4d52d0ddd1ed598e742b3a0e13e261ee6eb4/diff",
  "WorkDir": "/var/lib/docker/overlay2/59bcd145c580de3bb3b2b9c6102e4d52d0ddd1ed598e742b3a0e13e261ee6eb4/work"
}

~ $ tree -l 3 /var/lib/docker/overlay2/59bcd145c580de3bb3b2b9c6102e4d52d0ddd1ed598e742b3a0e13e261ee6eb4/diff  # The UpperDir
/var/lib/docker/overlay2/59bcd145c580de3bb3b2b9c6102e4d52d0ddd1ed598e742b3a0e13e261ee6eb4/diff
├── etc
│   └── nginx
│       └── conf.d
│           └── default.conf
├── run
│   └── nginx.pid
└── var
    └── cache
        └── nginx
            ├── client_temp
            ├── fastcgi_temp
            ├── proxy_temp
            ├── scgi_temp
            └── uwsgi_temp

        这里我们只是从前面的代码片段中获取值并将它们传递给 mount 命令中的适当参数,唯一的区别是我们使用 /mnt/merged 作为合并视图而不是 /var/lib/docker/overlay2/.../合并。

        这就是 Docker 中整个 OverlayFS 的真正含义 - 跨多个堆叠层的单个挂载命令。下面是负责此的 Docker 代码的一部分 - 替换 lowerdir=...,upperdir=...,workdir=... 值,然后是 unix.Mount

// https://github.com/moby/moby/blob/1ef1cc8388165b2b848f9b3f53ec91c87de09f63/daemon/graphdriver/overlay2/overlay.go#L580
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), path.Join(dir, "diff"), path.Join(dir, "work"))
mountData := label.FormatMountLabel(opts, mountLabel)
mount := unix.Mount
mountTarget := mergedDir

rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
// ...

七、结论

        从外面看 Docker 的界面,它可能看起来像一个黑盒子,里面有很多晦涩的技术。这些技术 - 虽然晦涩难懂 - 但非常有趣且有用,虽然您不需要了解它们就可以有效地使用 Docker,但在我看来,学习和理解它们仍然是值得的。

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

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

相关文章

为什么静默安装未经过数字签名的驱动是不可行的?

我想,在 Windows XP 系统上,造成蓝屏的最主要原因是带有 Bug 的设备驱动程序。 请问在座的,谁赞成,谁反对。 因为驱动运行在内核模式,再也没有更高级别的组件对其进行行为监管,它可以做它想做的任何事情。…

Power BI 筛选器函数---Index实例详解

一、Index函数 语法&#xff1a; INDEX ( <检索行号>, [<关系>], [<OrderBy>],[空白],[PartitionBy] ) 含义&#xff1a; 对指定分区(PartitioinBy)中的行&#xff08;关系表&#xff09;&#xff0c;按指定的列进行排序(OrderBy)后&#xff0c;根据&…

医院智能化解决方案-门(急)诊、医技、智能化项目解决方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a;篇幅有限&#xff0c;无法完全展…

Docker镜像的创建

Docker镜像Docker镜像Docker 镜像是一个特殊的文件系统提供容器运行时所需的程序、库、资源、配置等文件包含一些为运行时准备的一些配置参数&#xff08;如匿名卷、环境变量、用户等&#xff09;镜像不包含任何动态数据&#xff0c;其内容在构建之后也不会被改变。Docker镜像的…

论文中常用的注意力模块合集(上)

在深度卷积神经网络中&#xff0c;通过构建一系列的卷积层、非线性层和下采样层使得网络能够从全局感受野上提取图像特征来描述图像&#xff0c;但归根结底只是建模了图像的空间特征信息而没有建模通道之间的特征信息&#xff0c;整个特征图的各区域均被平等对待。在一些复杂度…

【化学试剂】endo-BCN-PEG4-Pomalidomide,(1R,8S,9S)-双环[6.1.0]壬-四聚乙二醇-泊马度胺纯度95%+

一、基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a;CAS号&#xff1a;N/A中文名&#xff1a;(1R,8S,9S)-双环[6.1.0]壬-四聚乙二醇-泊马度胺英文名&#xff1a;endo-BCN-PEG4-Pomalidomide二、详细产品数据&#xff08;Detailed Product Data&#xff09…

【软件测试】软件测试工作上95%会遇到的问题,你遇到多少?

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 1、测试负责人要进行…

C语言rand和srand用法详解

在实际编程中&#xff0c;我们经常需要生成随机数&#xff0c;例如&#xff0c;贪吃蛇游戏中在随机的位置出现食物&#xff0c;扑克牌游戏中随机发牌。在C语言中&#xff0c;我们一般使用 <stdlib.h> 头文件中的 rand() 函数来生成随机数&#xff0c;它的用法为&#xff…

Redis的事务和锁以及在SpringBoot中的使用

文章目录1、事务2、监视锁3、分布式锁1、事务 Redis中事务的操作主要有三个&#xff1a; # 1、开启事务 # 定事务的开启位置&#xff0c;此指令执行后&#xff0c;后续的所有指令均加入到事务中 1、multi # 2、执行事务 # 设定事务的结束位置&#xff0c;同时执行事务。与mul…

【java】map集合遍历: entrySet()、单独遍历key或者value、 keySet()、iterator遍历

目录 1.增强for循环。利用Map 的 entrySet&#xff08;&#xff09;方法获取元素2.增强for循环。单独遍历key或者value3.增强for循环。利用Map 的 keySet() 方法获取元素4.使用iterator遍历文中实体的定义在上篇笔记中&#xff1a;https://blog.csdn.net/qq_43622777/article/d…

spring面试题 一

一、为了降低Java开发的复杂性&#xff0c;Spring采取了那4种关键策略 基于POJO的轻量级和最小侵入性编程&#xff1b; 通过依赖注入和面向接口实现松耦合&#xff1b; 基于切面和惯例进行声明式编程&#xff1b; 通过切面和模板减少样板式代码。 二、Spring框架的核心&am…

14条生产环境项目踩坑复盘

缓存需要等事务结束之后再删除&#xff0c;避免旧数据导致数据库和缓存不一致。说明&#xff1a;比如线程A在减少账户的余额(11->10)&#xff0c;执行了数据库更新&#xff0c;但是事务还未提交&#xff0c;但把缓存删除了。线程B从缓存里去获取账户的余额&#xff0c;缓存里…

Python Numpy基础教程

本文是一个关于Python numpy的基础学习教程&#xff0c;其中&#xff0c;Python版本为Python 3.x 什么是Numpy Numpy Numerical Python&#xff0c;它是Python中科学计算的核心库&#xff0c;可以高效的处理多维数组的计算。并且&#xff0c;因为它的许多底层函数是用C语言编…

TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518

TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518 TC358775XBG芯片的主要功能是DSI到LVDS桥&#xff0c;通过DSI链路实现视频流输出&#xff0c;以驱动LVDS兼容的显示面板。该芯片支持单链路LVDS高达1366768 24位像素分辨率&#xff0c;双链路L…

了解webpack

文章目录一、webpack是什么&#xff1f;二、为什么要使用webpack三、webpack的五个核心概念四、安装webpack提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、webpack是什么&#xff1f; 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程…

PyTorch学习笔记:nn.MSELoss——MSE损失

PyTorch学习笔记&#xff1a;nn.MSELoss——MSE损失 torch.nn.MSELoss(size_average None&#xff0c;reduce None&#xff0c;reduction mean)功能&#xff1a;创建一个平方误差(MSE)损失函数&#xff0c;又称为L2损失&#xff1a; l(x,y)L{l1,…,lN}T,ln(xn−yn)2l(x,y)L…

设计模式:建造者模式教你创建复杂对象

一、问题场景 当我们需要创建资源池配置对象的时候&#xff0c;资源池配置类里面有以下成员变量: 如果我们使用new关键字调用构造函数&#xff0c;构造函数参数列表就会太长。 如果我们使用set方法设置字段值&#xff0c;那minIdle<maxIdle<maxTotal的约束逻辑就没地方…

【计组】内存和总线

课程链接&#xff1a;深入浅出计算机组成原理_组成原理_计算机基础-极客时间 一、虚拟内存和内存保护 日常使用的操作系统下&#xff0c;程序不能直接访问物理内存。内存需要被分成固定大小的页&#xff08;Page&#xff09;&#xff0c;再通过虚拟内存地址&#xff08;Virtu…

如何创建一个有效的FAQ/常见问题解答页面?

如果您的网站上没有常见问题解答页面&#xff0c;那么您将错过本可以节省的数小时的销售和支持时间&#xff0c;以及本可以推动购买的新客户。尽管与登录页面或销售页面不同&#xff0c;常见问题解答页面显然不会显示直接的投资回报&#xff0c;但它可以在其他因素上节省您的团…

内网渗透(十五)之内网信息收集-域环境中定位域管理员

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…