没耐心请直接看结论部分。
引言
弄了点硬盘打算组建 RAID 阵列,根据网上的教程和 Gemini 的提示,用 mdadm 创建了一个 RAID 0 阵列。能正常读写文件了,又摸索着设置了开机自动挂载。
重启设备试试自动挂载正常不正常,结果 SSH 连不上了。赶紧拿电视当显示器艰难 Debug,一打开就是 Emergenct Mode 😇。systemd 启动失败,挂载 /mnt/raid0 失败,等待设备 /dev/md0 超时。
排查
怀疑 1: NixOS 模块存在 Bug
服务器的系统是相当灵车的 NixOS,因此很自然就会想到是不是 unstable 分支的模块又炸了。NixOS 模块配置如下:
boot.swraid = {
enable = true;
# 以下填入 sudo mdadm --detail --scan 的结果
mdadmConf = ''
ARRAY /dev/md0 metadata=1.2 UUID=aaaaaaaa:bbbbbbbb:cccccccc:dddddddd
'';
};
fileSystems."/mnt/raid0" = {
device = "/dev/md0";
fsType = "ext4";
options = [
"defaults"
"noatime"
];
};
当即想到是不是启动时序问题,RAID 设备在 Stage 1 - initrd 阶段未被加载。又因为老觉得 mdadm 是由 systemd 启动的(这是我当时的误解!),于是尝试改变 initrd 为由 systemd 加载:boot.initrd.systemd.enable = true;
非常幸运……问题没有解决。nixpkgs 仓库里也没有相应的 Issue,按理来说如果确实是 Bug 会有人报告的,看起来不太像是 NixOS 的 Bug。
怀疑 2: 硬盘坏了
穷鬼就是穷鬼,捡来的硬盘已经通电几万小时了。刚拿到手我就怕它坏了,呜呜呜呜呜呜呜,现在终于坏掉了……吗?
执行以下命令扫描硬盘上 RAID 阵列的元数据:
sudo mdadm --detail --scan
sudo mdadm --assemble --scan # 尝试根据硬盘上的元数据组装阵列
没有结果,说明硬盘上 RAID 的元数据已经被摧毁(其实不是,后面有反转!)。
呜呜呜,它真的坏了……个鬼。在重新创建阵列并格式化后:
sudo mdadm --create --verbose /dev/md0 --level=0 --raid-devices=2 /dev/sda /dev/sdc
sudo mkfs.ext4 /dev/md0
新的阵列可以正常挂载并使用,sudo mdadm --detail --scan 命令也有结果。但是在重启后,它又不见了,症状和上面一模一样。说明不是硬盘坏掉的问题。
同病相怜相见恨晚伯牙遇子期
充满怒火地在网上搜索,见到了这样一篇帖子,症状和我的一样:记一次采坑经历, mdadm 创建 raid0,重启后自动消失
询问 Gemini 后,它告诉我:
结论与解决方案
结论
出现这个问题的原因是:磁盘签名冲突。
Linux 内核和 systemd 会优先识别常见的磁盘结构,比如 GPT(GUID 分区表)和 MBR(主引导记录)。
- 当系统检测到带有 GPT 签名的磁盘时,会立即将其视为一个已经分区的设备。
- mdadm 工具和 mdraid 内核模块则被设计为在整个磁盘或某个分区上寻找它们的超级块(superblock)。
- 如果一个磁盘带有 GPT 头部,它就已经被识别为一个分区的设备。mdraid 内核模块可能因此无法将其识别为一块用于 RAID 的、未被分区的“裸磁盘”(raw disk),从而导致阵列无法组装。
简而言之,就是残留在磁盘上的 GPT 分区数据“欺骗”了系统,让它看到了一个分区表,而不是一块可以用于 RAID 的原始磁盘。
解决方案
1. 擦除分区表信息
如果磁盘已经被用于 RAID 阵列,则擦除超级块:
sudo mdadm --zero-superblock --force /dev/sda
sudo mdadm --zero-superblock --force /dev/sdc
用 sgdisk (gptfdisk 包) 清空整个磁盘,包括分区表信息:
sudo sgdisk --zap-all /dev/sda
sudo sgdisk --zap-all /dev/sdc
以防万一,再次擦除储存分区信息的扇区:
sudo dd if=/dev/zero of=/dev/sda bs=1M count=100
sudo dd if=/dev/zero of=/dev/sdc bs=1M count=100
用 sudo fdisk -l 检验是否成功。当磁盘信息里不显示 磁盘标签类型:gpt 时,擦除已经成功。
2. 正常创建 RAID 阵列
擦除成功后,再次创建 RAID 阵列。
sudo mdadm --create --verbose /dev/md0 --level=0 --raid-devices=2 /dev/sda /dev/sdc
sudo mkfs.ext4 /dev/md0
此时再重启,RAID 阵列应该能正常挂载。用 lsblk 检测,TYPE 应为 raid0 而非 part。
结语
浪费了 3 个小时。
附录 1:NixOS 下正确挂载 RAID 阵列的方式
boot.swraid = {
enable = true;
# 以下填入 sudo mdadm --detail --scan 的结果
mdadmConf = ''
ARRAY /dev/md0 metadata=1.2 UUID=aaaaaaaa:bbbbbbbb:cccccccc:dddddddd
'';
};
fileSystems."/mnt/raid0" = {
device = "/dev/md0";
fsType = "ext4";
options = [
"defaults"
"noatime"
];
};
附录 2:NixOS 下设置硬盘自动休眠的方式
environment.systemPackages = [ pkgs.hd-idle ];
systemd.services.hd-idle = {
serviceConfig = {
Type = "simple";
User = "root";
ExecStart =
let
mkDisk = label: timeInMinutes: "-a /dev/${label} -i ${toString (timeInMinutes * 60)}";
disks = lib.concatStringsSep " " [
(mkDisk "sda" 10)
(mkDisk "sdb" 10)
(mkDisk "sdd" 10)
];
extraOptions = lib.concatStringsSep " " [
"-l /tmp/hd-idle.log"
"-d" # Debug
"-I" # Ignores spin down detection. Spins down the disk even it's already been.
];
in
''${lib.getExe pkgs.hd-idle} ${extraOptions} -i 0 ${disks}'';
};
wantedBy = [ "multi-user.target" ];
};
Loading Comment Component...
