aufs挂载例程
由于设备需要能回复出产设置功能,以前都是将最原始的文件都备份一份, 但是目前存在一个问题:系统升级后只是修改了一小部分文件,很多内容没有修改,这样就导致备份的大多数内容其实在浪费空间;那能不能和fork 那样做到cow呢?也就是升级的时候你要修改此文件,我才去备份!这样节约存储空间。so可以参考docker的aufs文件系统来设计自己的系统数据盘
mount -n -t aufs -o br:/fp/modified/root=rw :/fp/update/root=ro+wh :/fp/orig/root=rr,noatime unionfs /union_root
当需要修改一个文件,而该文件位于低层branch时,顶层branch会直接复制低层branch的文件至顶层再进行修改,而低层的文件不变,这种方式即是CoW技术(写复制),AUFS默认支持Cow技术。
当删除一个低层branch文件时,只是在顶层branch对该文件进行重命名并隐藏,实际并未删除文件,只是不可见,这种方式即AUFS的whiteout(写隐藏)。
写时复制(copy-on-write,常被简写为 CoW),也叫隐式共享,是一种提高资源使用效率的资源管理技术。它的思想是:如果一个资源是重复的,在没有对资源做出修改前,并不需要立即复制出一个新的资源实例,这个资源被不同的所有者共享使用。当任何一个所有者要对该资源做出修改时,复制出一个新的资源实例给该所有者进行修改,修改后的资源成为其所有者的私有资源。通过这种资源共享的方式,可以显著地减少复制相同资源带来的消耗,但是这样做也会在进行资源的修改时增加一部分开销
aufs文件系统一旦挂载,只能对挂载联合文件系统的目录进行操作,不能对其他用于合并的目录进行操作。节省空间:AUFS 的 CoW 特性能够允许在多个容器之间共享分层,从而减少物理空间占用。
- 查找文件:AUFS 的找性能在层数非常多时会出现下降,层数越多,查找性能越低。
- 性能:AUFS 的 CoW 特性在写入大型文件时第一次会出现延迟。
$ tree .
├── dir1
│ ├── a
│ └── b
├── dir2
│ ├── b
│ └── c
└── mnt
mount -t aufs -o dirs=./dir1:./dir2 none ./mnt $echo hello > ./mnt/b $ cat ./dir1/b hello $ cat ./dir2/b
我们向dir1和dir2都同时拥有的文件b中写入了一个test,上层目录dir1中的b的内容被修改为了test,而下层目录dir2中的b 依然是空的。所以修改unionFS中的文件,只会修改最上层的文件,这就是aufs的一些简单的功能
aufs联合文件系统在用在制作系统盘时的主要作用有2个,系统升级和系统恢复。
系统升级主要是更新系统。系统恢复主要是当系统不正常或误删系统文件的时候,可以将系统恢复正常和恢复rootfs误删的系统文件。
系统升级:
当我们需要进行升级的时候,通过将升级的系统文件解压到update层和modified层来实现升级,解压到modified层是为了立即生效,解压到update层是为了备份升级内容。
系统恢复:
恢复到打了升级包的状态:
场景:系统出问题,需要恢复系统,但是想保留之前打的所有升级包内容,包括修复的漏洞,解决的bug等。
方案:通过删除aufs文件系统中modified层中root和nsfocus目录中的所有内容后,重启,则恢复到了升级后的版本
恢复出厂设置:
场景:系统出问题,需要恢复系统,直接恢复到出厂设置。
方案:通过删除aufs文件系统中modified层和update层中root和nsfocus目录中的所有内容后,重启,则恢复到了出厂设置。

mount -n -t aufs -o \ br:/fp/modified/root=rw\ :/fp/update/root=ro+wh\ :/fp/orig/root=rr,noatime unionfs /union_root /bin/mount -n -t aufs -o remount,udba=notify,noatime,noxino unionfs /union_root mount -n -t aufs -o \ br:/fp/modified/fpwork=rw\ :/fp/update/fpwork=ro+wh\ :/fpwork=rr,noatime unionfs /union_root/fproot/fpwork /bin/mount -n -t aufs -o remount,udba=notify,noatime,noxino unionfs /union_root/fproot/fpwork
目前存在一个问题:由于orig层使用的是rr 权限挂载,所有修改文件权限就会触发COW; 就会将问价copy 一份到modify中去
Ubuntu 系统默认已经安装了 aufs,对应的安装包是 aufs-tools
。下面我们就做一个简单的试验,看看 aufs 具体的样子。
工作目录可以随便选择,后面的操作都是在这个目录进行的。首先创建三个子目录:
base
作为底层的目录top
作为上层的目录mnt
: aufs 使用的挂载点,会把上面两个目录挂载到这里
然后创建几个文件,如下:
➜ tree . ├── base │ ├── common.txt │ └── hello.txt ├── mnt └── top ├── common.txt └── foo.txt
接下来使用 aufs,把 base
和 top
一起 mount 到 ./mnt
目录:
➜
sudo mount -t aufs -o br=./top:./base none ./mnt
在 aufs 中,base/
和 top/
被称为 branch,它们就是源目录。
这个 mount 命令的参数意义是这样的:
-t aufs
:mount 的文件类型,使用的是 aufs-o
:传递个 aufs 的选项,每个文件类型的选项不同br
:表示 branch,也就是 aufs 需要的的各个目录none
:这个本来是设备的名字,但是我们并没有用到任何设备,只会用到文件夹,因此这里为 none./mnt
:挂载点,也就是内容最终出现的目录
默认情况下,-o
后面的第一个目录是以可读写模式挂载的,剩下的目录都是只读模式(和 docker 容器模型非常一致)。
查看挂载好之后的组织形式,发现 ./mnt
中出现了原来两个文件夹的综合内容,其中 common.txt
文件选择的是 top/
文件夹的。
➜ tree . ├── base │ ├── common.txt │ └── hello.txt ├── mnt │ ├── common.txt │ ├── foo.txt │ └── hello.txt └── top ├── common.txt └── foo.txt ➜ cat mnt/common.txt top
如果要修改 common.txt
文件,会发现只有 top
目录对应的内容发生了变化,base
下面的内容会保持不动:
➜ echo changed > ./mnt/common.txt ➜ cat top/common.txt changed ➜ cat base/common.txt base
这是因为 aufs 会逐层去查找文件,发现最上层存在文件 common.txt
并且是可写的,就会直接操作这个文件。类似的,如果是修改 foo.txt
也会直接反应在 top/
目录里面。
但是如果我们想要修改 hello.txt
文件,和预期不一样的是,base/hello.txt
并没有变化,而是新建了一个 top/hello.txt
文件,所有的操作都是在这个文件进行的。实验结果如下:
➜ echo hello, world > mnt/hello.txt ➜ tree . ├── base │ ├── common.txt │ └── hello.txt ├── mnt │ ├── common.txt │ ├── foo.txt │ └── hello.txt └── top ├── common.txt ├── foo.txt └── hello.txt
这是因为,aufs 从上往下查找文件,虽然在 base/
中发现了 hello.txt
文件,但是这个 branch 是以只读的方式挂载的,所以 aufs 并不能直接修改它,而是把它拷贝一份到上层,并对这个拷贝进行修改。
当然我们可以在 mount 的指定每个 branch 的读写模式,比如把两个 branch 都以可写的方式挂载:
➜
sudo mount -t aufs -o br=./top=rw:./base=rw none ./mnt
那么修改文件的规则会发生一些变化,文件查找还是从前到后,但是一旦发现文件,就能直接修改这个 branch 的文件内容,而不需要进行拷贝了。具体的实验就不做了,操作也非常简单,读者可以自行完成。
可以指定的权限一共有三种:
rw
:可读可写,用户能直接修改这个 branch 的文件内容ro
:只读,用户不能通过 aufs 的接口对文件进行写操作,只能读取里面的内容rr
:real read only,底层的文件本来就是只读的(这种情况比较少见),这种情况下,aufs 就不用担心文件不通过它的接口被修改的情况
除了读写模式之外,还有一个重要的属性——whiteout
。
通过 aufs 指定的读写模式,只有用户通过最终的挂载点访问才有效,如果用户绕过挂载点,直接修改原来的文件,aufs 应该怎么处理呢?这个行为是由一个参数控制的,udba
(全称是 User Direct Branch Access),这个参数有三个可选值:
udba=none
:aufs 不会进行任何数据同步的检查,因此性能会高一点,但是可能会出现数据不一致的情况。如果用户能保证文件不会直接被修改,或者对文件内容一致性要求不高,可以使用udba=reval
:aufs 会检查底层的文件有没有改动,如果有的话,把改动的内容更新到挂载点。这个性能会导致 aufs 产生额外的性能损耗udba=notify
:通过 inotify 监听底层的文件变化,基于事件驱动,能够减少第二种方式的性能损耗
说了这么多,可以看出来其实 aufs 最核心的功能还是那句话:把多个目录合并成一个目录,让用户决定在操作统一的文件系统。虽然看起来很有趣,那么 aufs 有哪些实际的用处呢?当然它被我们提起是因为 docker 可以用它来保存镜像和容器,但是 aufs 出现的时间要比 docker 长很多,它常见的用法包括:
- Linux 光盘演示和教程,录制了 Linux 的光盘可以用来让用户体验,但是光盘的内容是只读的,可以通过 aufs 把光盘和 U 盘或者磁盘 mount 到一起,用户对文件的修改保存到后面的存储上
- 如果系统上因为各种原因,不同用户的 home 目录保存在不同的路径和磁盘上,可以通过 aufs 把它们 mount 到一起,统一进行操作