水族馆

Btrfs 使用心得及布局管理

谈谈新文件系统 btrfs,新功能、新概念以及使用体验

阅读时间 2 分钟


前言

Btrfs 是 Linux 系统下的新一代文件系统。相比于传统 Ext4 类文件系统提出了诸如写时复制、子卷、快照等概念。尽管仍然可以像对待 Ext4 一样使用 Btrfs,但创造布局合理的子卷才能充分提高 Btrfs 各方面的使用效率。

前排提示:btrfs 仍在开发过程中,为了减少 bug,及更高的稳定性,请不要使用年代久远失修的祖传内核。

其实如果不考虑子卷布局,不作系统盘,仅仅用来存放资料或者当个 NAS,可以不用管子卷布局,直接 compress=zstd 挂载,然后当普通的 Ext4 用。

平铺式子卷布局

所谓平铺即 Flatten,所有子卷上层均为 subvolid=5 的根子卷。

举个例子,创建如下结构的子卷

- 根子卷 (subvolid=5)
| - mypc (子卷,挂载到 /mnt)
| - home (子卷,挂载到 /mnt/home)
| - snapshot (文件夹,不是子卷)
| | - home.backup.2021.08.25 (子卷的快照)

这么做的好处在于,可以轻松找到、删除、恢复任意子卷。因为如果要访问子卷,必须要先访问上层子卷。如果子卷中包含其他子卷,那么这个子卷是不能直接被删除的。

实际操作来说,即 mouunt /dev/sda1 /btrfs -o compress=zstd,noatime,subvolid=5 挂载默认 ID 为 5 的根子卷。这里假设 /dev/sda1 为 Btrfs 格式分区,/btrfs 可以使其他任意挂载点。

然后在根子卷中创建子卷,以常见的 Linux 系统布局为例 btrfs sub create /btrfs/mypc 创建名为 mypc 的子卷,它将挂载为 Linux 系统的根目录。

同理可以创建更多子卷(如果需要)btrfs sub create /btrfs/home 这个子卷将挂载为系统 home 目录

接着可以挂载子卷了 mount /dev/sda1 /mnt -o subvol=mypc 以及刚刚创建的 home 目录 mount /dev/sda1 /mnt/home -o subvol=home

要注意,挂载的子卷会对 第一个挂载的子卷参数 生效,即后来挂载的 mypchome 子卷都会应用 subvolid=5 根子卷的 compress=zstd 等参数。这是因为相关代码还没有实现。引用自 btrfs(5) $MOUNT OPTIONS,可前往 btrfs wiki FAQ 查阅哪些参数能够独立应用于子卷。

快照布局及恢复快照

快照属于子卷的一种,因此咱习惯将快照放在根子卷中。同时,为了方便管理,咱会在根子卷中创建名为 snapshot文件夹 (不是子卷)。

创建只读快照 btrfs sub snapshot /btrfs/home /btrfs/snapshot/home.backup.2021.08.25

如果要恢复子卷的快照,先卸载子卷 umount /mnt/home,然后删除子卷 btrfs sub del -c /btrfs/home,最后对只读快照进行快照,得到可写入的快照 btrfs sub snapshot /btrfs/snapshot/home.backup.2021.08.25 /btrfs/home 重新挂载 home 子卷即可。

如果系统存放在根子卷中

将系统存放在根子卷,这也是咱最开始的做法,目录结构如下

- 根子卷 (subvolid=5)
| - snapshot (文件夹,存放快照)
| | - backup.2021.08.24 (全盘快照)
| | - backup.2021.08.25 (全盘快照)
| - bin (Linux 文件夹,下同)
| - boot
| - dev
| - ...

这么布局没有什么问题,功能也一切正常,直到有一天咱尝试恢复全盘快照……

如果更改默认子卷,那么不仅要重新安装 grub,还要手动删除根子卷中的系统文件(因为根子卷是快照的上层子卷,不能删除根子卷),实在不够优雅。

备份子卷

快照不是备份,就算打了一万个快照,文件数据在磁盘中也只有一份。为了防止故障发生(硬盘物理损坏等),有必要定期将数据备份到外部磁盘。

使用 btrfs send /btrfs/home | btrfs receive /path/to/other/disk 将子卷发送到外部存储。还可以使用 -p 参数增量发送子卷。

要注意,如果被发送的子卷开启了压缩,管道中传输的数据是解压后的文件原数据。也就是说,发送子卷到外部存储是否会被压缩,取决于外部存储的挂载参数。

对特定目录禁用 COW

COW 即写时复制,对于数据库或虚拟机等应用可能希望禁用 COW 以提升性能。前面提到过,只有第一个挂载的子卷参数会生效,所以挂载了第一个子卷后,对其他子卷使用 nocow 参数挂载是无效的,系统不会报错,而是悄悄忽略掉 nocow 参数。

在单个文件系统中,无法使用 nodatacow 参数挂载某些子卷,而其他的使用 datacow 参数。第一个被挂载子卷的挂载参数将会应用于其他所有子卷。引用自 btrfs(5) $MOUNT OPTIONS

要单文件或目录禁用写时复制特性,请使用下面的命令:

chattr +C /path/to/file/or/folder

注意:在 Btrfs 上,‘C’ 标志应该被设置在新建的或者是空白的文件/目录,如果被设置在已有数据的文件,当块分配给该文件时,文件将不确定是否完全稳定。如果 ‘C’ 标志被设置给一个目录,将不会影响目前的目录,但在该目录创建的新文件将具有 No_COW 属性。引用自 chattr 手册页。

RAID

在单盘情况下,如果硬盘是机械硬盘等旋转类设备,Btrfs 中的元数据将以 DUP 的 RAID 等级存储。DUP 即在磁盘头部和尾部各存储一份数据,假设头部部分数据损坏,将自动使用尾部的备份进行恢复。默认情况下数据是以 single 即单份存储。

详细 RAID 等级可以使用命令 btrfs fi df 或者 btrfs device usage 查看

   $ btrfs fi df /
Data, single: total=44.01GiB, used=43.28GiB
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=2.00GiB, used=630.31MiB
GlobalReserve, single: total=91.92MiB, used=0.00B

添加/删除硬盘 btrfs device add/remove,以及转换 RAID 等级的命令 btrfs balance -mconvert=raid1 -dconvert=raid0 不再赘述。仅仅提一下:元数据和数据可以分别应用不同的 RAID 等级。

后记

Btrfs 是咱用过的最 fancy 的文件系统。Btrfs 子卷如何布局、怎么使用其实并无正确错误一说,除了扁平布局还有更多布局方案,只是一个良好的布局理念可以降低心智负担,减少操作出错概率。

用上 Btrfs 之后,zstd 压缩节省出了 25-50% 的磁盘空间,同时显著提高了机械硬盘和 U 盘等低速设备的 IO 速度。咱有一个装在 U 盘上的 Arch Linux,利用子卷功能安装了垃圾 nvidia 驱动,在开机时通过 grub 菜单选择从哪个子卷启动,解决了显卡驱动冲突的问题。子卷的在线快照和发送功能,能够让咱在线备份整个系统。ArchLiux 更新前打个快照,滚挂之后也可以轻松恢复。文件校验对于冷备份是非常重要的,不然文件坏了也不知道,Btrfs 默认开启的 checksum 校验,让咱通过 btrfs device stats 命令发现了冷备份用的硬盘出现坏道。对于咱来说,许多好处和便利的功能,都是用 btrfs 所想象不到的。

Btrfs 近年来发展得比较快,部分服务商已经开始在生产环境中使用 btrfs,win-btrfs 允许用户在 Windows 系统中使用 btrfs 中的一部分功能,但 btrfs 足够稳定了吗?没有人敢保证,只是从近年报告的案例来看,致命的 bug 应该是不存在了,更多的是某些情况下的性能问题。

这个新一代文件系统给咱带来了许多新概念:子卷、写时复制(严格来说很久以前就有了),从文件系统层面接管了压缩、RAID、用户配额等功能。未来还会加入在线文件去重、加密等新功能。对普通用户来说,结束了「文件系统里只有文件和文件夹」的时代,btrfs 的设计使用门槛可以很低,咱认为未来是很有希望下沉给普通用户使用(甚至包括 win-btrfs)。这也不禁让人好奇,还有什么基础软件设施可以带来概念性的升级,下一个文件系统革命会带来什么样的新功能,人类的文件系统将会如何发展(笑。

参考

  1. btrfs - ArchLinux wiki 简体中文
  2. System admin guide - btrfs wiki
  3. Using Btrfs with Multiple Devices - btrfs wiki
  4. btrfs(5) $MOUNT OPTIONS
  5. FAQ - btrfs wiki
  6. Getting started - btrfs wiki
  7. Btrfs status - btrfs wiki