[转载] 剖析Docker文件系统:Aufs与Devicemapper

在启动容器的时候,需要创建文件系统,为提供挂载点 。最初仅能在支持Aufs文件系统的Linux发行版上运行,但是由于Aufs未能加入Linux内核,为了寻求兼容性、扩展性 , 在内部通过机制这种可扩展的方式来实现对不同文件系统的支持 。目前 , 支持Aufs,,Btrfs和Vfs四种文件系统 。若想了解更多内容可以点击这里 。
本文以 1.4为基础,首先分析镜像的结构,接着介绍Aufs和的应用,最后从代码的角度分析文件系统的实现 。
镜像
典型的Linux文件系统由和两部分组成,(boot file )主要包含 和,主要是引导加载,当被加载到内存中后 就被了 。(root file ) 包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc等标准目录和文件 。
容器是建立在Aufs基础上的 , Aufs是一种Union FS, 简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念 。Aufs将挂载到同一虚拟文件系统下的多个目录分别设置成read-only,read-write以及-able权限,对read-only目录只能读,而写操作只能实施在read-write目录中 。重点在于,写操作是在read-only上的一种增量操作 , 不影响read-only目录 。当挂载目录的时候要严格按照各目录之间的这种增量关系,将被增量操作的目录优先于在它基础上增量操作的目录挂载,待所有目录挂载结束了,继续挂载一个read-write目录,如此便形成了一种层次结构 。
镜像的典型结构如下图 。传统的Linux加载时会先将设为read-only , 然后在系统自检之后将从read-only改为read-write,然后我们就可以在上进行写和读的操作了 。但的镜像却不是这样 , 它在自检完毕之后并不会把的read-only改为read-write 。而是利用union mount(的一种挂载机制)将一个或多个read-only的加载到之前的read-only的层之上 。在加载了这么多层的之后 , 仍然让它看起来只像是一个文件系统,在的体系里把union mount的这些read-only的叫做的镜像 。但是,此时的每一层都是read-only的,我们此时还不能对其进行操作 。当我们创建一个容器,也就是将镜像进行实例化,系统会在一层或是多层read-only的之上分配一层空的read-write的 。
相关厂商内容
QClub:专场——聚焦国内创业以及企业实践 《PHP7 - Newfor good old train》惠新宸 基于 /OTP 构建大规模实时系统 Lua 在 Nginx 中的应用 美图公司大数据职位北京&厦门火热招聘中
相关赞助商
全球软件开发大会,4月23-25日,北京,敬请期待!
为了形象化的镜像结构,我们 pull一个:14.04的镜像,使用-tree查看结果如下:
[root@qingze qingze]# docker images -treeWarning: '-tree' is deprecated, it will be removed soon. See usage.└─511136ea3c5a Virtual Size: 0 B└─3b363fd9d7da Virtual Size: 192.5 MB└─607c5d1cca71 Virtual Size: 192.7 MB└─f62feddc05dc Virtual Size: 192.7 MB└─8eaa4ff06b53 Virtual Size: 192.7 MB Tags: ubuntu:14.04,
可以看到的镜像中有多个长ID的layer,且以一种树状结构继承下来,如下图 。其中,第n+1层继承了第n层,并在此基础上有了自己的内容 , 直观上的表现就是第n+1层占用磁盘空间增大 。并且,不同的镜像可能会有相同的父镜像 。例如,图中和Nginx 继承于同一个Vim 镜像,这种组织方式起到共享的作用,节约了镜像在物理机上占用的空间 。
接下来,使用 save命令将镜像具体化并查看其结构:
[root@qingze qingze]# docker save -o ubuntu.tar ubuntu:14.04[root@qingze qingze]#tar -tf ubuntu.tar 3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a/3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a/VERSION3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a/json3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a/layer.tar511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/VERSION511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar607c5d1cca71dd3b6c04327c3903363079b72ab3e5e4289d74fb00a9ac7ec2aa/607c5d1cca71dd3b6c04327c3903363079b72ab3e5e4289d74fb00a9ac7ec2aa/VERSION607c5d1cca71dd3b6c04327c3903363079b72ab3e5e4289d74fb00a9ac7ec2aa/json607c5d1cca71dd3b6c04327c3903363079b72ab3e5e4289d74fb00a9ac7ec2aa/layer.tar8eaa4ff06b53ff7730c4d7a7e21b4426a4b46dee064ca2d5d90d757dc7ea040a/8eaa4ff06b53ff7730c4d7a7e21b4426a4b46dee064ca2d5d90d757dc7ea040a/VERSION8eaa4ff06b53ff7730c4d7a7e21b4426a4b46dee064ca2d5d90d757dc7ea040a/json8eaa4ff06b53ff7730c4d7a7e21b4426a4b46dee064ca2d5d90d757dc7ea040a/layer.tarf62feddc05dc67da9b725361f97d7ae72a32e355ce1585f9a60d090289120f73/f62feddc05dc67da9b725361f97d7ae72a32e355ce1585f9a60d090289120f73/VERSIONf62feddc05dc67da9b725361f97d7ae72a32e355ce1585f9a60d090289120f73/jsonf62feddc05dc67da9b725361f97d7ae72a32e355ce1585f9a60d090289120f73/layer.tarrepositories
我们可以发现在具体化的镜像中对应树型结构中的每一个layer都有一个文件夹存在,且每个文件夹中包含 , json,layer.tar三个文件,另外还有一个文件 。长ID为的layer没有继承任何layer,因此被称为base镜像,一般来说镜像都是以这个layer开始的,其内容也为空 。
[root@qingze 3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a]# lsjsonlayer.tarVERSION[root@qingze 3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a]# cat json | python -m json.tool{"Size": 192480945,"architecture": "amd64","checksum": "tarsum.dev+sha256:3679c533275a3074e9ab22c1a98a5c63515df754dacecb36cf03115a6ce7cc44","config": {"AttachStderr": false,"AttachStdin": false,"AttachStdout": false,"Cmd": null,"CpuShares": 0,"Cpuset": "","Domainname": "",...},"container": "8c41fcbc2d072f0158d254ca92f33e10d7cb57af3eec3cd3c7fb308a6f24a3e0","container_config": {...},"created": "2015-01-01T01:25:18.368698296Z","docker_version": "1.4.1","id": "3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a","os": "linux","parent": "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}
由于篇幅关系,这里只截取了json文件的一部分内容 , 可以看出json文件包含了镜像以及其对应的容器的配置信息 , 其中有 run时指定的,如,,也有系统生成的,如,正是这个属性记录了继承结构,从而实现了layer的层次关系 。
另外,每个文件夹下都有一个layer.tar的文件,这个压缩文件存放的正是的内容,不过是增量存放的而已 。为了证明这一点我们找到:14.04对应的layer是,进入该目录 , 查看layer的内容,未发现任何东西 。
[root@qingze 8eaa4ff06b53ff7730c4d7a7e21b4426a4b46dee064ca2d5d90d757dc7ea040a]# tar -tf layer.tar [root@qingze 8eaa4ff06b53ff7730c4d7a7e21b4426a4b46dee064ca2d5d90d757dc7ea040a]#
然后,我们使用该镜像启动一个容器,创建一个100M的文件并 该容器,具体化为:test后重新查看 。结果显示,:test相比:14.04多了一个文件夹,且该文件夹下的layer.tar中有了test文件 。读者亦可对比各个长ID中的内容,以加深理解 。
[root@qingze qingze]# docker images -treeWarning: '-tree' is deprecated, it will be removed soon. See usage.└─511136ea3c5a Virtual Size: 0 B└─3b363fd9d7da Virtual Size: 192.5 MB└─607c5d1cca71 Virtual Size: 192.7 MB└─f62feddc05dc Virtual Size: 192.7 MB└─8eaa4ff06b53 Virtual Size: 192.7 MB Tags: ubuntu:14.04└─7e64c2624ce0 Virtual Size: 297.5 MB Tags: ubuntu:test[root@qingze qingze]# docker save -o ubuntu-test.tar ubuntu:test [root@qingze qingze]# mkdir ubuntu-test[root@qingze qingze]# tar -xf ubuntu-test.tar -C ubuntu-test/[root@qingze qingze]# cd ubuntu-test/[root@qingze ubuntu-test]# cd 7e64c2624ce0fe1b893ecfbec4e47eb3bd80dd30e615e376ac37405a7baa4e4c/[root@qingze 7e64c2624ce0fe1b893ecfbec4e47eb3bd80dd30e615e376ac37405a7baa4e4c]# tar -tf layer.tar testroot/root/.bash_history
Aufs与的应用
Aufs是最初采用的文件系统 , 由于Aufs未能加入到Linux内核,考虑到兼容性问题,加入了的支持 。目前,除少数版本如 , 基本运行在基础上 。这一节中,我们将着重介绍Aufs和的应用 。
Aufs
Aufs是 Union File 的缩写,是一种Union FS,支持将多个目录挂载到同一个虚拟目录下 。由于上文已有介绍,不再赘述 。这里主要介绍一下Aufs的使用 。默认采用的是:14.04 。
Aufs的使用非常简单 , 只需简单的mount命令即可:
root@Standard-PC:/tmp# tree.├── aufs├── dir1│└── file1└── dir2└── file2root@Standard-PC:/tmp# sudo mount -t aufs -o br=/tmp/dir1=ro:/tmp/dir2=rw none /tmp/aufsmount: warning: /tmp/aufs seems to be mounted read-only.root@Standard-PC:/tmp#
假设在/tmp目录下存在如上文件结构,file1的内容为:this is dir1,file2的内容为:this is dir2 。我们使用mount命令将dir1和dir2挂载到/tmp/aufs目录下 , 其中:
经过以上命令,文件夹dir1和dir2被挂载到了/tmp/aufs目录下,且file1只有只读属性:
root@Standard-PC:/tmp/aufs# echo hello > file1bash: file1: Read-only file systemroot@Standard-PC:/tmp/aufs# echo hello > file2root@Standard-PC:/tmp/dir2# cat file2 hello
写到这里有的读者可能会问,如果dir1和dir2两个文件夹含有相同的文件会发生什么情况,我们来试一下,将dir1中的file1更名为file2,重新挂载 。我们发现/tmp/aufs目录中仅有file2 , 且内容为dir1中file2的内容 。由此可见 , 在挂载的过程中,mount命令按照命令行中给出的文件夹顺序挂载 , 若出现有同名文件的情况,则以先挂载的为主,其他的不再挂载 。这也说明了的镜像为什么采用增量的方式:完全是利用Aufs的特性达到节约空间的目的 。
root@wangqingze-Standard-PC:/tmp/aufs# lsfile2root@wangqingze-Standard-PC:/tmp/aufs# cat file2 this is dir1root@wangqingze-Standard-PC:/tmp/aufs#
在介绍之前,我们首先介绍几个Linux下有关 的概念以及他们的特性,以帮助后文的理解,主要包括:,-。
是Lvm提供的一种特性,它可以在不中断服务运行的情况下为the ( )创建一个虚拟快照(),它具有以下几个特点:
当the 内容发生变化时,对变化的部分做一个拷贝以用来对the 进行重构 。因为只对变化的部分做拷贝,所以Lvm的在读操作频繁而写操作不频繁的情况下占用很少的一部分空间便能完成特定任务 。当大小耗尽或者远大于实际需求时,我们可以对其大小进行调节 。当对的数据进行写操作的时候,实施相应操作,并丢弃从the 的拷贝 , 以后的操作以写操作之后中的数据为准 。在某些发行版的Linux系统下,可以使用的--merge选项将合并回the。
对的应用诸如:对数据进行实时备份,利用其可读写的特性创建供测试等等 。
Thin-是一项利用虚拟化方法减少物理存储部署的技术 , 可最大限度提升存储空间利用率 。下图中展示了某位用户向服务器管理员请求分配10TB的资源的情形 。实际情况中这个数值往往是峰值,根据使用情况,分配2TB就已足够 。因此 , 系统管理员准备2TB的物理存储,并给服务器分配10TB的虚拟卷 。服务器即可基于仅占虚拟卷容量1/5的现有物理磁盘池开始运行 。这样的“始于小”方案能够实现更高效地利用存储容量 。
Thin- 结合Thin-和两种技术,允许多个虚拟设备同时挂载到一个数据卷以达到数据共享的目的 。Thin- 的特点如下:
可以将不同的挂载到同一个the 上,节省了磁盘空间 。当多个挂载到了同一个the 上,并在the 上发生写操作时,将会触发COW操作 。这样不会降低效率 。Thin- 支持递归操作,即一个可以作为另一个的the,且没有深度限制 。在上可以创建一个逻辑卷 , 这个逻辑卷在实际写操作(COW,写操作)发生之前是不占用磁盘空间的 。
Thin- 虽然有诸多优点,但是也有很多不足之处,例如大小固定等问题 , 如若有兴趣可以阅读参考文献,这里不再赘述 。
Thin- 是作为 的一个在内核中实现的 。是Linux 2.6内核中提供的一种从逻辑设备到物理设备的映射框架机制 。在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,如条带化,镜像,快照等 。
主要包含内核空间的映射和用户空间的 库及工具 。库是对ioctl、用户空间创建删除 逻辑设备所需必要操作的封装,是一个提供给用户直接可用的创建删除 设备的命令行工具 。
我们以命令来介绍一下Thin- 时如何实现的 。Thin- 需要一个data设备和一个设备分别用来存放实际数据和元数据 。有两种方式可以更改:一种时通过函数调用 , 另一种则是通过 命令 。这里,我们创建两个稀疏文件作为data和设备:
[root@qingze dev]# dd if=/dev/zero of=/tmp/metadata bs=1K count=1 seek=2G1+0 records in1+0 records out1024 bytes (1.0 kB) copied, 0.000153611 s, 6.7 MB/s[root@qingze dev]# dd if=/dev/zero of=/tmp/data bs=1K count=1 seek=100G1+0 records in1+0 records out1024 bytes (1.0 kB) copied, 8.7567e-05 s, 11.7 MB/s
以上命令分别创建了2G的和100G的data两个稀疏文件 。注意命令中seek选项,其表示为略过of选项指定的输出文件的前2G空间再写入内容 。这2G在硬盘上是没有占有空间的,占有空间只有1k的内容 。当向其写入内容时,才会在硬盘上为其分配空间 。
文件创建完成后,我们将其挂载到设备上 , 以供使用:
[root@qingze dev]# losetup /dev/loop0 /tmp/metadata[root@qingze dev]# losetup /dev/loop1 /tmp/data[root@qingze dev]# losetup -a/dev/loop0: [0035]:183033 (/tmp/metadata)/dev/loop1: [0035]:185651 (/tmp/data)
准备工作就绪后 , 我们开始正式创建 。首先创建一个thin-pool,指定要用哪些设备来创建,此命令的作用为:将逻辑设备的0~之间的映射到/dev/loop1上,元数据信息存储到/dev/loop1:
[root@qingze dev]# dmsetup create pool --table "0 20971520 thin-pool /dev/loop0 /dev/loop1 128 32768 1 skip_block_zeroing"
在继续创建前,需要创建一个- 并格式化:
[root@qingze dev]# dmsetup message /dev/mapper/pool 0 "create_thin 0"[root@qingze dev]# dmsetup create thin --table "0 2097152 thin /dev/mapper/pool 0"[root@qingze dev]# mkfs.ext4 -E discard,lazy_itable_init=0 /dev/mapper/thin
命令的格式为:
Sendto . Ifnotuse 0
'0' is anfor the
官方文档中对有和之分,说明如下:
:
Once , the user doesn't have to worry about any
theand the .theis no
from any other -and can be
via the same . It'slegal to
have only one of them , and there's noon
orthem both. (Thisfrom
- .)
:
You can use anas anfor a
- . Any read to anarea of the
thinwill beto the .
theof newas usual.
读者可以根据自己的需求选择,这里我们只建立一个 :
[root@qingze dev]# dmsetup suspend /dev/mapper/thin[root@qingze dev]# dmsetup message /dev/mapper/pool 0 "create_snap 1 0"[root@qingze dev]# dmsetup resume /dev/mapper/thin[root@qingze dev]# dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 1"[root@qingze dev]# dmsetup statusthin: 0 2097152 thin 99840 2097151fedora-swap: 0 8126464 linear fedora-root: 0 104857600 linear snap: 0 2097152 thin 99840 2097151pool: 0 20971520 thin-pool 0 280/4161600 780/163840 - rw discard_passdown queue_if_no_space fedora-home: 0 97509376 linear
至此,我们已经成功建立了一个,接下来可以将其挂载到任何一个文件夹下,并对其进行创建文件等操作:
【[转载] 剖析Docker文件系统:Aufs与Devicemapper】[root@qingze dev] mount /dev/mapper/snap dv
我们在dv文件夹下创建一个内容为this is snap的文件file,然后,在snap的基础上再创建一个snap1,观察发生了什么:
[root@qingze tmp]# dmsetup message /dev/mapper/pool 0 "create_snap 2 1"[root@qingze tmp]# dmsetup create snap1 --table "0 2097152 thin /dev/mapper/pool 2"[root@qingze tmp]# mkdir dv1[root@qingze tmp]# mount /dev/mapper/snap1 dv1[root@qingze dv]# cd ..[root@qingze tmp]# cd dv1[root@qingze dv1]# lsfilelost+found[root@qingze dv1]# cat filethis is snap
可以发现包含同样的文件和内容 。事实上,正是以这种方式来创建容器的,读者可以执行以下命令来验证:
#在`metadata`文件夹下查看某个某个容器的`device_id`[root@qingze metadata]# cat 3b363fd9d7dab4db9591058a3f43e806f6fa6f7e2744b63b2df4b84eadb0685a {"device_id":910,"size":10737418240,"transaction_id":1817,"initialized":false}#创建并挂载snapshot[root@qingze tmp]# dmsetup message /dev/mapper/docker-253:1-3020991-pool 0 "create_snap 6001 910"[root@qingze tmp]# dmsetup create snap6 --table "0 20971520 thin /dev/mapper/docker-253:1-3020991-pool 6001"[root@qingze tmp]# mkdir dv6[root@qingze tmp]# mount /dev/mapper/snap6 dv6[root@qingze tmp]# cd dv6/[root@qingze dv6]# lsidlost+foundrootfs[root@qingze dv6]# cd rootfs/[root@qingze rootfs]# lsbinbootdevetchomeliblib64mediamntoptprocrootrunsbinsrvsystmpusrvar

[转载] 剖析Docker文件系统:Aufs与Devicemapper

文章插图
源码分析
在继续介绍之前,我们首先介绍一下的系统架构 。采用一种c/s的结构,如下图所示,由和两部分组成 。主要由三部分:,和Job 。启动时首先进行一些初始化操作 , 如初始化,创建 , 创建等等 。在初始化结束后,启动负责监听 发来的请求,的运行以及其对 端请求的响应都是以Job的形式在中完成的 。是架构中的运行引擎,同时也是的核心模块 。一个Job可以认为是架构中内部最基本的工作执行单元 。可以做的每一项工作,都可以抽象为一个Job 。负责对命令行的解析 , 其运用反射机制将命令行转换成函数的形式通过某种协议与 进行通信,通过以RPC的方式调用相应功能 。目前,支持三种调用方式:fd,unix  , http 。值得注意的是 ,  向 提供的所有功能都是以的方式注册到中的 。举例说明,的对象中有一项为:{"":.},则说明当名为""的Job在运行时 , 执行的是.的 。这样做的好处是增加了系统的可扩展性,添加新功能时只需简单的将其注册到中即可 。
在中为了实现对多种不同文件系统的支持,增加系统的可扩展性,采用多态的方式将文件系统的操作抽象为接口:
type ProtoDriver interface {// String returns a string representation of this driver.String() string// Create creates a new, empty, filesystem layer with the// specified id and parent. Parent may be "".Create(id, parent string) error// Remove attempts to remove the filesystem layer with this id.Remove(id string) error// Get returns the mountpoint for the layered filesystem referred// to by this id. You can optionally specify a mountLabel or "".// Returns the absolute path to the mounted layered filesystem.Get(id, mountLabel string) (dir string, err error)// Put releases the system resources for the specified id,// e.g, unmounting layered filesystem.Put(id string)// Exists returns whether a filesystem layer with the specified// ID exists on this driver.Exists(id string) bool// Status returns a set of key-value pairs which give low// level diagnostic status about this driver.Status() [][2]string// Cleanup performs necessary tasks to release resources// held by the driver, e.g., unmounting all layered filesystems// known to this driver.Cleanup() error}
包含了各文件系统所需的基本操作 , 如Crate , 等,不同的文件系统只需简单的实现各接口即可 。Go语言在语法上与其他语言有些不同,如果不理解具体实现,请参阅相关文档 。中文件系统的继承结构如下图(仅Aufs和画出):
文件系统功能分为两部分:文件系统初始化和创建容器 。我们首先介绍初始化部分Aufs和实现的功能,然后再介绍如何创建容器 。
文件系统初始化过程是在 启动时进行的 。在 启动时会调用函数,继而调用ry完成相关环境的设置与初始化 。其中,以下为初始化文件系统的操作:
// Load storage driverdriver, err := graphdriver.New(config.Root, config.GraphOptions)if err != nil {return nil, err}log.Debugf("Using graph driver %s", driver)
.New函数会调用//.go中的函数:
func GetDriver(name, home string, options []string) (Driver, error) {if initFunc, exists := drivers[name]; exists {return initFunc(path.Join(home, name), options)}return nil, ErrNotSupported}
函数中通过[name]返回物理机所支持的文件系统的初始化函数,进行初始化操作 。整个流程如下图:
Aufs在初始化Init函数中主要完成了以下几个操作:
调用函数加载Aufs模块 。调用在系统中为/var/lib//aufs创建一个挂载点 。这里的实现原理与mount --bind命令一样,只不过mount命令的源文件夹和目的文件夹一样,在系统中只创建了挂载点而已 。并且这个挂载点的内容即不受源文件夹的影响也不影响源文件夹 。最后,在/var/lib//aufs创建mnt,diff,文件夹 。mnt文件夹为容器的挂载点目录 , 每一个容器在mnt下都有一个长ID目录,对应为该容器的的挂载点 。diff有着与mnt中对应的长ID目录,这里的每个目录对应 镜像的一个layer层 , 里面存放的是该layer相比较于父layer变化的内容 。注意: 这里才是存放我们在容器中看到的内容的地方,比如/usr, /bin等等 。
相比之下,的初始化操作Init相对麻烦一些 。在使用的过程中需要需要一个24-bit的标识来唯一标记一个,同时为了保证向中数据的一致性,定义了和结构,通过对属性和加锁来实现一致性 。和是一对多的关系,每一个都会对应一个在中 。
在初始化的过程中,Init首先调用函数构建一个对象,配置相关选项,接着调用函数完成的初始化操作,大致分为四个步骤:
创建一个thin pool 。为了创建thin pool , 需要两个块设备 。默认情况下,启动时为我们在/var/lib///下创建了100G的和2G的data两个文件,并挂载到设备/dev/loop0和/dev/loop1,用loop0 , loop1创建thin pool 。由于它们是稀疏文件 , 所以直到被使用,否则它们是不占用空间的 。
在中调用创建一个 用作的the。在中调用ed激活步骤2创建的的设备 。在中调用对步骤2创建的的设备格式化 。
在我们使用 run创建一个容器时 , 如下图,这个过程分为两个步骤:1.创建容器,2.启动容器 。以下两个小节中,我们将以创建ID为为例说明创建容器的过程 。
Aufs在创建容器的过程中调用函数创建容器运行所需的 , 具体步骤如下:
在调用函数mnt和diff文件夹下创建-init文件夹 。在调用Get函数将容器所依赖的layer按照继承顺序挂载到mnt/-init文件夹下 。如下图所示,假设继承顺序为
在再次调用函数在mnt和diff文件夹下创建文件夹 。
当容器创建完成后,进入容器启动阶段:
在Mount调用Get函数将容器所依赖的layer按照继承顺序挂载到mnt/文件夹下,操作如同第一次挂载 。
在创建容器的过程中同样调用函数创建容器运行所需的,具体步骤如下:
在调用函数,其调用->vice->创建一个-init , 下图中以Snap-init-3表示 。其继承了Snap-1,Snap-2的所有内容,并且只保存相对于Snap-2 , Snap-3变化的内容 。这里为了简单未画出所有的,读者可以认为已经存在了容器所需的所有layer , 而Snap-2是容器的父layer 。
在调用Get函数,其调用->ed完成设备的激活,最后调用系统调用将-init挂载到mnt/-init文件夹下 。在调用函数创建 , 即图中的Snap-3 。
当容器创建完成后,进入启动阶段:
在Mount调用Get函数,其调用->ed完成设备的激活,最后调用系统调用将 挂载到mnt/文件夹下 。
总结
Aufs实现起来比较简单,但是由于其迟迟不能加入linux内核 , 导致兼容性差 。目前,仅有支持 。虽然实现起来复杂,但兼容性好 。其存在的一点不足是,当和data空间被耗尽时,需要重启来扩充空间 。
作者简介
王青泽,软件开发工程师 。东北大学硕士,研究方向为基于的多样性标签推荐 。2014年毕业后就职于京东,参与并完成了基于的自动化运维项目,因而对产生了浓厚兴趣 。邮箱:
参考文献 %E7%B3%BB%E7%BB%9F%E7%AE%A1%E7%90%86: