Btrfs文件系统已经逐渐被各种Linux发行版本支持(作为系统分区格式),Btrfs具备CoW(写时复制)的特性,相比于之前的很多文件系统增添了很多特殊的功能,本文对其中的常用功能进行了介绍。 但是由于文件系统操作不当容易丢失数据,操作之前记得做好额外备份。
写时复制
写时复制(Copy-on-write, CoW)指了在多个调用者请求相同资源时,只有在某个调用者试图修改资源的内容时,系统才会为其复制一份专用副本。这样没有写操作的时候,就不会有多余的副本被创建。1
CoW的缺点之一在于对于像VM镜像、数据库文件这样的就地更改(updated-in-place)的文件,会导致写入碎片化。2所以对于这一类数据,不妨建一个子卷然后禁用CoW来储存他们。(别忘了修改fstab
)
Btrfs默认启用写时复制,要停止使用写时复制,使用nodatacow
选项,但是这一更改只会影响新创建的文件,对于已有文件(夹)使用下列命令进行修改,但仍存在一些细节问题,使用前务必参见参考资料中关于此节的详细描述3。
chattr +C </path/to/file/or/folder>
子卷(Subvolume)4
Btrfs通过Subvolume来实现在备份时排除某些文件夹。
挂载子卷
通过设置挂载的选项可以挂载指定的子卷:
mount -o subvol=<subvol> <device> <mount_path>
子卷的修改操作
列出子卷
btrfs subvolume list -p path
使用后会列出对应path
下的所有子卷,其数量可能会很多,因为所有的快照也以subvolume的形式储存,有意思的是Docker镜像也被保存为了subvolume:
创建子卷
btrfs subvolume create </path/to/subvolume>
这里的
path
指的是子卷的绝对路径,比如当前挂载了@
到/mnt/@
目录下,则使用路径/mnt/@/home
创建出来的子卷为@/home
。
删除子卷
btrfs subvolume delete /path/to/subvolume
如果只移除文件目录,而不使用
btrfs subvolume delete
命令并不会真正删除一个子卷。
默认子卷
# 获取默认子卷
btrfs subvolume get-default /
# 设置默认子卷
btrfs subvolume set-default <subvolume-id> /
临时挂载
# 使用路径挂载
mount -t btrfs -o subvol=<subvolume> </mount/point>
# 使用id挂载
mount -t btrfs -o subvolid=<id> </dev/device> </mount/point>
Btrfs子卷组织形式的探究5
在openSUSE中查看当前的Btrfs的子卷,可能会显示大量的子卷,因为snapshot实际也是通过子卷来实现的,另外值得注意的是Docker镜像也被作为snapshot独立开了:
~$ sudo btrfs subvolume list /
ID 256 gen 90 top level 5 path @
ID 257 gen 113574 top level 256 path @/var
...
ID 263 gen 113574 top level 256 path @/home
ID 266 gen 112569 top level 256 path @/.snapshots
ID 298 gen 98293 top level 257 path @/var/lib/docker/btrfs/subvolumes/ce11ad5... # docker镜像
...
其中@
代表了文件系统的根(rootfs),但事实上它也仍然是一个snapshot,最顶层的卷是以0为标号的子卷,不过通常不使用。
同时默认的/
同样也不是@
子卷,一般也是某一个子卷,只是默认被挂载为了/
,通过查看默认子卷可以得知:
~$ btrfs subvolume get-default /
ID 267 gen 113599 top level 266 path @/.snapshots/1/snapshot # 一个snapshot被作为默认子卷,挂载为了文件系统的 `/` 目录
可见当前系统的/
实际上是一个路径为@/.snapshots/1/snapshot
的子卷,真正的@
在openSUSE中是隔离开的,作为独立的根来储存需要永久保存的子卷。
创建子卷的正规步骤
正如上述讨论,由于目前的系统目录也是一个(临时)快照。
如果我们此时要创建一个子卷,不可以建立在一个一个已有的快照下,否则在进行rollback操作后就不能再删除这个子卷了。正确的操作因该是将这个子卷建立在@
子卷下。
sudo mount /dev/sda2 -o subvol=@ /mnt
sudo btrfs subvolume create /mnt/usr/important
sudo umount /mnt
快照
Btrfs的快照是建立在其“写时复制”的功能基础上的。 创建快照可以使用如下命令:
btrfs subvolume snapshot </path/to/source> </path/to/dest>
对于openSUSE,目标目录通常为
/.shapshots
,这一目录为默认的统一存放快照的目录。 另外添加参数-r
可以创建只读快照,在只读快照上再创建一个快照可以获得只读快照的一个可写版本。
注意快照不是递归包含的,意味着子卷里的子卷在快照中会是空目录。
这也是为什么openSUSE下部分目录被排除在默认的snapper备份之外:它们都被创建为了额外的子卷,由于上述非递归性,他们在对/
创建的快照中均被忽略了。
Btrfs启用压缩6
在openSUSE中是支持Btrfs的压缩功能的,通过mount
的参数可以启用压缩:
mount -o compress </dev/sdx> </mount/point>
compress
的默认规则是:如果你创建了一个文件,Btrfs压缩后发现压缩率低,那对于之后的写入它都不再会进行压缩。如果不希望这样,可以使用compress-force
。
对于已经写入的文件,均不会被压缩,压缩仅对新写入的文件有效。
压缩有三种算法可选:
- lzo:压缩率低但是CPU资源占用少。
- zlib:压缩率高但是资源占用多。
- zstd:旧版本内核和
GRUB
引导对其缺乏支持,暂时忽略。
在fstab
中永久启用压缩,并指定压缩算法(算法以不指定):
UUID=1a2b3c4d /home btrfs subvol=@/home,compress=lzo 0 0
使用snapper进行管理7
snapper
通过一系列的配置来管理Btrfs分区,配置文件默认位于/etc/snapper/configs/
下。
默认的方案只为/
创建快照,且内容还要排除名下的子卷。
创建一个新的配置
sudo snapper -c <config-name> create-config </path>
这一操作会创建一个快照并从/etc/snapper/config-templates/default
获取一套默认配置。
配置快照的设置
见openSUSE相关文档的对应章节,以获得更准确的信息。
使用snapper -c home set-config "<KEY>=<value>"
来修改设置。