记一次将树莓派系统分区转为Btrfs的经过

记一次将树莓派系统分区转为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口,速度感人,于是就想弄一个增量备份方案。

拍脑门拍出来的增量备份方案有两个:

  1. rsync增量备份。
  2. 文件系统级别的而快照和快照发送。

由于是系统盘,有各种特殊挂载,特别是为了延长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。

完结

1 个赞

太详细了佬

后来想了想,是不是可以完全不使用rsync,而是直接创建一个根卷的可写快照,然后再把根卷的数据删掉……

感谢分享。

请谨慎使用 Btrfs。

Btrfs 的优点有很多,例如 透明压缩 写时复制 快照 以及不需要进行 fsck。

但是我之前遇到了一次元数据满了导致无法写入的问题。直接一片io错误。尤其你的系统分区如果用btrfs,在遇到这个问题后整个系统会陷入io error。自救都难…

我的想法是,利用 btrfs 做透明压缩和去重/快照的存储用的文件系统即可,用做系统的文件系统那就得确保这块盘不会被写满了。

系统分区我还是选择 ext4 和 xfs。硬盘性能强我用xfs,硬盘性能弱我用ext4。

inode写满应该是极端情况了吧,意味着有远超绝大部分使用场景的文件数量,比如某些需要极大量极小文件的使用场景。
传统文件系统如Ext4和XFS的inode表大小是随格式化固定的,可以在格式化的使用使用-i参数来自定义,默认的话Ext4是每16KB存储空间分配一个inode配额,XFS是按照文件系统大小的百分比来分配给inode表,1TB以下的文件系统默认为25%、50TB以下的默认值为5%、50TB以上的默认值为1%。而现代文件系统如Btrfs和ZFS是动态大小的inode表,理论上来说现代文件系统应该比传统文件系统在inode容量上更具有健壮性。

如果使用Btrfs依然遇到了inode用满,一种原因可能真的是磁盘空间用满了,这在使用任何文件系统都是应该尽量避免的(现代文件系统也可以使用配额功能来做一下限制,避免影响其他存储空间);
另一种原因可能是系统内核版本过低对Btrfs支持不足,因为现代文件系统的可用空间统计是与传统文件系统完全不一样的,特别是开启了压缩、快照、配额组、重复数据删除等特性之后,表征存储数据量甚至可能会与超过磁盘实际存储大小(这也是为什么df指令会输出错误信息),如果操作系统不能正确读取可用空间,就可能会导致误认为文件系统已满,乃至误以为无法再写入inode(元数据已满)。

我文章里使用Btrfs的核心需求是利用快照功能进行备份和恢复,也就是说我就是奔着系统盘随时会崩的情况去的(闪存颗粒的写入寿命问题),所以当备份机制已经在运转,就不怕它崩了,可以随时从备份盘恢复数据。在我现在的主力工作机上,我甚至使用Btrfs的快照和快照发送功能实现了个类苹果时间机器功能,而且已经实际进行过恢复操作。

Btrfs在最近几个Linux内核的minor版本更新已经比较稳定,由修复bug逐渐变为了优化性能。实际上我使用Btrfs已经至少有三四年的时间了,使用这个文件系统的设备也有很多,但还没有遇到过因为文件系统本身的缺陷导致的问题。所以至少从现在往后的时间节点来说,用Btrfs不会比使用其他文件系统风险更高(除了RAID,目前还是试验状态)。

但话说回来,如果你不打算使用备份来保障数据完整性,而是通过直接对硬盘进行数据恢复来解决,那么现代文件系统因为其存储复杂性肯定也是会让数据恢复变得更复杂,这时候使用传统文件系统会更好。不过作为一个网安从业者,我个人还是建议以备份作为保障数据完整性的核心手段。

可是 Arch Linux CN 的群友大部分都只在 btrfs 遇到了元数据存满,硬盘还有空余空间,导致系统被ro只读。理论来说ext4等传统文件系统用户更多,更应该被频繁报告提问的啊,可是我却没有见到过提问报告的。

我一般用 Linux 只用 Arch Linux,我去看下我内核版本。


Arch Linux 的 6.9.9 内核版本应该不低了吧

以及在互联网上进行相关内容搜索:
btrfs:有较多相关内容

xfs:有相关内容

ext4:未搜索到相关内容

不出问题的不会去网上问,所以有更多使用没有问题的是不会被人了解到的。

你自己也说了是磁盘空间用满了,这个无论是任何文件系统都是应该尽力避免的事情,只是现代文件系统更因为动态inode表大小,报的错误不是文件系统满而是inode满了,会让人觉得是inode的问题,实际上是整个文件系统空间都不够了。

没有的,我之前那次btrfs元数据问题发生时的硬盘可用空间还有3.7GB,但是元数据只剩60+MB了。

做平衡失败了很多次,最后加了块云硬盘再次尝试做平衡,做完删除云硬盘才解决问题。

这种搜索无意义,因为搜“ext4 inode run out”也能搜出来很多内容。

好的,只是至少目前我的使用体验是在 btrfs 快满时,容易发生元数据问题。从而系统提前只读。
而ext4和xfs我都可以用到最后1M字节。

只是说我确实亲历的一次 btrfs 的故障。并跟你们提个醒。

以及解释一下,跑 Docker 的话很容易产生海量小文件。这点我也没啥好办法…

实际上在我用 Arch Linux 作为桌面系统的那段时间,我也一直用的 btrfs,也一直正常工作。什么错误都没有。

但在我将其用于 10G 硬盘的 VPS 上时,跑了几个 Docker 他就出现元数据错误了。之前这台机器用的是 ext4,并未发生此问题。

只能说避免将 btrfs 用满吧。

我个人习惯于有问题去hack以下,这也是Linux比较吸引我的一点。

mkfs.btrfs有个参数是-M

-M|–mixed
Normally the data and metadata block groups are isolated. The
mixed mode will remove the isolation and store both types in
the same block group type. This helps to utilize the free
space regardless of the purpose and is suitable for small
devices. The separate allocation of block groups leads to a
situation where the space is reserved for the other block
group type, is not available for allocation and can lead to
ENOSPC state.

The recommended size for the mixed mode is for filesystems
less than 1GiB. The soft recommendation is to use it for
filesystems smaller than 5GiB. The mixed mode may lead to
degraded performance on larger filesystems, but is otherwise
usable, even on multiple devices.

The nodesize and sectorsize must be equal, and the block
group types must match.

    Note
    versions up to 4.2.x forced the mixed mode for devices
    smaller than 1GiB. This has been removed in 4.3+ as it
    caused some usability issues.

默认情况下inode和数据是隔离的,所以就会导致最后那一点空间无法分配给inode,对于希望将空间用尽的情况下可以使用这个来取消隔离,这样没准可以解决你的问题。

1 个赞

好的,感谢热佬耐心解答 :smiling_face_with_three_hearts:

1 个赞

刚开 tg,又发现 archlinuxcn 群里有人问关于 btrfs 的问题了 :rofl:

硬件问题就换盘(SSD坏块),文件系统的问题就修复文件系统,btrfs的superblock是冗余的,默认会有四个副本,无法修复又要抢救数据的话可以选择其他的副本。

From 配置调优 to 开发调优