折腾一块硬盘塔中新的硬盘

/ 0评 / 0

该扩容了!

本篇是「折腾一个」系列的第 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.