共计 6863 个字符,预计需要花费 18 分钟才能阅读完成。
本篇内容主要讲解“Docker AUFS 怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“Docker AUFS 怎么使用”吧!
AUFS 是一种 Union File System,所谓 UnionFS 就是把不同物理位置的目录合并 mount 到同一个目录中。UnionFS 的一个最主要的应用是,把一张 CD/DVD 和一个硬盘目录给联合 mount 在一起,然后,你就可以对这个只读的 CD/DVD 上的文件进行修改(当然,修改的文件存于硬盘上的目录里)。
AUFS 又叫 Another UnionFS,后来叫 Alternative UnionFS,后来可能觉得不够霸气,叫成 Advance UnionFS。是个叫 Junjiro Okajima(岡島順治郎)在 2006 年开发的,AUFS 完全重写了早期的 UnionFS 1.x,其主要目的是为了可靠性和性能,并且引入了一些新的功能,比如可写分支的负载均衡。AUFS 在使用上全兼容 UnionFS,而且比之前的 UnionFS 在稳定性和性能上都要好很多,后来的 UnionFS 2.x 开始抄 AUFS 中的功能。但是他居然没有进到 Linux 主干里,就是因为 Linus 不让,基本上是因为代码量比较多,而且写得烂(相对于只有 3000 行的 union mount 和 10000 行的 UnionFS,以及其它平均下来只有 6000 行代码左右的 VFS,AUFS 居然有 30000 行代码),所以,岡島不断地改进代码质量,不断地提交,不断地被 Linus 拒掉,所以,到今天 AUFS 都还进不了 Linux 主干(今天你可以看到 AUFS 的代码其实还好了,比起 OpenSSL 好 N 倍,要么就是 Linus 对代码的质量要求非常高,要么就是 Linus 就是不喜欢 AUFS)。
不过,好在有很多发行版都用了 AUFS,比如:Ubuntu 10.04,Debian6.0, Gentoo Live CD 支持 AUFS,所以,也 OK 了。
好了,扯完这些闲话,我们还是看一个示例吧(环境:Ubuntu 14.04)
首先,我们建上两个目录(水果和蔬菜),并在这两个目录中放上一些文件,水果中有苹果和蕃茄,蔬菜有胡萝卜和蕃茄。
1
2
3
4
5
6
7
8
$ tree
.
├── fruits
│ ├── apple
│ └── tomato
└── vegetables
├── carrots
└── tomato
然后,我们输入以下命令:
1
2
3
4
5
6
7
8
9
10
11
12
# 创建一个 mount 目录
$ mkdir mnt
# 把水果目录和蔬菜目录 union mount 到 ./mnt 目录中
$ sudo mount -t aufs -o dirs=./fruits:./vegetables none ./mnt
# 查看./mnt 目录
$ tree ./mnt
./mnt
├── apple
├── carrots
└── tomato
我们可以看到在./mnt 目录下有三个文件,苹果 apple、胡萝卜 carrots 和蕃茄 tomato。水果和蔬菜的目录被 union 到了./mnt 目录下了。
我们来修改一下其中的文件内容:
1
2
3
4
5
$ echo mnt ./mnt/apple
$ cat ./mnt/apple
mnt
$ cat ./fruits/apple
mnt
上面的示例,我们可以看到./mnt/apple 的内容改了,./fruits/apple 的内容也改了。
1
2
3
4
5
$ echo mnt_carrots ./mnt/carrots
$ cat ./vegetables/carrots
$ cat ./fruits/carrots
mnt_carrots
上面的示例,我们可以看到,我们修改了./mnt/carrots 的文件内容,./vegetables/carrots 并没有变化,反而是./fruits/carrots 的目录中出现了 carrots 文件,其内容是我们在./mnt/carrots 里的内容。
也就是说,我们在 mount aufs 命令中,我们没有指它 vegetables 和 fruits 的目录权限,默认上来说,命令行上第一个(最左边)的目录是可读可写的,后面的全都是只读的。(一般来说,最前面的目录应该是可写的,而后面的都应该是只读的)
所以,如果我们像下面这样指定权限来 mount aufs,你就会发现有不一样的效果(记得先把上面./fruits/carrots 的文件删除了):
1
2
3
4
5
6
7
8
9
$ sudo mount -t aufs -o dirs=./fruits=rw:./vegetables=rw none ./mnt
$ echo mnt_carrots ./mnt/carrots
$ cat ./vegetables/carrots
mnt_carrots
$ cat ./fruits/carrots
cat: ./fruits/carrots: No such file or directory
现在,在这情况下,如果我们要修改./mnt/tomato 这个文件,那么究竟是哪个文件会被改写?
1
2
3
4
5
6
7
$ echo mnt_tomato ./mnt/tomato
$ cat ./fruits/tomato
mnt_tomato
$ cat ./vegetables/tomato
I am a vegetable
可见,如果有重复的文件名,在 mount 命令行上,越往前的就优先级越高。
你可以用这个例子做一些各种各样的试验,我这里主要是给大家一个感性认识,就不展开试验下去了。
那么,这种 UnionFS 有什么用?
历史上,有一个叫 Knoppix 的 Linux 发行版,其主要用于 Linux 演示、光盘教学、系统急救,以及商业产品的演示,不需要硬盘安装,直接把 CD/DVD 上的 image 运行在一个可写的存储设备上(比如一个 U 盘上),其实,也就是把 CD/DVD 这个文件系统和 USB 这个可写的系统给联合 mount 起来,这样你对 CD/DVD 上的 image 做的任何改动都会在被应用在 U 盘上,于是乎,你可以对 CD/DVD 上的内容进行任意的修改,因为改动都在 U 盘上,所以你改不坏原来的东西。
我们可以再发挥一下想像力,你也可以把一个目录,比如你的源代码,作为一个只读的 template,和另一个你的 working directory 给 union 在一起,然后你就可以做各种修改而不用害怕会把源代码改坏了。有点像一个 ad hoc snapshot。
Docker 把 UnionFS 的想像力发挥到了容器的镜像。你是否还记得我在介绍 Linux Namespace 上篇中用 mount namespace 和 chroot 山寨了一镜像。现在当你看过了这个 UnionFS 的技术后,你是不是就明白了,你完全可以用 UnionFS 这样的技术做出分层的镜像来。
下图来自 Docker 的官方文档 Layer,其很好的展示了 Docker 用 UnionFS 搭建的分层镜像。
关于 docker 的分层镜像,除了 aufs,docker 还支持 btrfs, devicemapper 和 vfs,你可以使用 -s 或 –storage-driver= 选项来指定相关的镜像存储。在 Ubuntu 14.04 下,docker 默认 Ubuntu 的 aufs(在 CentOS7 下,用的是 devicemapper,关于 devicemapper,我会以以后的文章中讲解)你可以在下面的目录中查看相关的每个层的镜像:
1
/var/lib/docker/aufs/diff/ id
在 docker 执行起来后(比如:docker run -it ubuntu /bin/bash),你可以从 /sys/fs/aufs/si_[id] 目录下查看 aufs 的 mount 的情况,下面是个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ls /sys/fs/aufs/si_b71b209f85ff8e75/
br0 br2 br4 br6 brid1 brid3 brid5 xi_path
br1 br3 br5 brid0 brid2 brid4 brid6
# cat /sys/fs/aufs/si_b71b209f85ff8e75/*
/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7=rw
/var/lib/docker/aufs/diff/87315f1367e5703f599168d1e17528a0500bd2e2df7d2fe2aaf9595f3697dbd7-init=ro+wh
/var/lib/docker/aufs/diff/d0955f21bf24f5bfffd32d2d0bb669d0564701c271bc3dfc64cfc5adfdec2d07=ro+wh
/var/lib/docker/aufs/diff/9fec74352904baf5ab5237caa39a84b0af5c593dc7cc08839e2ba65193024507=ro+wh
/var/lib/docker/aufs/diff/a1a958a248181c9aa6413848cd67646e5afb9797f1a3da5995c7a636f050f537=ro+wh
/var/lib/docker/aufs/diff/f3c84ac3a0533f691c9fea4cc2ceaaf43baec22bf8d6a479e069f6d814be9b86=ro+wh
/var/lib/docker/aufs/diff/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158=ro+wh
64
65
66
67
68
69
70
/run/shm/aufs.xino
你会看到只有最顶上的层(branch)是 rw 权限,其它的都是 ro+wh 权限只读的。
关于 docker 的 aufs 的配置,你可以在 /var/lib/docker/repositories-aufs 这个文件中看到。
AUFS 的一些特性
AUFS 有所有 Union FS 的特性,把多个目录,合并成同一个目录,并可以为每个需要合并的目录指定相应的权限,实时的添加、删除、修改已经被 mount 好的目录。而且,他还能在多个可写的 branch/dir 间进行负载均衡。
上面的例子,我们已经看到 AUFS 的 mount 的示例了。下面我们来看一看被 union 的目录(分支)的相关权限:
rw 表示可写可读 read-write。
ro 表示 read-only,如果你不指权限,那么除了第一个外 ro 是默认值,对于 ro 分支,其永远不会收到写操作,也不会收到查找 whiteout 的操作。
rr 表示 real-read-only,与 read-only 不同的是,rr 标记的是天生就是只读的分支,这样,AUFS 可以提高性能,比如不再设置 inotify 来检查文件变动通知。
权限中,我们看到了一个术语:whiteout,下面我来解释一下这个术语。
一般来说 ro 的分支都会有 wh 的属性,比如“[dir]=ro+wh”。所谓 whiteout 的意思,如果在 union 中删除的某个文件,实际上是位于一个 readonly 的分支(目录)上,那么,在 mount 的 union 这个目录中你将看不到这个文件,但是 read-only 这个层上我们无法做任何的修改,所以,我们就需要对这个 readonly 目录里的文件作 whiteout。AUFS 的 whiteout 的实现是通过在上层的可写的目录下建立对应的 whiteout 隐藏文件来实现的。
看个例子:
假设我们有三个目录和文件如下所示(test 是个空目录):
1
2
3
4
5
6
7
8
9
# tree
.
├── fruits
│ ├── apple
│ └── tomato
├── test
└── vegetables
├── carrots
└── tomato
我们如下 mount:
1
2
3
4
5
6
# mkdir mnt
# mount -t aufs -o dirs=./test=rw:./fruits=ro:./vegetables=ro none ./mnt
# # ls ./mnt/
apple carrots tomato
现在我们在权限为 rw 的 test 目录下建个 whiteout 的隐藏文件.wh.apple,你就会发现./mnt/apple 这个文件就消失了:
1
2
3
4
# touch ./test/.wh.apple
# ls ./mnt
carrots tomato
上面这个操作和 rm ./mnt/apple 是一样的。
相关术语
Branch – 就是各个要被 union 起来的目录(就是我在上面使用的 dirs 的命令行参数)
Branch 根据被 union 的顺序形成一个 stack,一般来说最上面的是可写的,下面的都是只读的。
Branch 的 stack 可以在被 mount 后进行修改,比如:修改顺序,加入新的 branch,或是删除其中的 branch,或是直接修改 branch 的权限
Whiteout 和 Opaque
如果 UnionFS 中的某个目录被删除了,那么就应该不可见了,就算是在底层的 branch 中还有这个目录,那也应该不可见了。
Whiteout 就是某个上层目录覆盖了下层的相同名字的目录。用于隐藏低层分支的文件,也用于阻止 readdir 进入低层分支。
Opaque 的意思就是不允许任何下层的某个目录显示出来。
在隐藏低层档的情况下,whiteout 的名字是’.wh. filename’。
在阻止 readdir 的情况下,名字是’.wh..wh..opq’或者’.wh.__dir_opaque’。
相关问题
看到上面这些,你一定会有几个问题:
其一、你可能会问,要有文件在原来的地方被修改了会怎么样?mount 的目录会一起改变吗?答案是会的,也可以是不会的。因为你可以指定一个叫 udba 的参数(全称:User’s Direct Branch Access),这个参数有三个取值:
udba=none – 设置上这个参数后,AUFS 会运转的更快,因为那些不在 mount 目录里发生的修改,aufs 不会同步过来了,所以会有数据出错的问题。
udba=reval – 设置上这个参数后,AUFS 会去查文件有没有被更新,如果有的话,就会把修改拉到 mount 目录内。
udba=notify – 这个参数会让 AUFS 为所有的 branch 注册 inotify,这样可以让 AUFS 在更新文件修改的性能更高一些。
其二、如果有多个 rw 的 branch(目录)被 union 起来了,那么,当我创建文件的时候,aufs 会创建在哪里呢?aufs 提供了一个叫 create 的参数可以供你来配置相当的创建策略,下面有几个例子。
create=rr | round−robin 轮询。下面的示例可以看到,新创建的文件轮流写到三个目录中
1
2
3
4
5
6
7
8
9
10
hchen$ sudo mount -t aufs -o dirs=./1=rw:./2=rw:./3=rw -o create=rr none ./mnt
hchen$ touch ./mnt/a ./mnt/b ./mnt/c
hchen$ tree
.
├── 1
│ └── a
├── 2
│ └── c
└── 3
└── b
create=mfs[:second] | most−free−space[:second] 选一个可用空间最好的分支。可以指定一个检查可用磁盘空间的时间。
create=mfsrr:low[:second] 选一个空间大于 low 的 branch,如果空间小于 low 了,那么 aufs 会使用 round-robin 方式。
更多的关于 AUFS 的细节使用参数,大家可以直接在 Ubuntu 14.04 下通过 man aufs 来看一下其中的各种参数和命令。
AUFS 的性能
AUFS 的性能慢吗?也慢也不慢。因为 AUFS 会把所有的分支 mount 起来,所以,在查找文件上是比较慢了。因为它要遍历所有的 branch。是个 O(n) 的算法(很明显,这个算法有很大的改进空间的)所以,branch 越多,查找文件的性能也就越慢。但是,一旦 AUFS 找到了这个文件的 inode,那后以后的读写和操作原文件基本上是一样的。
所以,如果你的程序跑在在 AUFS 下,open 和 stat 操作会有明显的性能下降,branch 越多,性能越差,但是在 write/read 操作上,性能没有什么变化。
IBM 的研究中心对 Docker 的性能给了一份非常不错的性能报告(PDF)《An Updated Performance Comparison of Virtual Machinesand Linux Containers》
我截了两张图出来,第一张是顺序读写,第二张是随机读写。基本没有什么性能损失的问题。而 KVM 在随机读写的情况也就有点慢了(但是,如果硬盘是 SSD 的呢?)
顺序读写
到此,相信大家对“Docker AUFS 怎么使用”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!