容器原理之Union FS

一、前言

1.1 什么是 UnionFS

联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。

联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。

Docker 中使用的 AUFS(AnotherUnionFS)就是一种联合文件系统。 AUFS 支持为每一个成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限, 同时 AUFS 里有一个类似分层的概念, 对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分的)。

Docker 目前支持的联合文件系统包括:

 OverlayFSAUFSBtrfsVFSZFS 、 Device Mapper

各 Linux 发行版 Docker 推荐使用的存储驱动如下表。

Linux 发行版Docker 推荐使用的存储驱动:

Docker CE on Ubuntuaufs, 

devicemapper, 

overlay2 (Ubuntu 14.04.4 +, 16.04 +), 

overlay, 

zfs, 

vfsDocker CE on Debianaufs, 

devicemapper, 

overlay2 (Debian Stretch), overlay, 

vfsDocker CE on CentOSdevicemapper, 

vfsDocker CE on Fedoradevicemapper, 

overlay2 (Fedora 26 +), 

overlay (实验性支持)

vfs在可能的情况下,推荐使用 overlay2 存储驱动,overlay2 是目前 Docker 默认的存储驱动,以前则是 AUFS。你可以通过配置来使用以上提到的其他类型的存储驱动。

1.2 Docker镜像中对UnionFS的使用背景

任何程序运行时都会有依赖,无论是开发语言层的依赖库,还是各种系统lib、操作系统等,不同的系统上这些库可能是不一样的,或者有缺失的。为了让容器运行时一致,docker将依赖的操作系统、各种lib依赖整合打包在一起(即镜像),然后容器启动时,作为它的根目录(根文件系统rootfs),使得容器进程的各种依赖调用都在这个根目录里,这样就做到了环境的一致性。

不过,这时你可能已经发现了另一个问题:难道每开发一个应用,都要重复制作一次rootfs吗(那每次pull/push一个系统岂不疯掉)?

比如,我现在用Debian操作系统的ISO做了一个rootfs,然后又在里面安装了Golang环境,用来部署我的应用A。那么,我的另一个同事在发布他的Golang应用B时,希望能够直接使用我安装过Golang环境的rootfs,而不是重复这个流程,那么本文的主角UnionFS就派上用场了。

Docker镜像的设计中,引入了层(layer)的概念,也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量rootfs(一个目录),这样应用A和应用B所在的容器共同引用相同的Debian操作系统层或是Unbuntu系统层等、Golang环境层(作为只读层),而各自有各自应用程序层,和可写层。启动容器的时候通过UnionFS把相关的层挂载到一个目录,作为容器的根文件系统。

需要注意的是,rootfs只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。这就意味着,如果你的应用程序需要配置内核参数、加载额外的内核模块,以及跟内核进行直接的交互,你就需要注意了:这些操作和依赖的对象,都是宿主机操作系统的内核,它对于该机器上的所有容器来说是一个“全局变量”,牵一发而动全身。

分层的收益总结:

  • 分层最大的一个好处就是共享资源
  • 有多个镜像都从相同的base镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像;
  • 同时内存中也只需加载一份base镜像,就可以为所有容器服务了,而且镜像的每一层都可以被共享。

1.3 存储驱动

Docker 使用了一系列不同的存储驱动管理镜像内的文件系统并运行容器,这些存储驱动与 Docker 卷(volume)有些不同,存储引擎管理着能够在多个容器之间共享的存储。

想要理解 Docker 使用的存储驱动,我们首先需要理解 Docker 是如何构建并且存储镜像的,也需要明白 Docker 的镜像是如何被每一个容器所使用的;Docker 中的每一个镜像都是由一系列只读的层组成的,Dockerfile 中的每一个命令都会在已有的只读层上创建一个新的层:

FROM ubuntu:15.04

COPY . /app

RUN make /app

CMD python /app/app.py

容器中的每一层都只对当前容器进行了非常小的修改,上述的 Dockerfile 文件会构建一个拥有四层 layer 的镜像:

当镜像被 docker run 命令创建时就会在镜像的最上层添加g zhi一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。

容器和镜像的区别就在于,所有的镜像都是只读的,而每一个容器其实等于镜像加上一个可读写的层,也就是同一个镜像可以对应多个容器。

1.4 AUFS简述

AUFS又叫Another UnionFS,后来叫Alternative UnionFS。AUFS是Docker选用的第一种存储驱动。AUFS具有快速启动容器,高效利用存储和内存的优点,直到现在AUFS仍然是Docker支持的一种存储驱动类型。AUFS可以看成是 UnionFS 的升级版,它完全重写了早期的UnionFS 1.x,其主要目的是为了可靠性和性能,并且引入了一些新的功能,比如可写分支的负载均衡。AUFS在使用上全兼容UnionFS,而且比之前的UnionFS在稳定性和性能上都要好很多,后来的UnionFS 2.x开始抄AUFS中的功能。虽然AUFS没有进到Linux主干里,但是在很多Linux发行版都用了AUFS,比如:Ubuntu 10.04,Debian6.0, Gentoo Live CD支持AUFS。

AUFS 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 AUFS 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount):

每一个镜像层或者容器层都是 /var/lib/docker/ 目录下的一个子文件夹;在 Docker 中,所有镜像层和容器层的内容都存储在 /var/lib/docker/aufs/diff/ 目录中:

$ ls /var/lib/docker/aufs/diff/

00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c

93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8

00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3dbc0e74db9ca691c-init

93604f232a831b22aeb372d5b11af8c8779feb96590a6dc36a80140e38e764d8-init

019a8283e2ff6fca8d0a07884c78b41662979f848190f0658813bb6a9a464a90

93b06191602b7934fafc984fbacae02911b579769d0debd89cf2a032e7f35cfa

而 /var/lib/docker/aufs/layers/ 中存储着镜像层的元数据,每一个文件都保存着镜像层的元数据,最后的 /var/lib/docker/aufs/mnt/ 包含镜像或者容器层的挂载点,最终会被 Docker 通过联合的方式进行组装。

上面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。

1.5 其他存储驱动

AUFS 只是 Docker 使用的存储驱动的一种,除了 AUFS 之外,Docker 还支持了不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和 vfs 等等,在最新的 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动,但是在没有 overlay2 驱动的机器上仍然会使用 aufs 作为 Docker 的默认驱动。

不同的存储驱动在存储镜像和容器文件时也有着完全不同的实现,有兴趣的读者可以在 Docker 的官方文档 Select a storage driver 中找到相应的内容。

想要查看当前系统的 Docker 上使用了哪种存储驱动只需要使用以下的命令就能得到相对应的信息:

$ docker info | grep Storage

Storage Driver: aufs

1.5.1 以 OverlayFS 为例

OverlayFS 也是一种与 AUFS 类似的联合文件系统,同样属于文件级的存储驱动,包含了最初的 Overlay 和更新更稳定的 overlay2。Overlay 只有两层:upper 层和 Lower 层。Lower 层代表镜像层,upper 层代表容器可写层。

相当于可以将多个目录mount成为一个合并的目录,在合并的时候有多个源,需要指定哪个源是下层,哪个源是上层。合并之后就会做merge。

  • 如果一个文件在下层,那么合并之后是可以看到的。
  • 如果一个文件在上层,合并之后还是可以看的到的。
  • 如果文件即来自上层和下层,上层文件会将下层文件覆盖掉。

二. Docker 如何使用 UnionFS?

容器镜像由多个镜像层组成,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /text,上层的 /text 会覆盖下层的 /text,也就是说用户只能访问到上层中的文件 /text。

将中间只读的 rootfs 的集合称为 Docker 镜像,Docker 镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。UnionFS 使得镜像的复用、定制变得更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

当用docker run启动这个容器时,实际上在镜像的顶部添加了一个新的可写层。这个可写层也叫容器层。

Docker 镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层!容器启动后,其内的应用所有对容器的改动,文件的增删改操作都只会发生在容器层中,对容器层下面的所有只读镜像层没有影响。依然引用这张经典图来做形象示意:

使用 UnionFS 搭建的分层结构

图中的容器是运行在 debian 容器环境中的 apache 网页应用,这个环境还提供了 emacs 编辑器功能。

将之前我们自己构建的 rootfs 与上面这张图片对比,会发现我们将所有系统文件、运行库文件和上层应用,都放到了一个 rootfs 里面,这样做缺乏灵活性,增大了维护的复杂度。而 Docker 引入了层(layer)的概念,将 rootfs 的内容进行了分层管理,有系统层,运行库依赖层等等,可以一层接一层进行增量式挂载叠加。启动容器的时候通过 UnionFS 把相关的层挂载到一个目录,作为容器的根 rootfs。

借助于 UnionFS,容器内部的更改都被保存到了最上面的读写层,而其他层都是只读的,这样中间的只读 rootfs 是可以被多个容器复用的。UnionFS 将文件的更新挂载到老的文件之上,而不去修改那些不更新的内容,这就意味着即使虚拟的文件系统被反复修改,也能保证宿主机空间占用保持一个较低水平。

三. 衍生出的 Docker 概念

3.1 Docker 镜像

docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。

bootfs(boot file system)主要包含 bootloader 和 Kernel , bootloader 主要是引导加 kernel, Linux刚启动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs 。这一层与我们典型的 Linux/Unix系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs 。

rootfs(root file system),在 bootfs之上。包含的就是典型 Linux系统中 的 /dev,/proc,/bin,/etc 等标准目录和文件。 rootfs就是各种不同的操作系统发行版,比如 Ubuntu, Centos 等等。平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?

对于精简的 OS,rootfs 可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用宿主机的kernel,自己只需要提供 rootfs 就可以了。由此可见对于不同的Linux发行版, bootfs 基本是一致的,rootfs会有差別,因此不同的发行版可以公用 bootfs。

docker本身是没有完整的操作系统的,它需要借助主机,无论是物理机还是虚拟机,docker启动的容器没有独立的操作系统,不需要自己的Bootloader ,所以没有bootfs的,因为主机已经启动起来了,但是它需要rootfs。

以在Linux操作系统主机的启动docker容器为例:

1. 在Linux操作系统启动后,首先将 rootfs 设置为 readonly, 进行一系列检查, 然后将其切换为 “readwrite”供用户使用。

2.Docker启动(以unionfs方式加载文件系统),初始化时也是将 rootfs 以 readonly 方式加载并检查;

3. 接下来,利用 union mount方式将一个readwrite文件系统挂载在readonly 的 rootfs 之上,并且允许再次将下层的 FS(file system) 设定为 readonly 并且向上叠加,这样一组readonly和一个writeable的层级结构就构成了一个 container 的运行时态, 每一个 FS 被称作一个 FS 层。但是在 Docker里,root文件系统永远只能是只读状态。

4. 这样一层一层堆叠,下面的层永远都是只读的,当所有层级加载完毕之后,它会将最上面的一层变为readwrite。所以针对这个容器的修改,事实上都是在最上面这一层进行的,并不会修改下面的readonly层。Union FS是层层叠加的,可以看到在做镜像构建的时候,差不多每条指令都会作为一个文件层保存下来。

可以查看到每一层里面执行了什么样的命令。

5. 在docker run具体容器的时候,就会去回放这个镜像,按照层级一级一级的去加载,通过unionfs方式去加载,这会有不同的驱动,会将dockerfile里面的每一层加载,每一层是readonly的层,然后不断的叠加,将下面一层变为readonly,最终将上面变为writeable,这个时候完整的操作系统所需要的文件系统就存在了,rootfs也就存在了,容器就可以去读取这些文件了。

目前有多种文件系统可以被当作联合文件系统,CentOS8 使用的是 overlay2,下面,我们尝试着挂载一个 overlay2 文件系统。

3.2 Docker 容器

Docker 容器与我们之前的容器在本质上没有区别,我们之前的容器更偏向抽象的技术概念,而受到在 Docker 管理约束的容器就是 Docker 容器,它会带有 Docker 产品的一些特征和功能。

Docker镜像 和 Docker容器 的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。
从文件系统来看,Docker容器比Docker镜像多一层可读写的文件系统挂载层,从生命周期来看,Docker容器可以被创建、启动、停止、删除、暂停等。

五. 小结

在 rootfs 的基础上,Docker 公司创新性地提出了使用 UnionFS,多个增量 rootfs 联合挂载一个完整 rootfs 的方案,通过“分层镜像”的设计,围绕 Docker 镜像,大家甚至可以协同工作,再加上 Docker 官方提供的镜像仓库,进一步减少了共享镜像的成本,这大大提高了开发部署的效率。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为OD机试之阿里巴巴找黄金宝箱(IV) C++

题目背景 贫如洗的椎夫阿里巴巴在去砍柴的路上,无意中发现了强盗集团的藏宝地,藏宝地有编号从0-N的箱子,每个箱子上面有一人数字,箱子排列成一个环,编号最大的箱子的下一个是编号为0的箱子。请输出每个箱了贴的数字之…

【记一次线上事故的排查思路】- CPU飙升问题排查

问题描述 由于项目排期较紧,临时从其他组调来三个开发资源帮我一起做项目,难免上线的时候大家的需求一块上线。 问题来了,上线三天后,线上CPU总是莫名奇妙的突然飙升,飙升后CPU并未降下来,而是一直处在高点…

解密POM:提升自动化脚本稳定性和开发效率的正确姿势!

Page Objects是selenium的一种测试设计模式,主要将每个页面看作是一个class。class的内容主要包括属性和方法,属性不难理解,就是这个页面中的元素对象,比如输入用户名的输入框,输入登陆密码的输入框、登陆按钮、这个页…

《WebKit 技术内幕》学习之七(3): 渲染基础

3 渲染方式 3.1 绘图上下文(GraphicsContext) 上面介绍了WebKit的内部表示结构,RenderObject对象知道如何绘制自己,但是,问题是RenderObject对象用什么来绘制内容呢?在WebKit中,绘图操作被定…

【Leetcode】2765. 最长交替子数组

文章目录 题目思路代码结果 题目 2765. 最长交替子数组 题目:给你一个下标从 0 开始的整数数组 nums 。如果 nums 中长度为 m 的子数组 s 满足以下条件,我们称它是一个 交替子数组 : m 大于 1 。 s1 s0 1 。 下标从 0 开始的子数组 s 与…

Vue中$watch()方法和watch属性的区别

vue中$watch()和watch属性都是监听值的变化的,是同一个作用,但是有两个不同写法。 用法一: //注意:这种方法是监听不到对象的变化的。 this.$watch((newVal,oldVal)>{ }) 用法二: watch:{xxx:(newVal,oldVal)>…

SpringCloud Aliba-Seata【上】-从入门到学废【7】

目录 🧂.Seata是什么 🌭2.Seata术语表 🥓3.处理过程 🧈4.下载 🍿5.修改相关配置 🥞6.启动seata 1.Seata是什么 Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能…

硅像素传感器文献调研(八)

1977 平面单场限环器件的理论与击穿电压 摘要 使用一个或多个浮置场限制环减少了平面器件中结曲率对击穿电压的不利影响。虽然这已经知道了一段时间,但还没有一种方法可以准确地预测使用场环可以实现的改善量。本文提出了一种计算机算法,它使得有可能进…

残差连接是什么意思

残差连接是深度神经网络中一种用于缓解梯度消失问题的技术。它的核心思想是通过将网络的输入直接传递到网络的输出,从而构建了一条直达路径,使得梯度更容易通过整个网络传播。这有助于在训练深层网络时避免梯度消失或梯度爆炸的问题。 在残差连接中&…

Linux 一键部署grafana

grafana 前言 Grafana 是一款开源的数据可视化和监控仪表盘工具。它提供了丰富的数据查询、可视化和报警功能,可用于实时监控、数据分析和故障排除等领域。 通过 Grafana,您可以连接到各种不同的数据源,包括时序数据库(如 Prometheus、InfluxDB)和关系型数据库(如 MySQ…

题记(26)--Sharing(链表公共后缀)

目录 一、题目内容 二、输入描述 三、输出描述 四、输入输出示例 五、完整C语言代码 一、题目内容 To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if…

Mybatis----缓存

MyBatis是一个流行的Java持久化框架,它提供了一个灵活的缓存机制来提高查询性能。 MyBatis的缓存机制主要分为一级缓存和二级缓存。 一级缓存是指在同一个SqlSession中,查询结果会被缓存起来,当再次执行同样的查询时,直接从缓存中…

Python学习04—基本图形绘制

通过一个案例来初步认识Python的图形绘制 案例:绘制Python蟒蛇 #PythonDraw.py import turtle turtle.setup(650,350,200,200) turtle.penup() turtle.fd(-250) turtle.pendown() turtle.pensize(25) turtle.pencolor("purple") turtle.seth(-40) for i…

基于springboot+vue的“衣依”服装销售平台系统(前后端分离)

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

使用云服务器被攻击,该如何防止ddos攻击

目前我们运行各项网络业务都离不开服务器,现在使用比较多的都是云服务器了。大家都知道,目前市场上用的云服务器大多数都是没有带什么防护了,那么用云服务器的时候,如果遭受到了ddos攻击,该怎么办,云服务器…

司铭宇老师:家具导购销售培训:家具导购员销售技巧和话术

家具导购销售培训:家具导购员销售技巧和话术 在现代家居市场中,家具不仅仅是日常生活的必需品,更是体现居住者个性和生活品味的重要元素。作为家具导购员,掌握专业的销售技巧和巧妙的话术对于吸引顾客、提高成交率至关重要。本文将…

[每日一题] 01.23 - 画矩形

画矩形 height,width,c,d input().split() height,width,d int(height),int(width),int(d) lis [c * width if d else c * (width - 2) c for i in range(height) ]lis: ##### # # # # ##### 或 # # # # # # # #if not d:print(c * width)for i in lis[1:-1…

【Linux编辑器-vim使用】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、vim的基本概念 二、vim的基本操作 分屏操作: 三、vim正常(命令)模式命令集 四、vim末行(底行)模…

PHP+vue+Mysql家庭理财管理系统演5x6nf

本文着重阐述了收支管理系统的分析、设计与实现,首先介绍开发系统和环境配置、数据库的设计,对系统的功能需求作出分析,根据需求对系统进行设计,明确各个部分的规范,来完成系统的设计。最后在对设计的系统进行一系列的…

项目工程下载与XML配置文件下载:EtherCAT超高速实时运动控制卡XPCIE1032H上位机C#开发(十)

XPCIE1032H功能简介 XPCIE1032H是一款基于PCI Express的EtherCAT总线运动控制卡,可选6-64轴运动控制,支持多路高速数字输入输出,可轻松实现多轴同步控制和高速数据传输。 XPCIE1032H集成了强大的运动控制功能,结合MotionRT7运动…