记一次将树莓派系统分区转为Btrfs的经过
背景
前段时间用吃灰的树莓派给家里的NAS整了个异地备份,为了避免系统盘因为写入寿命问题随时可能挂掉,就需要备份系统。
此前简单粗暴用dd直接全盘备份到备份盘:
# 我的系统盘设备路径是/dev/mmcblk0,备份盘挂载点是/mnt/backup
sudo dd if=/dev/mmcblk0 bs=4M | gzip -c > /mnt/backup/mmcblk0/$(date +"%Y-%m-%d-%H-%M").img.gz
但每次都是全量备份,备份盘走的树莓派的USB2.0口,速度感人,于是就想弄一个增量备份方案。
拍脑门拍出来的增量备份方案有两个:
- rsync增量备份。
- 文件系统级别的而快照和快照发送。
由于是系统盘,有各种特殊挂载,特别是为了延长Micro SD卡的寿命设置了大量的folder2ram挂载点,不知道rsync是不是不好处理。
rsync增量同步也是需要对目录和文件的差异进行对比,而快照在发送时就可以根据版本信息直接发送目标没有的数据,这么一琢磨快照应该效率更高。
快照本身的只读特性还能避免勒索病毒和误操作导致的备份失效。
综上所述,打算折腾个快照方案。一些现代文件系统都支持快照功能,比如Btrfs、ZFS等,我个人对Btrfs比较了解,就选择了Btrfs。
此帖目的在于分享经过,操作可能不是最短路径,选择的方案可能不适合所有情况。
准备
先用上面的dd指令备份一下系统,以免折腾过程中把系统整没了。
转换分区格式
Btrfs提供了btrfs-convert工具可以自动将一些主流文件系统(如Ext4)转换成为Btrfs,这个指令包含在btrfs-progs软件包中。
但要想使用btrfs-convert需要把要转换的分区卸载(umount),而系统盘可能不适合在系统运行的时候做这个操作,所以我选择把Micro SD卡拔出来插到电脑上,使用电脑的btrfs-convert来进行转换。
# Micro SD卡在我的电脑上的设备路径是/dev/sdb,一般sdb1是boot分区,sdb2就是系统盘那个分区
sudo btrfs-convert /dev/sdb2
等它转换完,sdb2就从Ext4变成了Btrfs。
迁移到子卷
Btrfs默认情况下没有子卷,数据存放在默认卷里面,按理说默认卷也可以创建快照,但是没法从快照恢复,所以最好还是创建一个子卷,把要备份的数据都放在子卷中,这样未来想要恢复也比较方便。
先挂转换好的Btrfs分区,然后进入挂载点:
# 随便找个地方
mkdir /tmp/rpi
sudo mount -o subvol=rootfs /dev/sdb2 /tmp/rpi
cd /tmp/rpi
可以用以下指令检查一下是不是Btrfs文件系统:
sudo btrfs filesystem usage ./
创建子卷:
# 我给这个子卷命名为rootfs
sudo btrfs subvolume create rootfs
Btrfs的子卷,看起来就像是一个目录,只不过它可以使用一些子卷相关的功能。
然后将数据复制到这个子卷中:
sudo rsync -aX --exclude rootfs --exclude ext2_saved --info=progress2 --info=name0 ./ ./rootfs/
因为rootfs也在这个挂载点下面,所以要排除它;另外貌似btrfs-convert创建了一个ext2_saved备份用于回滚,也需要忽略掉。
其实后来一想,早晚都要rsync的话其实也不一定要用btrfs-convert了,因为我有备份,所以可以把Micro SD卡上的系统容分区直接格式化了,然后把备份解压、挂载再rsync。也都差不多。
确认复制成功之后,再删除源数据,包括那个ext2_saved备份。
修复引导
树莓派的引导会读取 boot/firmware/cmdline.txt
中的系统盘挂载参数,原本是挂载一个Ext4文件系统,要改成Btrfs文件系统,而且要指定子卷(注意修改的是/tmp/rpi/boot/firmware/cmdline.txt
,不是当前电脑系统的boot):
console=serial0,115200 console=tty1 root=PARTUUID=fa32cc24-02 rootfstype=btrfs rootflags=subvol=rootfs fsck.repair=yes rootwait
修改rootfstype以及添加rootflags=subvol=rootfs即可。
然后要修改系统的etc/fstab,在系统启动后正确挂载系统盘(注意修改的是/tmp/rpi/etc/fstab
,不是电脑系统的/etc/fstab):
PARTUUID=fa32cc24-02 / btrfs defaults,subvol=rootfs 0 1
其实就是文件系统改为btrfs,以及添加一个subvol参数指定子卷,这样系统启动后跟目录/
实际上就是rootfs子卷的挂载点了。
一般来说修复引导都需要重建boot目录下的initramfs,但我电脑是x86的,而树莓派是arm的,可能比较折腾,所以我打算先试试能不能引导。
把卡插回树莓派,接电启动,顺利进入系统,原来不需要重建initramfs,我们成功了!
备份
我进入了树莓派的系统,在跟路径下创建了一个目录来用于放置快照。
是的,我给根目录创建快照,可以存在根目录下的目录,这在Btrfs上是没问题的。因为快照本身也是一种子卷,而Btrfs在给一个子卷做快照的时候不会连带着子卷目录下的其他子卷的数据。
当然我没有手动创建快照,而是使用了btrbk脚本来自动创建快照和将快照增量发送到备份盘上。
我的btrbk配置:
# 日志路径
transaction_log /var/log/btrbk.log
# 快照存放的目录名,我将快照放在 /btrbk_snapshots 路径下
snapshot_dir btrbk_snapshots
# 快照最少保留多少个,我设置的是最新的一个
snapshot_preserve_min latest
# 快照保留多长时间内的,我设置的是保留7天内的每天快照,保留2周内的每周快照
snapshot_preserve 7d 2w
# 快照发送目的地最少保留多少个
target_preserve_min latest
# 快照发送目的地保留多长时间内的
target_preserve 7d 2w
# 备份跟路径
volume /
# 因为跟路径挂载的就是子卷,所以子卷设置为"."
subvolume .
# 快照增量发送到备份盘上
target /mnt/backup/rootfs
可以测试一下:
# -n 参数是dry-run,只模拟,不真的运行
sudo btrbk -n run
再来个定时任务,使用系统包管理器安装的btrbk自带一个每天的定时任务,但是夜里0点执行,我打算改到早上6点,那时候我还没醒。
将timer复制到/etc/systemd/system
下,这样系统就会优先读取我自己定义的这个timer,来代替软件包默认的timer行为:
sudo cp /lib/systemd/system/btrbk.timer /etc/systemd/system/btrbk.timer
修改文件:
[Unit]
Description=btrbk daily backup
[Timer]
#OnCalendar=daily
OnCalendar=*-*-* 06:00:00
AccuracySec=10min
Persistent=true
[Install]
WantedBy=timers.target
跟timer配套的还有一个service,默认也是在/lib/systemd/system
下,但如果我不需要改动内容的话就不需要复制到/etc
下,系统在/etc
下找不到service文件的话,会自动到/lib/systemd/system
找到同名的service。