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行代码左右的VFSAUFS居然有30000行代码所以��不断地改进代码质量不断地提交不断地被Linus拒掉所以到今天 AUFS 都还进不了Linux主干今天你可以看到AUFS的代码其实还好了比起 OpenSSL 好N倍要么就是 Linus 对代码的质量要求非常高要么就是Linus就是不喜欢 AUFS。
不过好在有很多发行版都用了 AUFS比如Ubuntu 10.04Debian6.0, Gentoo Live CD支持AUFS所以也OK了。
好了扯完这些闲话我们还是看一个示例吧环境Ubuntu 14.04
首先我们建上两个目录水果和蔬菜并在这两个目录中放上一些文件水果中有苹果和蕃茄蔬菜有胡萝卜和蕃茄。
$ tree . ├── fruits │ ├── apple │ └── tomato └── vegetables ├── carrots └── tomato
然后我们输入以下命令
# 创建一个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 目录下了。
我们来修改一下其中的文件内容
$ echo mnt > ./mnt/apple $ cat ./mnt/apple mnt $ cat ./fruits/apple mnt
上面的示例我们可以看到./mnt/apple 的内容改了./fruits/apple 的内容也改了。
$ 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 的文件删除了
$ 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这个文件那么究竟是哪个文件会被改写
$ 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的想像力发挥到了容器的镜像。你是否还记得我在大话Docker四Linux Namespace上中用mount namespace和chroot山寨了一镜像。现在当你看过了这个UnionFS的技术后你是不是就明白了你完全可以用UnionFS这样的技术做出分层的镜像来。
下图来自Docker的官方文档Layer其很好的展示了Docker用UnionFS搭建的分层镜像。
关于docker的分层镜像除了aufsdocker还支持btrfs, devicemapper和vfs你可以使用 -s 或 �storage-driver= 选项来指定相关的镜像存储。在Ubuntu 14.04下docker默认Ubuntu的 aufs在CentOS7下用的是devicemapper关于devicemapper我会以以后的文章中讲解你可以在下面的目录中查看相关的每个层的镜像
/var/lib/docker/aufs/diff/<id>
在docker执行起来后比如docker run -it ubuntu /bin/bash 你可以从/sys/fs/aufs/si_[id]目录下查看aufs的mount的情况下面是个示例
#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是个空目录
# tree . ├── fruits │ ├── apple │ └── tomato ├── test └── vegetables ├── carrots └── tomato
我们如下mount
# 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这个文件就消失了:
# mkdir mnt # mount -t aufs -o dirs=./test=rw:./fruits=ro:./vegetables=ro none ./mnt # # ls ./mnt/ apple 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 | roundrobin 轮询。下面的示例可以看到新创建的文件轮流写到三个目录中
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] | mostfreespace[: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的呢?
顺序读写
随机读写
作者陈皓
出处酷壳-coolshell.cn
欢迎关注Reboot教育 运维自动化班
课程表:http://www.51reboot.com/course/devops/
上课形式:面授班 / 网络直播班
QQ:979950755
交流群:238757010