[原写于2012年2月]

需求大概是这样:在一个linux系统上,想跑多个不同应用,这些应用由不同的运维来操作,为了避免互相干扰,希望运维只能看见自己的文件,而看不见别的应用的文件信息。一个常用解决办法就是干脆装多个虚拟机,但是,虚拟机对我们来说偏“重”,比如,多个应用公用的一些动态链接库(比如 libc.so)和配置文件(比如 hosts.conf)就复制了多份,如果原先一个系统在运行时系统文件占了500M的cache,那么现在装了4台虚拟机,就有2G的cache被重复占用了。

怎样才能让系统文件只占一份cache呢?我们首先想到这么个主意:把linux系统装到ext4上,然后做4个snapshot("快照“),这4个snapshot分别mount到4个目录,4个运维chroot到这4个目录里,然后就自己干自己的,干扰不到别人的文件。由于ext4 snapshot的实现机制是让同一个物理block被映射到不同的文件系统里,所以我们觉得,这一个4k的物理block应该就只占4k的cache。

(也许有人要说,这么费劲干嘛,直接把系统常用的动态链接库做4个软链接出来,给4个运维用不就行了?这样做有两个问题,第一,动态链接库以及各种系统文件很多,不可能一一做软链接;第二,也是关键的一点,如果其中一位运维错误操作,例如覆盖写了某个系统文件,那么其他的运维就歇菜了,因为软链接实际指向的是同一个实际文件。)

于是开始考察ext4的snapshot。ext4目前是没有snapshot功能的,但是Amir Goldstein已经开发好了对应的patch,但是目前还没有被收入mainline。粗略看了一下,Amir的patch目前只支持readonly的snapshot,于是我发邮件问“如果改成writable snapshot,代码量大不大?“,Amir回帖表示代码量不大;另外还有别人回帖,推荐不用ext4而是用device mapper提供的thin provision的internal snapshot(1,2),这样就不用依赖于某一个文件系统(就是如果咱们以后不用ext4了,也可以继续做snapshot)。

鉴于ext4 snapshot不支持writable snapshot,且有7000行的改动之多,且目前都没有进mainline的计划,而device mapper的thin provision已经进了3.2 kernel,且只有5000行改动,且支持writable snapshot,所以。。。转而又考察thin provision。考察基本顺利,做snapshot没有问题,snapshot写入没问题,最后chroot然后编译kernel测试速度也没问题,但是,最后发现一个郁闷的事情:这些snapshot被mount以后,公用的文件在不同的文件系统里各自都要占一份cache,也就是说,明明是一个4k物理block,mount到4个不同的文件系统,就占4 x 4k的内存cache了!

难道是device mapper的问题?于是再试了一下ext4的snapshot,甚至btrfs的snapshot,都一样!这就是vfs的特性:只要是inode不同,即使这些inode指向的是同一个物理block,那么它们的cache都是各自独有的,不共享。

我把这个事儿告诉了coly

coly: (石化片刻)唉,我们之前想漏了,snapshot根本不能解决这个问题。。。太郁闷了,测了快两周才发现

我: 想开一点吧,还好是测出来的,而不是上线了才发现——到时候运维找过来“你们的这个方案好像不省内存啊”,然后还得解释,还得回滚,就更被动更狼狈了

coly: 噢,你这样一说,我舒坦多了

最后还是马涛同学给出了一个方案——overlay fs,能把两个目录(甭管是什么文件系统的两个目录,是目录就行)“叠合”成一个文件系统,而这个新文件系统的inode其实还是原来目录里的那个,但是视图已经是“叠合”后的了。

比如,有两个目录,其中一个目录dir1有两个文件,是:

./ab (ino:14)
./cd (ino:16)

另一个目录dir2有三个文件,是:

./apple (ino:23)
./banana (ino:27)
./lemon (ino:31)

最后用

mount -t overlayfs overlayfs -olowerdir=/dir1,upperdir=/dir2 /test/

建立的新文件系统/test/里看上去是这样:

./ab (ino:14)
./cd (ino:16)
./apple (ino:23)
./banana (ino:27)
./lemon (ino:31)

注意,inode还是那些inode,但是他们“凑一块儿了”,而且,这个新文件系统是可写的,即使覆盖写了某个文件,也只影响upperdir(例子里的dir2)的内容,而lowerdir(例子里的dir1)没有任何影响。这样,我们就可以把linux系统根目录当成lowerdir,而每个运维自己的系统当成 upperdir ,某个运维的错误操作就不会影响其他人了。

感谢马涛同学的推荐,目前这个还没有进mainline的overlay fs非常契合我们的应用。