mdadm RAID5 硬盘替换实战 环境
虚拟机:VMware Workstation
系统:Ubuntu 22.04 (kernel 5.15.0-185)
磁盘:4×20G 虚拟磁盘 + 1×20G NVMe 系统盘(Paravirtual SCSI 控制器)
存储栈:
1 2 3 4 sda ─┐sdb ─┼─ md0 (RAID5, 3 +1 spare) → vg0 → lv-0 (ext4) → /data (40 G)sdc ─┤sdd ─┘ (hot spare)
实战时间线 00: 前置 — 从 LVM 裸 PV 迁移到 mdadm 原先 4 块盘直接做 LVM PV,改为 mdadm RAID5 后重建 LVM 栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 umount /data vgremove vg0 pvremove /dev/sd{a,b,c,d} mdadm --create /dev/md0 --level=5 --raid-devices=3 --spare-devices=1 \ /dev/sda /dev/sdb /dev/sdc /dev/sdd pvcreate /dev/md0 vgcreate vg0 /dev/md0 lvcreate -n lv-0 -l 100%FREE vg0 mkfs.ext4 /dev/vg0/lv-0 mount /dev/vg0/lv-0 /dataecho '/dev/vg0/lv-0 /data ext4 defaults 0 2' >> /etc/fstab mdadm --detail --scan >> /etc/mdadm/mdadm.conf update-initramfs -u
初始阵列状态:sda/sdb/sdc active sync,sdd spare(热备)。State: clean。
01: 创建测试数据 + 校验和 1 2 3 4 5 6 7 8 9 10 11 12 dd if =/dev/urandom of=/data/test_100M.bin bs=1M count=100 dd if =/dev/urandom of=/data/test_10M.bin bs=1M count=10 echo "重要数据-验证用" > /data/important.txtcp -a /etc /data/etc_backupmd5sum /data/test_100M.bin /data/test_10M.bin /data/important.txt > /tmp/checksums_before.txt find /data/etc_backup -type f -exec md5sum {} \; | sort -k2 > /tmp/checksums_etc_before.txt
教训 :/tmp/checksums_before.txt 存在 tmpfs 上,重启即丢。重要校验和应存到持久存储或 /data 自身。
02: 模拟故障 — 拔 sdb,热备自动接管 方式一:命令行标记故障(可控):
1 2 3 4 mdadm --manage /dev/md0 --fail /dev/sdb
方式二:直接从 VMware 移除虚拟磁盘:
内核报 DID_NO_CONNECT I/O error,mdadm 检测到后自动标记故障:
1 2 3 dmesg: sd 32:0:1:0: [sdb] FAILED Result: hostbyte=DID_NO_CONNECT dmesg: md/raid:md0: Disk failure on sdb, disabling device. dmesg: md: recovery of RAID array md0
重建完成后移除故障盘:
1 2 3 mdadm --manage /dev/md0 --remove /dev/sdb
03: 加入新热备盘 1 2 3 mdadm --zero-superblock /dev/sdc mdadm --manage /dev/md0 --add /dev/sdc
04: ⚠️ 首次重启踩坑 — emergency mode 现象 :
重启后系统掉进 emergency mode:
1 2 3 4 5 You are in emergency mode. After logging in , type "journalctl -xb" to view system logs, "systemctl reboot" to reboot, "systemctl default" or "exit"to boot into default mode. Press Enter for maintenance (or press Control-D to continue ):
根因 :/etc/fstab 中有两行 /data 挂载条目,任一行挂载失败都触发 emergency mode:
curtin/cloud-init 生成的 by-id 路径在 RAID 重组后 UUID 变了,挂载失败。
实际踩坑中还遇到一个血泪细节:emergency mode 下根文件系统默认只读,sed 修改 fstab 静默失败。必须先 mount -o remount,rw /。
修复 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 mount -o remount,rw / sed -i 's|^/dev.* /data |#&|' /etc/fstab grep data /etc/fstabexit
05: 第二次重启 — 正常 1 2 3 4 5 6 7 8 9 10 11 lsblkcat /proc/mdstat mdadm --detail /dev/md0
06: 验证数据完整性 1 2 3 4 5 6 7 8 9 mount /dev/vg0/lv-0 /data df -h /data cat /data/important.txt ls -lh /data/
注意 :/tmp/checksums_before.txt 因重启丢失(tmpfs),无法做严格 MD5 对比。但文件大小、内容完全一致,RAID5 重建成功验证数据完好。
07: 恢复 fstab
踩坑复盘
编号
坑
现象
根因
解法
1
emergency mode
重启后卡 emergency mode,systemctl default 报 Transaction destructive
fstab 有两行 /data,curtin 生成的 by-id 行和手动添加的 /dev/vg0/lv-0 行,任一挂载失败触发 emergency
mount -o remount,rw / 后 sed 注释掉所有 /data 行
2
emergency mode 下 fstab 只读
sed 执行看似成功,重启后 fstab 没变
emergency mode 默认挂载根分区为只读,sed -i 修改实际写入失败
必须先 mount -o remount,rw /
3
sed 命令拼写错误
`sed -i ‘s
^/dev/vg0/lv-0
…’中的vg0打成vgo`,匹配不到目标行
4
校验和丢失
/tmp/checksums_before.txt 重启后不存在
/tmp 是 tmpfs,重启即清空
校验和文件应存到 /data、/root 或外部持久存储
5
VMware 新盘不识别
添加虚拟磁盘后 lsblk 不显示新盘
新盘可能挂在不同 SCSI 控制器类型(SATA vs Paravirtual SCSI)
重启虚拟机解决;或 echo "- - -" > /sys/class/scsi_host/host*/scan 手动扫描
6
VMware 拔盘后 mdadm 反应延迟
dmesg 已报 I/O error 但 cat /proc/mdstat 仍显示 [UUU]
mdadm 不是实时轮询,有一定检测延迟
等待几秒或手动 mdadm --fail 加速
7
拔盘后重启,不同设备名
第一次启动 sdb 是 faulty,重启后 sdb 又变回正常 active
mdadm 用 superblock UUID 识别成员,设备名 /dev/sdX 只是临时路径,重启后内核可能重新分配
不要用设备名判断状态,始终看 mdadm --detail 中的 Number/State
完整操作流程(生产环境标准步骤) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d) sed -i 's|^/dev.* /data |#&|' /etc/fstabdd if =/dev/urandom of=/data/test_100M.bin bs=1M count=100echo "重要数据" > /data/important.txtmd5sum /data/test_100M.bin /data/important.txt > /root/checksums_before.txt mdadm --manage /dev/md0 --fail /dev/sdb watch -n 3 'cat /proc/mdstat | grep md0' mdadm --manage /dev/md0 --remove /dev/sdb mdadm --zero-superblock /dev/sdb mdadm --manage /dev/md0 --add /dev/sdbcat /proc/mdstatmd5sum -c /root/checksums_before.txtls -lh /data/cp /etc/fstab.bak.$(date +%Y%m%d) /etc/fstab mount -a
命令速查
操作
命令
查看阵列状态
cat /proc/mdstat
查看详情
mdadm --detail /dev/md0
查看阵列成员状态
mdadm --detail /dev/md0 | grep -E 'State|Number.*sd'
标记故障
mdadm --manage /dev/md0 --fail /dev/sdX
移除盘
mdadm --manage /dev/md0 --remove /dev/sdX
添加盘
mdadm --manage /dev/md0 --add /dev/sdX
擦除 superblock
mdadm --zero-superblock /dev/sdX
持久化配置
mdadm --detail --scan >> /etc/mdadm/mdadm.conf
更新 initramfs
update-initramfs -u
手动触发检查
echo check > /sys/block/md0/md/sync_action
扫描 SCSI 总线
for h in /sys/class/scsi_host/host*/scan; do echo "- - -" > "$h"; done
dmesg 查盘故障
dmesg | grep -iE 'sd[a-z]|fail|error|DID_NO_CONNECT'
挂载验证
mount -a(无报错即 fstab 正常)
查询命令汇总 日常巡检和排障时快速查看 RAID + LVM + 磁盘 全链路状态。
RAID 阵列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 cat /proc/mdstat mdadm --detail /dev/md0 mdadm --detail /dev/md0 | grep -E 'State|Active|Working|Failed|Spare|Rebuild' mdadm --detail /dev/md0 | grep -A20 'Number.*Major' mdadm --detail --scan
LVM 1 2 3 4 5 6 7 8 9 10 11 12 pvs pvdisplay vgs vgdisplay vg0 lvs lvs -a -o name,size,segtype,devices,vg_name lvdisplay /dev/vg0/lv-0
磁盘 1 2 3 4 5 6 7 8 9 10 11 lsblk -o NAME,SIZE,TYPE,MOUNTPOINT lsblk -o NAME,SIZE,MODEL,SERIAL,TYPE lsblk -f fdisk -l /dev/sd[a-z] 2>/dev/null | grep -E 'Disk /dev|Disk model'
文件系统 1 2 3 4 5 6 7 8 9 df -hT /data mount | grep /data grep data /etc/fstab blkid /dev/vg0/lv-0
故障排查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 dmesg | grep -iE 'sd[a-z]|md|fail|error|DID_NO_CONNECT|I/O error' smartctl -a /dev/sda | grep -E 'Reallocated|Pending|Uncorrectable|Error' echo check > /sys/block/md0/md/sync_actioncat /proc/mdstat cat /sys/block/md0/md/sync_completedcat /sys/block/md0/md/sync_speedcat /sys/block/md0/md/sync_action
一键巡检脚本 保存为 /root/check-storage.sh,日常跑一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #!/bin/bash echo "===== RAID 状态 =====" cat /proc/mdstatecho "" echo "===== RAID 详情 =====" mdadm --detail /dev/md0 | grep -E 'State|Active|Working|Failed|Spare|Rebuild' echo "" echo "===== LVM =====" pvs && vgs && lvsecho "" echo "===== 磁盘 =====" lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | grep -E 'sd|md|vg|nvme' echo "" echo "===== 挂载 =====" df -hT /dataecho "" echo "===== 最近磁盘错误 =====" dmesg | grep -iE 'sd[a-z]|md|fail|error|DID_NO_CONNECT' | tail -10