Arch Linux 使用 BTRFS 文件系统实现分区快照

0
(0)

前言

近几个月来,笔者将日常工作和学习的笔记本电脑系统完全迁移到了 Arch Linux 上,为了防止折腾系统和软件包时发生意外(炸掉系统)影响工作学习,笔者对 Linux 上的系统备份手段进行了一些浅浅的研究。其中,利用 BTRFS 文件系统写时复制特性进行子卷快照的方案引起了笔者的兴趣,基于笔者的需求,这个方案的优点有:

  • 速度快,创建快照是即时的,可以随时根据需要保存当前文件系统状态,也不会中断工作
  • 空间占用低,快照不会一创建就占用空间,而是随着原始子卷和快照差异的增大缓慢增加,可以保持大量快照而不损失过多磁盘空间
  • 恢复便捷,只需在 /boot 中修改引导配置中的子卷名,即可启动到不同子卷(选择挂载到 / 的子卷)
  • 快照是非递归的,意味着对子卷创建或恢复快照不会影响子卷内的其他子卷(例如备份和恢复 / 时,/home 等目录不受影响),可以为不需要快照的目录创建子卷(例如家目录、Docker和虚拟机的目录),在 /etc/fstab 中配置挂载到需要的位置即可

当然,BTRFS 作为一个新兴的文件系统也有一些说法称其稳定性欠佳,笔者在数月的使用中,暂时没有遇到什么问题,认为 BTRFS 还是完全足以支撑普通日常使用需要的。

本文将简要介绍 Arch Linux 下使用 BTRFS 创建、恢复子卷快照的操作步骤,以及在无法开机时如何通过 LiveCD 恢复到上一快照等使用技巧,不涉及有关文件系统底层原理的高级话题。

安装

在 Arch Linux 中,可以在使用 archinstall 脚本安装系统时,直接选择使用 btrfs 分区,并使用其默认的子卷布局。

若需要添加更多子卷,可以在选择最佳分区结构后,再次进入选择手动分区,对默认的配置进行修改。

笔者在这里添加了一个 @docker 子卷,挂载至 /var/lib/docker 以避免创建和恢复快照时影响 Docker 容器。完整的子卷布局如下:

subvolumemountpoint
@/
@home/home
@log/var/log
@pkg/var/cache/pacman/pkg
@.snapshots/.snapshots
@docker/var/lib/docker

子卷名前的 @ 符号不是强制的,只是便于区分子卷和普通目录的标识。

安装完成后,可以通过下列命令查看存在的子卷:

sudo btrfs subvolume list /

通过 lsblk 可以看到,所有的子卷都存在于 /dev/sda2 中,它们可以共享硬盘的全部存储空间 (除 /boot 分区占用的几百M空间外)。通过 mount -t btrfs 可以看到各个子卷挂载到的位置,subvol= 后的信息为子卷路径。

注意嵌套在 @ 子卷下的 var/lib/portables 和 var/lib/machines 子卷是由 systemd 自动创建的,可以安全地删除它们用普通目录替换。如果未来需要删除 @ 子卷,建议先删除它们:

sudo btrfs subvolume delete /var/lib/portables/
sudo btrfs subvolume delete /var/lib/machines/

sudo mkdir /var/lib/portables
sudo mkdir /var/lib/machines

创建快照

创建子卷快照的基本方法为:

sudo btrfs subvolume snapshot <subvolume> <subdir>

建议将快照放置在 @.snapshots 子卷中,它默认挂载在 /.snapshots,下面的命令可以创建一个名为 @231221 的可读写的 @ 子卷快照:

sudo btrfs subvolume snapshot / /.snapshots/@231221

子卷就像一个普通的目录,可以进入查看。

我们可以测试一下,在根目录下创建一个文件,安装或删除一个软件包,稍后查看是否对快照产生了影响。

恢复到快照

快照也是一个子卷,我们只需要更改挂载到根目录 / 的子卷为刚才创建的 @231221 即可恢复到创建快照时的状态。为了挂载到不同子卷,需要修改内核启动参数,根据使用的引导加载程序不同,有不同的配置方法,下面简单介绍 grub 和 systemd-boot 的配置方法。

grub 引导配置修改

编辑 /etc/default/grub 文件,找到其中的 GRUB_CMDLINE_LINUX 行:

在其中加入参数 rootflags=subvol=/@.snapshots/@231221,保存。

修改完成后,执行以下命令更新 grub 配置:

sudo grub-mkconfig -o /boot/grub/grub.cfg

更新完成后,重启系统。

重新启动后,可以执行以下命令查看当前根目录的挂载参数:

mount | grep " / "

可以看到,已成功挂载到我们创建的快照。也可以验证确认,在创建快照时点之后所做的更改对快照都没有任何影响。/home 等目录由于位于不同子卷中,不受快照的影响,不会被还原。

systemd-boot 引导配置修改

进入 /boot/loader/entries 目录,找到主要的引导配置 conf 文件(默认的文件名可能与下图不同),编辑它。

找到其中的 options 行,同样的,加入参数 rootflags=subvol=/@.snapshots/@231221,保存。

修改完成后,直接重启系统即可。同样的,重启后可以执行 mount | grep ” / ” 检查挂载参数,确认是否挂载成功。

需要特别注意的是:如果在更新软件包时更新了内核文件(即 /boot 分区下的文件),则不能还原到此更新之前创建的快照,否则重启后可能会出现 mount: unknown filesystem type ‘vfat’ 等错误无法开机。由于 /boot 分区是单独的 FAT32 分区,快照并不会保存该分区的状态,如果需要还原,需要在创建快照时搭配其他手段备份 /boot 分区,boot 分区的大小通常在 100~200M,也不会带来过多备份压力。

无法开机时如何恢复到快照

首先,在进行破坏性操作前,执行一次快照,这里我将其命名为 @bak。

随后,直接删除当前正在挂载的子卷,模拟破坏性操作导致系统损坏时的情景。

可以看到,系统被彻底破坏,基础命令都已无法执行,甚至无法正常关机。我们直接强制重启,启动到 U 盘 archiso 镜像中。

首先,通过 lsblk 检查硬盘分区,从容量大小可以推断,/dev/sda1 是 boot 分区,/dev/sda2 是主分区。然后,创建两个文件夹用于挂载分区。

挂载上相应分区,可以看到 sda2 分区上实际的布局,与我们前面用到过的“子卷路径”相符。

若引导管理器使用的是 systemd-boot,直接参考上面的「systemd-boot 引导配置修改」一节修改 boot/loader/entries 中的 conf 配置即可。

若使用 grub,可以编辑 boot/grub/grub.cfg 文件:

在其中搜索 rootflags 找到所有匹配的位置,修改为要挂载的快照 (如 rootflags=subvol=/@.snapshots/@bak),修改完成后,直接重启,正常启动系统。

可以看到,系统已完美恢复正常运作。

若你使用的是 grub,由于 grub.cfg 是由工具生成的文件,手动进行的编辑可能会被覆盖,还需要参考上面的「grub 引导配置修改」一节编辑好 /etc/default/grub 文件,更新一下 grub 配置。

题外话

经过一番折腾,由于我们已经将系统根目录 / 使用快照子卷进行了挂载,原先的 @ 子卷可能已经没有了用处,为了节省空间,可以直接删除 @ 子卷。

由于系统上已经没有了 @ 子卷的挂载点,我们需要先使用 lsblk 找到对应的磁盘分区,将分区挂载到 /mnt,然后就可以通过 sudo btrfs subvolum delete /mnt/@ 将其删除。

快照也是子卷,此时如果我们执行 sudo btrfs subvolum snapshot / /.snapshots/@go,就是对 @.snapshots/@bak 子卷 (当前挂载在根目录 / 的子卷) 的当前状态进行了快照,快照生成的子卷为 @.snapshots/@go。同样的,不再需要的快照子卷也应该及时删除已释放空间,这个过程可以使用脚本进行管理。

为了安全起见,还可以考虑不要开机自动挂载 @.snapshots 子卷,仅在需要时挂载。可以编辑 /etc/fstab 文件,将挂载 /.snapshots 的配置项注释掉:

番外: Luks 分区解密

如果在安装系统时,使用 Luks 加密了分区,而在使用 LiveCD 救援时,又不记得快照子卷的名称需要挂载查看,可以使用以下方法挂载。

首先,在 archiso 中执行下面的命令查看加密分区:

blkid | grep crypto

找到加密分区后 (例如 /dev/sda2),使用下面的命令解密分区,将其映射为 cryptdisk (这个名称可以自定义):

cryptsetup luksOpen /dev/sda2 cryptdisk

输入密码🔑解密分区后,就可以将映射挂载到需要的位置了 (如 /mnt/root):

mount /dev/mapper/cryptdisk /mnt/root

此时就可以任意查看文件了。

参考资料

[1] SysadminGuide - btrfs Wiki - https://archive.kernel.org/oldwiki/btrfs.wiki.kernel.org/index.php/SysadminGuide.html#Snapshots
[2] Btrfs vs ZFS 实现 snapshot 的差异 - Farseerfc的小窝 - https://farseerfc.me/zhs/btrfs-vs-zfs-difference-in-implementing-snapshots.html
[3] Btrfs - ArchWiki - https://wiki.archlinux.org/title/Btrfs
[4] Unknown subvolumes on btrfs partitions / Newbie Corner / Arch Linux Forums - https://bbs.archlinux.org/viewtopic.php?id=260291
[5] debian - grub-mkconfig gives error in chroot environment - Unix & Linux Stack Exchange - https://unix.stackexchange.com/questions/558604/grub-mkconfig-gives-error-in-chroot-environment
[6] How to mount LUKS encrypted partitions manually | Linux M0nk3ys - https://evilshit.wordpress.com/2012/10/29/how-to-mount-luks-encrypted-partitions-manually/

这篇文章有用吗?

点击星号为它评分!

平均评分 0 / 5. 投票数: 0

到目前为止还没有投票!成为第一位评论此文章。

很抱歉,这篇文章对您没有用!

让我们改善这篇文章!

告诉我们我们如何改善这篇文章?

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注