折腾了一下 neptune 上的 ZFS

我一直是非常反对重装系统的。从技术上说,今天的折腾并不算是重装系统,不过因为把机器上所有的数据(是的,文件系统全部都拆掉重建了)都重写了一遍,所以还是算做了一次吧。

缘起
在采购 家里的路由器 的时候,选择了 WD 的 AV-25【1】 系列硬盘。我选的那款硬盘使用的是新式的 AF (4kiB扇区)格式。

FreeBSD 使用的主流文件系统 UFS 和 ZFS,以及 ahci(4) 驱动都 直接支持 4kiB 扇区。但是,目前市面上的AF硬盘,为了与先前的 BIOS 和操作系统(主要是 Windows XP)兼容,对于 ATA IDENTIFY 的回应,原先返回扇区尺寸的位置变成了逻辑扇区尺寸,这种做法俗称512e,即硬盘通过固件或其他方式模拟山区尺寸为512字节,并处理相关的回写操作。

以512字节为单位进行读写时,在AF格式的硬盘上是低效的。FreeBSD的 ahci(4) 驱动和对应的 ada(4) 驱动会设置 stripesize 以反映驱动器采用的实际物理扇区尺寸,但文件系统并不直接识别这个尺寸。

对于 ZFS 而言,其扇区尺寸是在创建时以 ashift 值写死的,目前在命令行没有办法指定这个值,也不能在创建 ZFS 之后修改。如果修改内核令其使用 GEOM 的 stripesize 来产生 ashift,对 AF 硬盘则会出现内核得到的 ashift 比先前已经存在的 ashift 大,从而导致 ZFS 无法识别的问题(如果创建 ZFS 时已经使用了更大的 ashift 则没有关系)。因此,必须想办法让 ZFS 在创建时就知道扇区尺寸是 4KiB。

FreeBSD 5.3-RELEASE 时新增了一个调试用的 GEOM class ---- gnop。可以用它来封装其他 GEOM 对象,并改变扇区尺寸,方法是 gnop create -S 4096 /dev/gpt/store (此处 /dev/gpt/store 是一个按 4k 对齐的 GPT 分区的 label)。gnop会产生一个新的设备节点,/dev/gpt/store.nop,其向系统汇报的扇区尺寸是我们指定的 4096 字节,而不是驱动器汇报的逻辑扇区尺寸 512 字节。

使用这个设备节点创建的 ZFS 就会采用正确的 ashift 值了。

使用 zdb -C pool名字可以检查 ashift 值:对于扇区尺寸为 512 字节的 zpool,其 ashift 是 9,而我们希望的 ashift 值是12。

gnop节点在系统重启以后会消失,但 ZFS 会记住 ashift,因此并不会导致问题。此处也可以 zpool export,gnop destroy /dev/gpt/store.nop 然后再 zpool import 来验证。

经测试,ZFS在知道正确的扇区尺寸以后,持续写操作的性能可以提高至少一倍。

折腾
作为一个不折腾就会死星人,一倍的性能改善是有非常大的诱惑力的。因为我有两块硬盘,总容量刚好用到45%而且也没做成 mirror 或 stripe,所以正好倒腾一下。

首先是删除不必要的数据。凡是在家中有备份的数据一律删除(我自己的笔记本的系统文件和日常工作的数据备份共腾出约70GB)。

然后首先是把所有的数据复制到系统所在的 zpool 上。

制作快照:

zfs snapshot -r backup@pre-4k; zfs send -R backup@pre-4k | zfs receive -Fvd system/backup

确认备份可用且无数据校验错误之后,撤销备份卷:

zpool destroy backup

友情提示:这一步请在意识清醒的时候做。

然后为备份卷创建gnop设备:gnop create -S 4096 /dev/gpt/backup

由于之前创建这套系统时没有考虑引导部分的冗余,因此希望在重建系统时加入这方面的考虑。用gpart删掉备份卷所在分区,创建一个4GiB的freebsd-zfs类型的分区,剩余的空间作为另一个freebsd-zfs分区,并标记为新的备份卷设备:

gpart delete -i 3 ada1; gpart add -s 8388608 -t freebsd-zfs -l boot1 ada1; gpart add -t freebsd-zfs -l backup ada1

此处3、ada1是我的备份卷对应的分区编号和设备。boot1因为要做成mirror,因此暂时先不用。

重新创建备份卷:

zpool create backup /dev/gpt/backup.nop

用zdb确认ashift已经是12;

卸下备份卷并取消gnop设备:

zpool export backup; gnop destroy /dev/gpt/backup.nop

重新加载备份卷:

zpool import backup

此时可以再次用zdb检查ashift值(仍是12)。

从系统卷中恢复全部dataset

zfs send -R system/backup@pre-4k | zfs receive -Fvd backup

用类似的方法将系统卷复制到备份卷上。

由于系统卷在引导时一直在占用状态,因此不能直接在系统中将其卸下。使用插在主板上的USB stick上的系统引导之后:

删除系统卷所在的分区,并重新划分:

gpart delete -i 3 ada0; gpart add -s 8388608 -t freebsd-zfs -l boot0 ada0; gpart add -t freebsd-zfs -l neptune ada0

建立一个普通的系统卷【2】,使用两块盘上的boot0、1分区做成mirror:

zpool create -O canmount=off -O atime=off -O setuid=off system mirror /dev/gpt/boot[01]

建立用于引导系统的根dataset:

zfs create -m legacy -o compression=on -o setuid=on system/root

加载backup卷:

zpool import -R /mnt

挂载backup/system/root并将内容复制过来:

mkdir /tmp/src /tmp/dst; mount -t zfs backup/system/root /tmp/src; mount -t zfs system/root /tmp/dst; cd /tmp/src; tar cf - . | tar xf - -C /tmp/dst/

调整的具体操作在此不再赘述。简要说明:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/20881.html