docker学习笔记-AUFS

2016-11-28 14:41:01

AUFS的八卦

AUFS最初命名为Another UnionFS,后来改为Alternative UnionFS,后来可能觉得不够霸气,又改为Advance UnionFS。Junjiro Okajima(岡島順治郎)在2006年开发的,AUFS完全重写了早期的UnionFS 1.x,其主要目的是为了可靠性和性能,并且引入了一些新的功能,比如可写分支的负载均衡。AUFS在使用上全兼容UnionFS,而且比之前的UnionFS在稳定性和性能上都要好很多,后来的UnionFS 2.x开始抄AUFS中的功能。因为Linus的原因,没有进到Linux主干里,基本上是因为代码量比较多,而且写得烂(相对于只有3000行的union mount和10000行的UnionFS,以及其它平均下来只有6000行代码左右的VFS,AUFS居然有30000行代码),所以,岡島不断地改进代码质量,不断地提交,不断地被Linus拒掉,所以,到今天AUFS都还进不了Linux主干(今天你可以看到AUFS的代码其实还好了,比起OpenSSL好N倍,要么就是Linus对代码的质量要求非常高,要么就是Linus就是不喜欢AUFS)

Linux文件系统 vs AUFS

典型的Linux文件系统有bootfs和rootfs两部分组成
bootfs主要包含bootloader和kernel,bootloader主要引导加载kernel,当kernel被加载到内存中后bootfs就被umount了
rootfs包含linux系统中的/dev、/proc、/bin、/etc等标准目录和文件

AUFS是一种Union File System,所谓UnionFS就是把不同物理位置的目录合并mount到同一个目录中。UnionFS的一个最主要的应用是,把一张CD/DVD和一个硬盘目录给联合 mount在一起,然后,你就可以对这个只读的CD/DVD上的文件进行修改(当然,修改的文件存于硬盘上的目录里)

传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统自检之后将rootfs从read-only改为read-write,然后就可以在rootfs上进行写和读的操作了。但Docker的镜像却不是这样,它在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来只像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,此时还不能对其进行操作。当创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs

回到AUFS,让我们来看个示例吧
首先,建上两个目录(水果和蔬菜),并在这两个目录中放上一些文件,水果中有苹果和蕃茄,蔬菜有胡萝卜和蕃茄。

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
# 创建一个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
$ 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
 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
$ echo "mnt_tomato" > ./mnt/tomato
$ cat ./fruits/tomato
mnt_tomato
$ cat ./vegetables/tomato
I am a vegetable

可见,如果有重复的文件名,在mount命令行上,越往前的就优先级越高。

关于docker的分层镜像,除了aufs,docker还支持btrfs, devicemapper和vfs,你可以使用-s或–storage-driver=选项来指定相关的镜像存储。在Ubuntu 14.04下,docker默认Ubuntu的aufs。在CentOS7下,用的是devicemapper,可以在下面的目录中查看相关的每个层的镜像

1
/var/lib/docker/aufs/diff/<id>

AUFS的一些特性

AUFS实现了所有Union FS的特性,把多个目录合并为同一目录,并可以按每个需要合并的目录指定相应的权限,实时的添加、删除、修改已经mount好的目录,还可以在多个可写的branch/dir间进行负载均衡
被union的目录(分支)的相关权限

  • rw表示可写可读read-write
  • ro表示read-only,如果你不指权限,那么除了第一个外ro是默认值,对于ro分支,其永远不会收到写操作,也不会收到查找whiteout的操作
  • rr表示real-read-only,与read-only不同的是,rr标记的是天生就是只读的分支,这样,AUFS可以提高性能,比如不再设置inotify来检查文件变动通知

a.术语Branch-就是各个要被union起来的目录

  • Branch根据被union的顺序形成一个stack,一般来说最上面的是可写的,下面的都是只读的
  • Branch的stack可以在被mount后进行修改,比如:修改顺序,加入新的branch,或是删除其中的branch,或是直接修改branch的权限

b.术语Opaque-就是不允许任何下层的某个目录显示出来

c.术语whiteout-一般来说ro的分支都会有wh的属性,比如“[dir]=ro+wh”。所谓whiteout的意思,如果在union中删除的某个文件,实际上是位于一个readonly的分支(目录)上,那么,在mount的union这个目录中将看不到这个文件,但是read-only这个层上无法做任何的修改,所以就需要对这个readonly目录里的文件作whiteout。AUFS的whiteout的实现是通过在上层的可写的目录下建立对应的whiteout隐藏文件来实现的

  • 在隐藏低层档的情况下,whiteout的名字是’.wh.
  • 在阻止readdir的情况下,名字是’.wh..wh..opq’或者 ’.wh.__dir_opaque’

假设有三个目录和文件如下所示(test是个空目录):

1
2
3
4
5
6
7
8
9
# tree
.
├── fruits
│ ├── apple
│ └── tomato
├── test
└── vegetables
├── carrots
└── tomato

如下mount:

1
2
3
4
# 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
# touch ./test/.wh.apple
# ls ./mnt
carrots tomato

ref

Docker基础技术:AUFS
Docker学习笔记(五)Linux AUFS


您的鼓励是我写作最大的动力

俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。