两天前的凌晨,正在玩手机的我发现家里断网了。唤醒待机中的电脑,发现没有获取到 IPv4 地址。手动配上 IP 地址后打开路由器的管理界面,没发现什么问题,就尝试重启 dnsmasq, 但没有起作用。于是我决定重启整个 openwrt 路由器。
然而路由器并没有完成重启,反而,现在连 luci 管理界面都挂了。尝试 ssh 登录路由器,以及 web/ssh 登录 PVEW 母机(我的 openwrt 是运行在 PVE 上的虚拟机),都没有成功,只能去 reset PVE 母机。
重启后网络恢复了,但是检查 PVE 母机发现,两块硬盘组成的 zfs mirror degraded 了。 sdb 彻底失效,连 smart 数据都读不出来;同时 sda 的 smart 数据里有 pending sectors 和 offline uncorrected sectors. 当时我就觉得两块盘同时故障,可能恢复不了了。于是决定买两块盘替换掉故障硬盘,同时尽量拯救数据。不过事已至此,深夜就不要搞变更操作了,先睡觉,其它都第二天再说吧。
故障止损
首先,随着这台 PVE 母机自动启动的只有至关重要的 openwrt 路由器,其它的虚拟机都是手动启动的。目前这个状态下我就没有去启动其它虚拟机了。
ZED Notification
ZFS 是有一个 zed 服务监听 zfs 相关的事件并发送通知的。默认应该是通过邮件发送,但是我没有配置 MTA (邮件还是太复杂了)。现在我决定要赶快把它配上。
看了一下 zed 的配置文件,很好,它是自带 pushover 支持的。我本来就在使用 pushover 的服务,这里就直接配上 pushover 的 token, 重启 zed.service 即可。
数据备份
第二天下单了两块新硬盘 + UAS 硬盘盒,同时开始准备备份数据。
故障的这两块硬盘其实是 SMR 盘,所以我一开始就没把重要数据放在上面。上面的数据其实就是 PVE 系统、一个 openwrt 虚拟机、一个运行 ubnt controller 的 LXC. 另外还有几台虚拟机跑在这个母机上,但存储是通过 iSCSI 挂载在 NAS 上的。
查了一下 PVE 的备份命令 vzdump 的手册,发现它可以把备份写到 stdout. 那么最好的备份方式就是通过 ssh vzdump 把备份直接保存在我的台式机上,避免再往已经出问题的硬盘上写入大量数据。备份命令为:
ssh root@<pve-host> vzdump <vmid> --compress zstd --stdout > <vmid>.vma.zst
Openwrt 虚拟机的磁盘只有十几 MB, 很快完成了。 运行 ubnt controller 的 LXC 更大一点,不过也顺利完成了备份,没有触发 I/O 错误。
替换故障硬盘
收到硬盘后,首先关机用新硬盘换掉失效的 sdb. 开机后首先需要对硬盘进行分区。
创建分区
一开始我准备用 sfdisk --dump <old> | sfdisk <new>
直接把旧硬盘的分区表复制过去。但我买的新硬盘比旧硬盘大,复制分区表过去后发现没办法把分区扩大到整个硬盘的大小。(可能是 GPT 分区表里面还记录了整个磁盘的大小?)于是只能手动把分区抄过去。
PVE 安装的时候会在硬盘上创建 3 个分区: BIOS BOOT, EFI SYSTEM PARITION, 和根分区。其中 BIOS BOOT 分区不是 4K 对齐的。这让我用分区工具手动创建的时候很不好操作。所以我就不创建 BIOS BOOT 分区了,反正我不需要 legacy boot.
替换 zpool 设备
执行 zpool status rpool
命令(其中 rpool 是这个 zpool 的名称),会显示当前的状态:
# zpool status rpool
pool: rpool
state: DEGRADED
status: One or more devices could not be used because the label is missing or
invalid. Sufficient replicas exist for the pool to continue
functioning in a degraded state.
action: Replace the device using 'zpool replace'.
see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-4J
scan: resilvered 1.21G in 00:15:09 with 0 errors on Sat Jul 8 17:18:50 2023
config:
NAME STATE READ WRITE CKSUM
rpool DEGRADED 0 0 0
mirror-0 DEGRADED 0 0 0
ata-ST***********************H9-part3 ONLINE 0 0 0
52***************90 UNAVAIL 0 0 0 was /dev/disk/by-id/ata-ST***********************ZX-part3
其中, UNAVAIL
的是我们要替换的设备。替换的命令是:
zpool replace rpool 52***************90 /dev/disk/by-id/ata-HGST_HU********************YM-part3
其中,52***************90
是前面 zpool status
命令显示的 NAME, /dev/disk/by-id...
是新的设备分区。执行后 zfs 会开始在后台 resilver, 即重建 mirror. 可以执行 zpool status
命令查看进度。我这里需要复制 29 GB 的数据,即使是 SMR 盘,也不会花很长时间。
不久后手机上收到了 pushover 的推送通知,resilver 顺利完成了。还好 sda 的 pending sectors 和 offline uncorrected sectors 没有影响到数据。
重建引导
为了达到整个系统的高可用,除了根分区的 ZFS 是 mirror 的,PVE 还需要在两个硬盘上都建立 ESP, 安装上 systemd-boot、内核、initramfs, 并注册到 UEFI 启动项中。之前分区的时候已经创建了 ESP, 现在只要用 PVE 自带的工具就可以一键完成。
proxmox-boot-tool format /dev/sdb2
proxmox-boot-tool init /dev/sdb2
执行 init 的过程中会提示有磁盘 does not exist, 这是被替换掉的旧设备。根据提示去编辑 /etc/kernel/proxmox-boot-uuids
, 删除其中的旧设备。
执行完后用 efibootmgr -v
检查一下,发现旧的硬盘还在里面。执行 efibootmgr --delete-bootnum -b <num>
删掉对应的旧启动项。
替换 sda
sda 有 pending sectors 和 offline uncorrected sectors, 也是处于非常不健康的状态,所以也要替换掉它。替换的过程和前面替换 sdb 完全一致。
扩大文件系统
这次换的新硬盘比旧硬盘大,所以还需要扩大文件系统。执行命令 zpool set autoexpand=on rpool
打开 autoexpand
. 但是好像没有触发 expand. 试了一下 zpool reopen rpool
也没效果。最后用
zpool offline rpool ata-HGST_HU********************YM-part3
zpool online rpool ata-HGST_HU********************YM-part3
zpool offline rpool ata-HGST_HU********************5M-part3
zpool online rpool ata-HGST_HU********************5M-part3
给两块硬盘分别切换 offline/online, 触发了 expand.
Expand 完成后执行 zpool set autoexpand=off rpool
关掉 autoexpand
.
处理旧硬盘
两块旧硬盘,一块还能访问的,插到 UAS 硬盘盒里去执行 secure erase. 另一块彻底失效的,拆掉螺丝打开外壳,毁掉盘面。附上硬盘照片。
可以看到这是一块单盘双面的硬盘。另外有意思的是,以前我以为磁头是用弹簧固定在磁头架上的,只要断电就会回弹。但实际上这块硬盘的磁头拨到盘面上以后,是不会自动回弹的,所以断电紧急停靠大概是依赖电容存储的能量来驱动电机收回磁头。
磁盘表面也真的是光滑到极致了,在划破磁盘表面之前,我去转动轴承,都看不出来盘面有没有跟着旋转。
故障分析
首先,这里根因肯定在于 SMR 硬盘。我知道 SMR 不靠谱,但没想到这么不靠谱。根据 resilver 时的统计信息,我这个 zfs 上面就只有 29 GB 数据,会持续产生写入的只有 ubnt controller 使用的 mangodb. SMART 数据显示总共有 400 TB 的写入量,我很怀疑这个数据不准确。这两块硬盘在我这样的轻度负载下,运行了两年多,还没满 3 年保修期就坏了。
其次,我没有配置 zed 故障告警是问题很大的。那天凌晨当机可能已经不是故障的第一现场了。如果我能早点发现问题,可能就不会像现在这样一块硬盘失效,另一块硬盘告警这么吓人了。但是现在要配置告警通知也很麻烦,一般普遍支持的方式都是邮件告警。但现在商业邮箱注册基本上都要验证手机号,我又不想和自己共用主力邮箱(担心数据安全),要自己搭建 MTA 那就更加复杂。还好现在 zed 支持 pushover 之类基于 HTTP API 的通知服务。
最后修改于 2025-08-14