该扩容了!
本篇是「折腾一个」系列的第 4 篇。上一篇:《折腾一个网络电视(上)》。
本文章将阐述如何就地在线替换硬盘塔中的一块硬盘。「就地」指不需要同时上线旧硬盘和新硬盘。「在线」整个过程中尽可能不打断正在运行的服务,也不进入特殊的维护系统。
该硬盘隶属于一个 LVM JBOD 结构;该结构隶属于一个 BtrFS RAID1 系统。
注意到:我们将要执行的动作会操作大量的数据,其中的一些步骤可能会按小时进行。总共的操作可能需要耗费一到两天。其中必须保证电源稳定,一旦断电将会造成不可逆的数据损毁。
数据无价,谨慎操作
初始状态
硬盘塔的初始状态如下:
物理硬盘:
PD1: 500GB -> /dev/sdb
PD2: 320GB -> /dev/sdc
PD3: 500GB -> /dev/sdd
PD4: 2TB -> /dev/sde
LVM 物理卷:
PV0: PD1 全部
PV1: PD2 全部
PV2: PD3 全部
PV3: PD4 全部
LVM 卷组:
VG0: PV0 + PV1 + PV2 组成 JBOD; 总大小 1.20TiB
VG1: PV3; 总大小约 1.82TiB
LVM 逻辑卷:
LV0: VG0 1.20TiB
LV1: VG1 1.20TiB
LV2: VG1 634.21GiB
文件系统:
btrfs: LV0 + LV1 组成 RAID1
ext4: LV2 简单卷
挂载点:
btrfs -> /mnt/ds-volume
ext4 -> /mnt/data
目标状态
我们的目标是将系统转换到如下状态:
物理硬盘:
PD1: 500GB -> /dev/sdb
PD2: 2TB -> /dev/sdc --- 更换这块硬盘
PD3: 500GB -> /dev/sdd
PD4: 2TB -> /dev/sde
LVM 物理卷:
PV0: PD1 全部
PV1: PD2 全部
PV2: PD3 全部
PV3: PD4 全部
LVM 卷组:--- 卷组重新调整
VG0: PV0 + PV2 组成 JBOD; 总大小约 930GiB
VG1: PV3; 总大小约 1.82TiB
VG2: PV1; 总大小约 1.82TiB
LVM 逻辑卷:
LV0: VG0 930TiB --- 数据移动
LV1: VG1 1.82TiB --- 数据移动,扩展
LV2: VG2 1.82GiB --- 新加卷
文件系统:--- 目标变更
btrfs: LV1 + LV2 组成 RAID1
ext4: LV0 简单卷
挂载点:
btrfs -> /mnt/ds-volume
ext4 -> /mnt/data
由于过程中遇到了技术问题(见下文),将目标调整为:
物理硬盘:
PD1: 2TB -> /dev/sdb --- 更换这块硬盘
PD2: 320GB -> /dev/sdc
PD3: 500GB -> /dev/sdd
PD4: 2TB -> /dev/sde
LVM 物理卷:
PV0: PD1 全部
PV1: PD2 全部
PV2: PD3 全部
PV3: PD4 全部
LVM 卷组:--- 卷组重新调整
VG0: PV1 + PV2 组成 JBOD; 总大小约 930GiB
VG1: PV3; 总大小约 1.82TiB
VG2: PV0; 总大小约 1.82TiB
LVM 逻辑卷:
LV0: VG0 930TiB --- 数据移动
LV1: VG1 1.82TiB --- 数据移动,扩展
LV2: VG2 1.82GiB --- 新加卷
文件系统:--- 目标变更
btrfs: LV1 + LV2 组成 RAID1
ext4: LV0 简单卷
挂载点:
btrfs -> /mnt/ds-volume
ext4 -> /mnt/data
操作目标
- 完成总体迁移工作;
- 过程中不应损失数据;
- 就地更换硬盘——新盘和旧盘不能同时上线,因为硬盘塔里已经没有盘位
- 在线更换硬盘——对系统现有服务产生尽可能小的侵扰,也不需要重启到特殊操作环境
- 完成迁移后,其他应用程序不需要额外重新配置
操作步骤
关闭共享和其他读写目标卷的服务
尽管理论上我们接下来的操作是完全在线的,为了迁移性能考虑,应关闭文件共享等需要操作目标卷的服务。
转换 RAID1 模式到 JBOD 模式
将 BtrFS RAID1 卷转换到两个并联卷,以便进行之后的下线操作:
# btrfs balance start -f -sconvert=single -mconvert=single -dconvert=single /mnt/ds-volume
这个操作需要特 别 长 时 间,而且不会显示进度条。这个操作步骤在我的机器上跑了将近 7 个小时,而且报错退出了。查看系统日志:
[368242.485087] BTRFS warning (device dm-2): csum failed root -9 ino 1067 off 313540608 csum 0xfd8937d1 expected csum 0x8084cabe mirror 2
[368242.485116] BTRFS error (device dm-2): bdev /dev/mapper/MassStorage-data errs: wr 0, rd 0, flush 0, corrupt 443, gen 0
[368242.546834] BTRFS warning (device dm-2): csum failed root -9 ino 1067 off 313540608 csum 0xfd8937d1 expected csum 0x8084cabe mirror 1
[368242.546857] BTRFS error (device dm-2): bdev /dev/mapper/MassMirror-mirror errs: wr 0, rd 0, flush 0, corrupt 333, gen 0
[368242.683224] BTRFS warning (device dm-2): csum failed root -9 ino 1067 off 313540608 csum 0xfd8937d1 expected csum 0x8084cabe mirror 2
[368242.683247] BTRFS error (device dm-2): bdev /dev/mapper/MassStorage-data errs: wr 0, rd 0, flush 0, corrupt 444, gen 0
[368242.687522] BTRFS warning (device dm-2): csum failed root -9 ino 1067 off 313540608 csum 0xfd8937d1 expected csum 0x8084cabe mirror 1
[368242.687538] BTRFS error (device dm-2): bdev /dev/mapper/MassMirror-mirror errs: wr 0, rd 0, flush 0, corrupt 334, gen 0
[368244.606908] BTRFS info (device dm-2): balance: ended with status: -5
好么,开局翻车。BtrFS 报告了不可修复的错误。原来 BtrFS 需要时不时运行 scrub
命令以确保数据不会因为 IO 错误和自然衰变挂掉的,但当时我又没配置系统自动执行 scrub
.
那只能现在亡羊补牢一次了:
# btrfs scrub start /mnt/ds-volume
这个指令同样要跑很久,不过它是在后台跑的,所以我们可以观察进度:
# watch btrfs scrub status /mnt/ds-volume
机械硬盘的读写速度在 160MB/s 左右;同时 scrub
操作不需要访问整个硬盘,而只需要访问已经有数据的区域。这个指令跑了总共有 2 个小时。
不行的是,它在处理前 1% 的数据的时候就汇报了 9 起不可恢复错误;处理到 20% 的时候汇报了 28 起。这种情况下,我们是必然会丢失数据了。
到了 40% 的时候,错误总数似乎平稳在了 64 起,其中 10 起为不可恢复错误;54 起已纠正错误。PD1
的 SMART 数据显示这块硬盘可能命数已尽了:
# smartctl -a /dev/sdb # /dev/sda is the system drive
smartctl 7.3 2022-02-28 r5338 [x86_64-linux-6.1.12-arch1-1] (local build)
Copyright (C) 2002-22, Bruce Allen, Christian Franke, www.smartmontools.org
=== START OF INFORMATION SECTION ===
Model Family: Hitachi/HGST Travelstar Z5K500
Device Model: Hitachi HTS545050A7E380
Serial Number: TA85123VCXKKAT
LU WWN Device Id: 5 000cca 6f5ccfc4e
Firmware Version: GG2OA6C0
User Capacity: 500,107,862,016 bytes [500 GB]
Sector Sizes: 512 bytes logical, 4096 bytes physical
Rotation Rate: 5400 rpm
Form Factor: 2.5 inches
Device is: In smartctl database 7.3/5319
ATA Version is: ATA8-ACS T13/1699-D revision 6
SATA Version is: SATA 2.6, 3.0 Gb/s
Local Time is: Sun Feb 26 23:06:09 2023 CST
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
--8<--8<--8<--8<--
SMART Error Log Version: 1
ATA Error Count: 23 (device log contains only the most recent five errors)
CR = Command Register [HEX]
FR = Features Register [HEX]
SC = Sector Count Register [HEX]
SN = Sector Number Register [HEX]
CL = Cylinder Low Register [HEX]
CH = Cylinder High Register [HEX]
DH = Device/Head Register [HEX]
DC = Device Command Register [HEX]
ER = Error register [HEX]
ST = Status register [HEX]
Powered_Up_Time is measured from power on, and printed as
DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,
SS=sec, and sss=millisec. It "wraps" after 49.710 days.
Error 23 occurred at disk power-on lifetime: 7500 hours (312 days + 12 hours)
When the command that caused the error occurred, the device was active or idle.
After command completion occurred, registers were:
ER ST SC SN CL CH DH
-- -- -- -- -- -- --
84 51 01 5f 52 5f 00 Error: ICRC, ABRT at LBA = 0x005f525f = 6247007
Commands leading to the command that caused the error were:
CR FR SC SN CL CH DH DC Powered_Up_Time Command/Feature_Name
-- -- -- -- -- -- -- -- ---------------- --------------------
60 20 00 40 52 5f 40 00 00:04:05.627 READ FPDMA QUEUED
60 20 00 20 52 5f 40 00 00:04:05.627 READ FPDMA QUEUED
60 20 00 00 52 5f 40 00 00:04:05.626 READ FPDMA QUEUED
60 20 00 e0 51 5f 40 00 00:04:05.626 READ FPDMA QUEUED
60 20 00 c0 51 5f 40 00 00:04:05.626 READ FPDMA QUEUED
--8<--8<--8<--8<--
SMART Self-test log structure revision number 1
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Short offline Completed without error 00% 8239 -
考虑到新的硬盘也是日立盘,我心里有点慌。但至少这块硬盘已经连续服役了 8239 小时了,换算出来大概是……不到一年?显然不对啊。
再运行一次自检:
# smartctl -t short /dev/sdb
一般 SMART 快速自检需要一到两分钟完成。这个工具会提示你需要运行多长时间的自检。
再读一次自检数据:
# smartctl -l selftest /dev/sdb
smartctl 7.3 2022-02-28 r5338 [x86_64-linux-6.1.12-arch1-1] (local build)
Copyright (C) 2002-22, Bruce Allen, Christian Franke, www.smartmontools.org
=== START OF READ SMART DATA SECTION ===
SMART Self-test log structure revision number 1
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Short offline Completed without error 00% 16945 -
# 2 Short offline Completed without error 00% 8239 -
16945 小时差不多是 700 天,基本上接近 2 年。但这么算下来其实还是有点不太对,毕竟这块硬盘是从老笔记本里拆出来的,那台笔记本的岁数都已经快 10 岁了。
反正我们要换掉一块硬盘,原本计划换掉最小的,现在只能选择换掉要炸掉的了。至少我们在这块硬盘完全归西一炮带走全部数据之前还是抓到了的,可喜可贺。
scrub
指令最终报告了 54 个已纠正错误和 10 个不可恢复错误。从 dmesg
输出中可以拿到损坏的文件列表。结果是 Backups/gbacard.img
文件损坏了。备份文件本身,损坏了。这未免有些过于黑色幽默。
没办法,只能选择删掉这个文件了:
# rm -rf /mnt/ds-volume/Backups/gbacard.img
老文件不死,只是褪色。
再次运行 scrub
操作确认没有其他文件损坏,一跑又是 2 小时。不过这回终于没有再汇报错误,可以继续进行转换操作了:
# btrfs balance start -f -sconvert=single -mconvert=single -dconvert=single -dsoft /mnt/ds-volume
Done, had to relocate 37 out of 844 chunks
转换完成之后,这两个 LVM 逻辑卷就组成了一个 BtrFS 跨区卷(不是)。虽然从技术上来讲,完全可以利用 RAID1 特性用 btrfs replace
命令去添加一个新的盘到系统中,但是这样要么需要将旧盘和新盘同时接入系统;要么需要直接把旧盘拔掉然后通过维修模式挂载并替换。这两个操作在当前系统上都不太好实现。
删除不需要的 BtrFS 设备
转换完成后,我们就可以将待下线的设备直接删除掉。BtrFS 会自动将数据挪到其他未被删除的卷中:
# btrfs device remove /dev/mapper/MassStorage-data /mnt/ds-volume
又是一个漫长而艰辛的过程。不过至少这个命令也是在后台运行的。不过它倒是没有一个单独的 status
子命令查看状态,但是我们可以从设备用量上估算当前的进度:
# btrfs device usage /mnt/ds-volume
/dev/mapper/MassStorage-data, ID: 1
Device size: 1.20TiB
Device slack: 1.20TiB
Data,single: 148.00GiB --- 这个数字会慢慢减少
Metadata,single: 2.00GiB
Unallocated: -150.00GiB
/dev/mapper/MassMirror-mirror, ID: 2
Device size: 1.20TiB
Device slack: 0.00B
Data,single: 692.00GiB
System,single: 32.00MiB
Unallocated: 536.77GiB
从 LVM 卷组中删除物理卷
首先,下线卷组:
# vgchange -a n MassStorage
然后我们就可以移除卷组上的逻辑卷了:
# lvremove MassStorage/data
然后将物理卷从卷组中移除:
# vgreduce MassStorage /dev/sdb1
# pvremove /dev/sdb1
完成后,即可将硬盘从硬盘塔中抽出。
注意:如果抽出硬盘后系统重新分配了各个卷的卷标,则需要在安装新硬盘之后重启系统。如果你的 /etc/fstab
中指定了依赖于卷标的挂载条目,则可能需要手动修复。
建立新的 LVM 结构
装入了新的硬盘之后,我们就可以着手调整之前的 LVM 架构,将新的硬盘纳入 LVM 管理:
# pvcreate /dev/sdb
目前 LVM 并不支持将逻辑卷跨卷组移动,所以我们必须手动执行逻辑复制。
从 MassStorage
卷组中新建逻辑卷用于系统数据的存储:
# lvcreate -l 100%FREE -n data MassStorage
进行格式化和复制操作:
# vgchange -a y MassStorage
# mkfs.ext4 /dev/mapper/MassStorage-data
# mkdir /mnt/data-new
# mount /dev/mapper/MassStorage-data /mnt/data-new
# rsync -rv --progress /mnt/data/* /mnt/data-new
至少这次我们有进度条可以看。30MB/s 的写入速度,相当刺激。
拿到新逻辑卷的 UUID, 把它作为新的系统数据分区挂载:
# blkid /dev/mapper/MassStorage-data
/dev/mapper/MassStorage-data: UUID="ab21336b-8e82-475a-b4fc-cedcc46ec262" BLOCK_SIZE="4096" TYPE="ext4"
/// in /etc/fstab
UUID=ab21336b-8e82-475a-b4fc-cedcc46ec262 /mnt/data ext4 rw,nofail,user 0 2
通知 systemd
更新挂载状态,并重新挂载分区:
# systemctl daemon-reload
# mount -o remount /mnt/data
# umount /mnt/data-new
此时,原有的逻辑卷应该被卸载了:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 7.5G 0 disk
├─sda1 8:1 0 512M 0 part /boot
├─sda2 8:2 0 1G 0 part [SWAP]
└─sda3 8:3 0 6G 0 part /
sdb 8:16 0 1.8T 0 disk
sdc 8:32 0 298.1G 0 disk
└─sdc1 8:33 0 298.1G 0 part
└─MassStorage-data 254:2 0 763.8G 0 lvm /mnt/data
sdd 8:48 0 465.8G 0 disk
└─sdd1 8:49 0 465.8G 0 part
└─MassStorage-data 254:2 0 763.8G 0 lvm /mnt/data
sde 8:64 0 1.8T 0 disk
└─sde1 8:65 0 1.8T 0 part
├─MassMirror-mirror 254:0 0 1.2T 0 lvm /srv/nfs/volume
│ /mnt/ds-volume
└─MassMirror-data 254:1 0 634.2G 0 lvm
现在我们就可以直接删除旧逻辑卷了:
# lvremove MassMirror/data
扩展 LVM 逻辑卷
现在我们可以使用整个 2TB 硬盘的空间了。LVM 扩展空间还是很简单的:
# lvextend -l +100%FREE MassMirror/mirror
BtrFS 会检测到分区大小有更改:
# btrfs device usage /mnt/ds-volume
/dev/mapper/MassMirror-mirror, ID: 2
Device size: 1.82TiB
Device slack: 634.21GiB
Data,RAID1: 839.00GiB
Metadata,RAID1: 2.00GiB
System,RAID1: 32.00MiB
Unallocated: 387.77GiB
此时直接要求 BtrFS 扩展到整个分区即可:
# btrfs filesystem resize 2:max /mnt/ds-volume
启用新硬盘
将新硬盘加入 LVM 卷组:
# vgcreate ReplicaSet /dev/sdb
我们将使用 BtrFS 做 RAID 控制,所以直接创建简单逻辑卷即可:
# lvcreate -l 100%VG -n data ReplicaSet
重新平衡 BtrFS
将新的逻辑卷注册到 BtrFS:
# btrfs device add /dev/mapper/ReplicaSet-data /mnt/ds-volume
然后进行 RAID 1 转换。这个转换仍然是会在前台运行且不会有任何进度输出,所以可以考虑用 screen
或者 tmux
放在后台:
# btrfs balance start -mconvert=raid1 -dconvert=raid1 /mnt/ds-volume
数据完整性检查
上述命令完成之后,系统即转换到目标状态。重新启动文件共享等服务即可恢复上线。
不要忘记启动 scrub 服务:
# systemctl enable btrfs-scrub@mnt-ds\x2dvolume.timer --now
Your comments will be submitted to a human moderator and will only be shown publicly after approval. The moderator reserves the full right to not approve any comment without reason. Please be civil.