docker在ubuntu下使用aufs作为分层技术,而在centos等非debian系的则使用devicemapper,因为我们都使用ubuntu,这个devicemapper我们暂时不涉及,以后有机会再看。 AUFS全称为advanced multi layered unification filesystem,也就是【高级多层级联合文件系统】,听起来很高端,实际上就是把多个文件夹合成一个,使外部不需要去关注多个的操作。 下面我们先来了解一下aufs的合并的相关知识:

  • 多个文件夹mount为一个

假设我们有两个文件夹,并且文件夹内部各有两个文件。 如下,执行tree命令:

├── ipad
│   ├── ios
│   ├── ipad air2
│   └── ipad pro
├── iphone
    ├── ios
    ├── iphone6
    └── iphone6s

我们有两个文件夹,一个是ipad,里面有ios, ipad air2和ipad pro三个文件, 另外一个是iphone,里面有ios, iphone6和iphone 6s三个文件。 接下来我们希望把这两个文件夹放到一个文件夹里面去。我们可以执行以下的操作:

mkdir mnt#创建mnt目录
sudo mount -t aufs -o dirs=./ipad:./iphone none ./mnt#这里的none是指不使用设备加载

执行完成操作后我们可以看到:

> tree
mnt
├── ios
├── ipad air2
├── ipad pro
├── iphone6
└── iphone6s

我们看到只有一个ios,至于如果修改它会发生什么事情,我们接下来继续看。

  • 对文件夹进行操作

上面我们已经把多个文件夹mount为一个了,mount操作当然是为了方便操作了,假设我们修改其中一个文件,如:

echo "ipad air2" > "mnt/ipad air2"
cat "mnt/ipad air2"
ipad air2
cat "ipad/ipad air2"
ipad air2

我们看到ipad目录和mnt目录的ipad air2的内容都被修改了,这说明加载成功后的目录是可以进行修改操作的。真的这样? 我们再来看下面的操作:

echo "iphone6" > mnt/iphone6
cat mnt/iphone6
iphone6
cat iphone/iphone6

我们发现iphone文件夹下的iphone6并没有跟随mnt下的iphone6修改,这说明加载后的目录并不是都可以修改的。那至于怎么修改呢,我们等下再看。

上面我们看到两个文件夹里面都有ios,但mount之后变为一个了,那怎么办,改这个究竟是改了哪个? 而根据我们前面修改了ipad文件夹内容可以同步修改,而iphone的却不行,我们大胆猜测,这个修改的是ipad的内容,那来测试下:

echo "current ios9.1" > mnt/ios
cat mnt/ios
current ios9.1
cat ipad/ios
current ios9.1
cat iphone/ios

看样子跟我们的猜测也很符合,我们修改了mnt的ios文件内容,却只有ipad的Ios内容被修改,而iphone的不变。但这并不足以证明我们的猜测。

因为我们前面进行的都是修改操作,假如我们新建呢?那新建的文件夹跑谁哪里去?

echo "ipad mini2" > "mnt/ipad mini2"
cat "mnt/ipad mini2"
ipad mini2
cat "ipad/ipad mini2"
ipad mini2
cat "iphone/ipad mini2"
cat: iphone/ipad mini2: 没有那个文件或目录

输出结果很明显证明了我们创建的文件在ipad目录内。

实际上当我们执行mount操作,但不执行读写规则的时候,默认第一个指定的目录为可写,即rw,而之后的目录为只读,即ro。 因为上面我们的ipad为第一个目录,iphone为第二个,所以ipad的目录和文件为可读可写,而iphone的目录为只读。

  • 加载方式

前面我们默认mount的时候,第一个为可读写,之后的为只读,那我们如果想要所有都可读写,怎么办? 很简单,mount支持指定方式,前面的操作有新建一些文件,先删除恢复到原始状态。

sudo mount -t aufs -o dirs=./ipad=rw:./iphone=rw none ./mnt

这里指定了ipad和iphone文件夹都通过可读写的文件加载,那么我们再试一下前面的操作:

echo iphone6 > mnt/iphone6
cat mnt/iphone6
iphone6
cat iphone/iphone6
iphone6
cat ipad/iphone6
cat: ipad/iphone6: 没有那个文件或目录

这一次我们看到修改了iphone6文件,同时iphone文件夹下的也跟着修改了。说明iphone文件夹由之前的只读变为现在的可读写。 新建文件的,大家可以自己试下。(提示:由于文件夹都是可写的,所以哪个在前就操作哪个。)

再回到前面的ios文件的问题,前面因为只有一个可写,很容易理解,而这次是两个都可写,那怎么办?

echo "ios for ipad" > mnt/ios
cat mnt/ios
ios for ipad
cat ipad/ios
ios for ipad
cat iphone/ios

我们看到修改mnt中的Ios内容后ipad内的ios内容同步修改了,跟前面的结果一样。但这里的原因肯定跟上面不一样,因为两个文件夹都可写。造成这样的现象是因为aufs会识别在最前的文件夹,新建,修改共同文件等操作会作用于最前面的文件夹。在这里即是我们的ipad文件夹。大家有兴趣可以把文件夹顺序调换一下看看结果。

docker的layer技术实际就是运用了这样的原理,把各个文件夹一层层叠加,每一层都是一个diff,即做出的修改,最底层的diff拥有所有初始镜像的内容。docker的layer只有最上层可以修改,其他都是只读的。

我们公司现在的实践是所有人都共用一个镜像,这样对于维护会方便点,有一个新镜像,再打tag即可,而不需要重复去做commit。——而这种做法的坏处有一个:假设我和另外一位同事都需要commit镜像,并且commit为同一个镜像名称,那么在commit的过程中,如果我先commit完成,之后他再commit,那么我做的修改将会全部丢失。这实际上就是因为每个人的修改都保留在自己的容器层内,别人做的修改对自己是不可见的,所以commit为同一个镜像,就会出现丢失的情况。

但每一个做法都有利有弊,共同同个镜像:减少占用硬盘空间,大家都共用一份;用不同镜像:commit的影响可以减少。但基于我们的使用场景,我们选择使用同个镜像,因为其他同学维护镜像的频率不高。

此篇参考陈皓的——aufs介绍文章,有兴趣的同学可以去看看:http://coolshell.cn/articles/17061.html