Pony of Shadows

Queen of Truth & King of Practice


用Btrfs为根分区创建快照

得益于Btrfs的CoW特性,它创建快照的开销可以非常小,所以我觉得它非常适合作为根分区的文件系统。

子卷(subwolume)

“A Btrfs subvolume is not a block device (and cannot be treated as one) instead, a Btrfs subvolume can be thought of as a POSIX file namespace.”

  1. 最顶层的子卷
  • ID: 5
  • 无法被删除,无法被替换
  • 路径: “/”
  1. 其它子卷
  • 挂载在顶层子卷的下方
  • 可以在文件系统中移动路径,但ID不变

使用子卷

  1. 创建一个子卷
btrfs subvolume create /path/to/subvolume

可以像mkdir一样使用--parents参数 2. 列出一个路径下的所有子卷

btrfs subvolume list -p path
  1. 删除一个子卷 btrfs subvolume delete /path/to/subvolume

分区的储存空间还够吗?

一般的系统工具不会给出Btrfs分区的准确用量信息。需要用下面的命令:

# as superuser
btrfs filesystem usage /
# as normaluser
btrfs filesystem df /

使用快照备份和恢复根分区

参考

如果已经用了顶层子卷作为根分区怎么办

如果你已经把顶层子卷当成了系统根路径,要知道顶层子卷是唯一特殊的,无法通过重命名的方式替换,请通过一下步骤改用次级子卷作为根分区:

  1. 修改配置重新生成GRUB,以及修改@current子卷下的fstab
  • /etc/default/grub中找到GRUB_CMDLINE_LINUX这一行,改为
GRUB_CMDLINE_LINUX="rootflags=subvol=/@current"
  • 重新生成GRUB:
sudo grub-mkconfig -o /boot/grub/grub.cfg
  • 修改fstab
# 比如这是原来的根目录挂载
UUID=*******	/         	btrfs     	rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/	0 0
# 现在改成
UUID=*******	/         	btrfs     	rw,relatime,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/@current	0 0
  1. 生成顶层子卷的快照(命名为@current), 然后重新启动系统
sudo btrfs subvolume snapshot / /@current

可以这样列出所有子卷

sudo btrfs subvolume list /
  1. 最后挂载顶层子卷并删除其中除了@current以外所有内容
sudo mount /dev/nvme0n1p3 /mnt
## 如果挂载的不是顶层子卷,用-o选项,指定subvol的值为快照子卷的路径,或指定subvolid的值为快照子卷的ID

sudo chatter -i /mnt/etc/resolv.conf
rm -rf /mnt/[a-z]* 
ls /mnt

创建快照的脚本

#!/bin/bash
system_root_device=$(findmnt -n -o SOURCE /)
system_root_device=${system_root_device%%[*}
snapshot_date=$(date +'%Y-%m-%d_%H:%M:%S')

mount -m "$system_root_device" /mnt/@top_subvol

btrfs subvolume snapshot / "/mnt/@top_subvol/@snapshot$snapshot_date"
snapshots=( $(ls -dr /mnt/@top_subvol/@snapshot*) )
echo "${snapshots[@]}"
if [[ ${#snapshots[@]} > 2 ]]; then
    for snapshot in ${snapshots[@]:1}; do
        btrfs subvolume delete "$snapshot"
    done
fi

umount /mnt/@top_subvol
rmdir /mnt/@top_subvol

在对根分区执行有风险的操作之前记得执行这个脚本哦

使用钩子让pacman在更新前调用创建快照的脚本

创建文件/etc/pacman.d/hooks/update_snapshot.hook 内容如下:

[Trigger]
Operation = Upgrade
Type = Package
Target = *

[Action]
Description = Creating Btrfs snapshot of / before upgrade...
When = PreTransaction
Exec = /home/poshd/all/local/bin/update_snapshot

如果@current子卷损坏,无法正常启动系统,怎么办?

  • 开机一般会自动进入救援模式 如果不放心,可以像这样确定自己的系统确实支持btrfs文件系统
# lsinitcpio /boot/initramfs-linux.img | grep btrfs
usr/lib/modules/6.9.5-arch1-1/kernel/fs/btrfs/
usr/lib/modules/6.9.5-arch1-1/kernel/fs/btrfs/btrfs.ko.zst
usr/bin/fsck.btrfs
usr/lib/udev/rules.d/64-btrfs.rules
  • 挂载根分区硬盘的顶级子卷
  • 重命名@current为@abandoned
  • 重命名最新的快照为@current
  • 重启进入linux系统,确认恢复到快照,则挂载顶级子卷后用btrfs命令删除@abandoned。