☰
Ceph OSD盘符漂移排查与恢复:从/dev/sdX到by-id的加固实践
2026/10/3 1:27:32 网站建设 项目流程

凌晨1点47分,监控把我叫醒:osd.7 down。登录服务器后,ceph -s里确实躺着一条醒目的告警,但奇怪的是,lsblk里那块SSD明明还在,SMART也正常,怎么想都不像盘挂了。再查systemd日志,一句话点破:/dev/sdb: No such file or directory。这不是盘坏了,是又一场SSD盘符漂移——重启前还叫/dev/sdb的盘,这次变成了/dev/sdc,而OSD的挂载单元还在固执地寻找/dev/sdb。

这种故障在存储运维里不算罕见,但每次出现都足够让人头皮发麻:集群里明明有盘,OSD却起不来;重启节点,盘序还可能再洗一次牌;要是有人急着ceph osd rm重建,数据冗余就会进一步恶化。这篇不写理论空话,我把从根因、复现、排障、恢复到加固的完整链路梳理一遍,全程基于测试环境的实际操练,适合Ceph管理员、存储运维、SRE参考,尤其是那些还在用/dev/sdX路径部署OSD的团队。

1. 盘符漂移的根因:Linux设备名只是内核按扫描顺序发的临时号

1.1 盘符是怎么被分配的

Linux内核并不关心你的SSD是哪个品牌。系统启动时,SCSI子系统会扫描各个HBA/控制器上的设备,扫描到一块盘,就按顺序给它排一个sdX的设备名,sda、sdb、sdc一路排下去。NVMe设备则是nvme0n1、nvme1n1,原理类似,也是按PCIe枚举顺序来的。

这个"扫描顺序"听起来是确定的,实际上并不保证稳定。一台服务器里通常有多个控制器:板载SATA控制器、PCIe扩展卡、RAID卡直通模式、NVMe控制器,内核模块加载的顺序、PCIe total枚举的时序、设备响应速度的快慢,都会影响"谁先被内核看到"。某块盘在启动瞬间响应慢了几十毫秒,就可能被排到后面。再加上热插拔、BIOS盘序设置变化、内核版本升级,设备名被打乱也就不奇怪了。

拿生活里的场景类比:/dev/sdb就像排队时管理员发的临时号码牌,先到先得,号码可以随时重发,但你本人的身份没有变。硬盘真正的"身份"是设备自身的序列号、WWN、文件系统UUID、LVM的PV UUID,而不是这个临时号码牌。

1.2 OSD启动流程里哪一环会被盘符漂移卡住

Ceph OSD要正常工作,需要先把对应的块设备挂载到/var/lib/ceph/osd/ceph-<id>目录,然后由ceph-osd进程检查并读写这个设备。新版Ceph用ceph-volume lvm管理OSD,会把物理盘做成LVM PV,再创建LV,OSD挂载的是LV的设备节点,同时LV的UUID、VG名都写在OSD的元数据里。理论上这个机制对盘符漂移是免疫的:不管物理盘叫/dev/sdb还是/dev/sdc,LVM都能通过UUID找到PV,激活VG,生成LV节点。

但现实里会有几类"雷":

  • 老集群用ceph-disk部署,激活逻辑走udev规则,依赖设备事件和分区标签,某些环节还会引用固定路径;
  • 手工部署或者脚本部署时,为了省事,把/dev/sdb这类固定盘符写进了systemd mount unit、ceph.conf、或者容器挂载参数;
  • 运维在OSD数据盘上单独建了文件系统并写进/etc/fstab,挂载项用了/dev/sdX;
  • 初始化脚本里用parted、mkfs时直接指定了设备名,执行前没有校验UUID。

只要这些"雷"存在,重启后盘符一漂移,OSD的挂载就会失败,进程起不来,mon超过阈值后就会把它标记为down。有些情况下更危险:/dev/sdb漂移后指向了另一块盘,而启动脚本没有校验UUID,直接把错盘挂上,甚至格式化,那就从故障升级成事故了。

1.3 为什么SSD是高发区

机械盘时代盘符漂移一样存在,只是大家习惯性觉得"盘坏了"。到了SSD时代,服务器里常常塞着十几块SSD,又混着NVMe和SATA/SAS,接口和驱动都不一样,枚举时序更难预测。NVMe设备名相对稳定,但PCIe槽位变化、BIOS开关CSM、内核升级都可能让它重新编号。SATA SSD和SAS SSD则继承了SCSI子系统的扫描逻辑,盘序最容易变。所以"SSD盘符漂移"不是玄学,是存储服务器里一个高概率事件,尤其当集群还是手工脚本部署、各种路径写死的环境。

下表是我平时判断设备命名是否稳定时经常对照的:

设备类型命名示例稳定性推荐识别方式
SATA SSD/dev/sda不稳定/dev/disk/by-id/ata-*、by-uuid
SAS SSD/dev/sdb不稳定/dev/disk/by-id/scsi-、wwn-
NVMe SSD/dev/nvme0n1相对稳定但会变/dev/disk/by-id/nvme-、by-path/pci-

2. 复现实验:在虚拟机上折腾出一块"找不到家"的OSD

2.1 实验环境和部署细节

我建议用虚拟机做这类实验,因为可以随意调整磁盘顺序而不影响生产。一个三节点Ceph测试集群比较理想,单节点也能复现,但看不到PG迁移和心跳超时的完整过程。我这里搭了一套Ubuntu 22.04 + Ceph Quincy的三节点集群,其中一个节点挂了4块虚拟盘:一块系统盘vda,三块SSD数据盘vdb、vdc、vdd,控制器用的SCSI类型,方便之后模拟扫描顺序变化。

先正常部署OSD,建议用标准做法:

ceph-volume lvm batch --bluestore /dev/vdb /dev/vdc /dev/vdd

创建完成后,记录一下ceph-volume lvm list的输出,重点看OSD id、fsid和device信息。这一步非常关键,恢复时要靠这些字段确认哪块物理盘属于哪个OSD。

然后我故意埋一颗雷:把osd.0对应的mount unit里的What=从LV的设备节点改成固定盘符/dev/vdb。生产环境没人会刻意这么干,但很多老教程、自研脚本、甚至同事的部署习惯里都有类似写法,我这里只是把这个问题显性化。如果你用的是标准配置,盘符漂移后OSD可能照样起得来,那说明当前环境还没踩到雷,但隐患就在那里。

2.2 三种触发方法,按真实度排序

方法A:在虚拟化平台调整磁盘顺序

关机,在libvirt/VMware里把磁盘插槽顺序调整一下,或者加一块空白盘放到更靠前的槽位,再开机。这样内核扫描SCSI设备时,盘序会整体后移,原来的vdb可能变成vdc或vdd。这最接近真实硬件盘序变化。

方法B:在系统内删除设备再重新扫描

如果你的VM用的是SCSI控制器,可以直接在系统内模拟设备消失再出现:

echo 1 > /sys/block/sdb/device/delete

重新扫描SCSI总线:

for host in /sys/class/scsi_host/host*; do echo "- - -" > $host/scan; done

设备删除后重新出现,内核会重新分配设备号,大概率分配一个新的节点名。这个方法不用重启,适合快速复现,但要注意别在有任何业务IO的机器上试。device/delete的路径只对SCSI设备有效,virtio-blk盘没有这个接口,用VM平台调整顺序更方便。

方法C:直接做配置级故障注入

不重启、不动设备,单纯把mount unit的What=指向一个不存在的路径,也能看到OSD down。但这样只能验证恢复流程,不能体验"盘符漂移"本身,所以我推荐至少做一次A或B。

2.3 故障后的现场

我用方法A做了一次。重启后系统正常起来,lsblk输出里,原来三块数据盘的顺序完全变了,我提前加进去的空盘成了最前面的vdb,原来的三块数据盘顺延为vdc、vdd、vde。此时那个What=/dev/vdb的挂载单元,找到的不是原来的OSD数据盘,而是一块空盘,mount直接失败,OSD起不来。

过了一会,ceph -s出现:

osd.0 down (since 5 minutes)

systemctl status ceph-osd@0显示:

Active: failed

journalctl -u ceph-osd@0里最关键的一行:

Mount process exited with code=32 mount: /dev/vdb: no such file or directory

如果重启后vdb仍然存在,只是变成了一块空盘,mount报错会变成wrong fs type, bad option, bad superblock。别被报错文字绕晕,关键是要确认:当前设备名对应的物理盘,和OSD元数据里记录的物理盘是不是同一块。

3. 排查链路:从"OSD down"到"设备名漂移"的完整取证

3.1 从集群到节点,逐层缩小范围

遇到OSD down,第一件事不是上服务器乱翻,而是先看清集群状态:

ceph -s ceph osd tree ceph health detail

osd.0 down的根因可能在节点宕机、网络分区、盘故障、文件系统错误、盘符漂移。挨个排除:先看节点是否在线,ceph osd find 0能看到OSD所在主机,ssh过去看节点活着没;再看OSD进程是否在跑,systemctl status ceph-osd@0。如果进程已经failed,就可以把注意力放到本机的启动链路和存储设备上。

3.2 日志里找直接原因

在OSD节点上执行:

journalctl -u ceph-osd@0 -n 200

然后看/var/log/ceph/ceph-osd.0.log最后几十行,再配合dmesg | grep -Ei "error|failed|I/O"。留在报错信息里的"设备不存在""mount失败""block device is read-only""superblock on /dev/xxx has bad magic"都是有价值的线索。本例中日志直接指向挂载失败,且失败的源设备路径是我们写死的/dev/vdb。

3.3 用唯一标识确认盘的实际身份

这一步是整个排障的核心:你要证明"原来的OSD数据盘还在机器上,只是改了名字"。判断方法:

  • lsblk -f:看各盘的文件系统UUID、LABEL、挂载点。OSD数据盘如果是ceph-volume管理的LVM,会显示LVM2_member或者bluestore相关的分区信息;
  • ls -l /dev/disk/by-id/ | grep <serial>:用SCSI/NVMe/ATA的序列号去匹配物理盘;
  • ceph-volume lvm list:这个命令直接告诉你有哪几个OSD、对应哪个LV、fsid是什么。如果它能列出osd.0且device路径是/dev/sdc1之类的"新盘符",说明LV元数据还在;
  • pvs、pvscan:看LVM能不能认出PV,PV Name现在是/dev/sdc1还是老的/dev/vdb1。

比如我的实验里,ceph-volume lvm list输出显示osd.0的LV仍然是ceph-<uuid>/osd-0-<fsid>,物理盘对应的设备路径已经变成/dev/sdc。同时lsblk -f显示/dev/sdc上是LVM PV,UUID和之前一致。到这里基本可以确定是盘符漂移,而不是盘坏了。

3.4 三类常见down原因的区别

现象盘符漂移磁盘故障OSD进程崩溃
设备路径磁盘还在,但名字变了设备消失/IO错误设备名没变
日志关键字No such file / mount fail / LVM未激活I/O error、end_request、buffer I/O errorbluefs报错、assert、segfault
smartctl正常大量Reallocated_Sector/UDMA_CRC错误正常
LVM/PV能看到,PV Name可能变化PV不见或IO异常正常
处理方式修正路径并重新激活更换磁盘排查OSD进程/coredump

3.5 最容易被带偏的误区

看到OSD down就急着ceph osd rm重建,是最常见的惯性动作。但你要知道,ceph osd rm会把OSD从CRUSH map里移除,PG会进入degraded,如果这时候再把盘格式化,基本就是主动丢副本。遇到down,先问自己:盘还在吗?数据还完整吗?如果盘还在、数据没丢,优先恢复,而不是重建。另外也别拿着reboot当万金油,盘符漂移场景下盲目重启,可能把盘序再洗乱一次。

4. 恢复步骤:让OSD在设备名变化后重新up的实操序列

4.1 恢复前必须确认的三件事

第一,OSD是否还在集群注册表里。ceph osd tree能看到osd.0,即使down也没关系,说明集群还认这个id。如果连id都没了,那是另一套恢复逻辑。

第二,OSD的fsid和keyring是否还在。通常它们都在/var/lib/ceph/osd/ceph-0/目录下,文件名是fsid、keyring和type。

第三,底层块设备是否被内核识别、LVM是否激活。执行lsblk、pvscan、vgscan,确保OSD所在的VG存在,LV可以激活。如果pvscan没扫到PV,先执行partprobe让内核重新读取分区表;如果VG处于inactive状态,执行vgchange -ay激活。

4.2 先处理掉写死盘符的mount unit

如果像我实验里一样,之前把mount unit的What=写成了/dev/vdb,那么先把这个雷拆了。查看现有unit:

systemctl cat var-lib-ceph-osd-ceph-0.mount

如果What=确实是固定盘符,把它改成LV设备节点或/dev/disk/by-id/路径。以Ceph LVM标准布局为例,What建议用/dev/disk/by-id/ata-<serial>-part1或者/dev/mapper/ceph--<uuid>-osd--0--<fsid>这类稳定路径。改完后:

systemctl daemon-reload

如果这个unit已经被标记failed,可以先reset掉:

systemctl reset-failed var-lib-ceph-osd-ceph-0.mount systemctl reset-failed ceph-osd@0.service

4.3 标准恢复动作:ceph-volume lvm activate

确认完以上信息后,执行:

ceph-volume lvm activate 0 <fsid>

0是OSD id,<fsid>用cat /var/lib/ceph/osd/ceph-0/fsid拿到的值替代。activate会做三件事:检查并激活对应的LV,把LV挂载到/var/lib/ceph/osd/ceph-0,然后通知systemd启动ceph-osd@0.service。因为这个流程依赖LV的UUID而不是固定盘符,所以即使设备的/dev/sdX名字已经变了,也能正确找到数据卷。

如果节点上的OSD不多,也可以:

ceph-volume lvm activate --all

但我建议定点激活单个OSD,否则万一同时有多个OSD在激活时出问题,排障范围会变大。

4.4 当LVM没有自动识别时怎么办

有些场景盘符漂移后,内核认出了盘,但LVM的缓存还是旧的,或者PV就是不出来。可以强制刷一下:

pvscan --cache vgscan --cache partprobe /dev/sdc

再看pvs输出,确认PV Name是不是/dev/sdc1。如果PV Name变了,LVM通常也能自动跟踪;但如果VG元数据里记录的设备名和当前不一致,可能需要:

vgcfgbackup <vg-name> vgreduce --removemissing <vg-name> vgextend <vg-name> /dev/sdc1

这类操作要非常谨慎,只在你确认/dev/sdc1就是原来那块PV时才能做。我的习惯是操作前先把VG元数据备份出来,避免误删。

4.5 恢复后验证

OSD进程起来后,还没完。依次检查:

systemctl status ceph-osd@0 ceph -s ceph osd tree

等待OSD状态从down变成up,再观察PG状态是否从degraded恢复为active+clean。恢复过程中,节点上可能短暂出现ceph-osd@0重启一两次的现象,先别慌,观察日志确认是正常重挂还是又报错。如果反复crash,去查/var/log/ceph/ceph-osd.0.log和dmesg,看是不是还有别的路径引用了旧设备名。

如果OSD已经up,但权重异常,先确认ceph osd tree里的weight是不是0。盘符漂移通常不影响weight,除非有人之前执行过ceph osd out。

4.6 重建是最后手段,什么时候才考虑

如果尝试了上面的方法,OSD还是起不来,并且确认是数据盘真的损坏、文件系统结构损坏无法挂载,或者LVM元数据彻底丢失,才考虑重建。重建流程也有讲究:

ceph osd out 0 # 等待所有PG迁出,数据重新均衡 ceph osd purge 0 --yes-i-really-mean-it

然后找到新盘(或者修好后的同一块盘),用by-id路径重新创建OSD。注意,在osd out到迁移完成的窗口期,集群冗余度是降低的,如果min_size配置是2,手一抖可能连IO都挂了。所以"能救就救"才是正路。

5. 加固方案:让部署配置与物理盘绑定,而不是与盘符绑定

5.1 创建OSD用by-id路径,拒绝/dev/sdX

从创建OSD这一步开始,就避免依赖临时盘符。以前很多人写ceph-volume lvm create --data /dev/sdb,这是坏习惯。正确写法:

ceph-volume lvm batch --bluestore \ /dev/disk/by-id/ata-QEMU_HARDDISK_QM00001 \ /dev/disk/by-id/ata-QEMU_HARDDISK_QM00002 \ /dev/disk/by-id/ata-QEMU_HARDDISK_QM00003

如果盘特别多,可以用/dev/disk/by-path/pci-0000:00:0a.0-scsi-0:2:0:0这样的路径,它绑定物理槽位,比盘符稳定。NVMe直接用/dev/disk/by-id/nvme-eui.xxxx。这套路径在服务器重启后依然指向同一块物理盘,不会因为扫描顺序变化而改变。

5.2 用udev规则给盘起个稳定的"别名"

如果不想记那么长的by-id字符串,可以写一条udev规则,根据磁盘序列号创建稳定链接:

# /etc/udev/rules.d/70-osd-disk.rules SUBSYSTEM=="block", KERNEL=="sd*", ATTRS{serial}=="QM00001", SYMLINK+="disk/osd-data-0"

然后重载并触发:

udevadm control --reload udevadm trigger

之后在mount unit、Ansible脚本、监控脚本里统一用/dev/disk/osd-data-0。这样不管它是sdb还是sdc,你访问的路径永远指向同一块物理盘。注意serial未必全局唯一,有条件优先匹配ID_WWN或ID_SCSI_SERIAL,避免同型号多块盘互相冲突。

5.3 系统层面减少盘序变化

可以从几个层面降低概率:

  • BIOS里固定磁盘槽位顺序,禁用不必要的热插拔扫描;
  • 服务器里同一厂商同型号的盘尽量插在同一控制器下,避免跨控制器乱序;
  • 内核升级、BIOS升级后主动检查一次lsblk和/dev/disk/by-id对应关系;
  • 如果磁盘接了多路径,用/dev/mapper/mpathX或WWN路径,不要用底层sdX。

这些不是强制项,但能很大程度减少"重启后莫名其妙的设备名变化"。至少要做到:升级内核或BIOS后,第一时间检查集群OSD状态,而不是等监控半夜叫你。

5.4 监控与自愈的兜底

无论怎么加固,还是要做好告警。ceph -s里的OSD down是最直观的。更细一层,可以写个巡检脚本,对比ceph-volume lvm list里的device路径和/dev/disk/by-id的映射,如果发现路径对不上,自动修正mount unit并daemon-reload。有人给systemd service加Restart=on-failure,但这只能处理进程崩溃,处理不了mount失败——mount失败的unit会直接进入failed状态,不会触发service restart。所以自愈只能兜底,核心还是要从配置源头根治。

5.5 维护checklist(都是踩过坑才总结出来的)

  • 每次重启前,把lsblk -o NAME,SERIAL,UUID,LABEL,MOUNTPOINT和ceph-volume lvm list各存一份;
  • 建立一张"OSD id -> 磁盘序列号 -> 机房槽位"的对应表,换盘、排查时能省大量时间;
  • 修改任何systemd unit、fstab、脚本里的设备路径时,只允许用UUID/by-id/by-partlabel;
  • 不要顺手执行rescan、device/delete这类操作,除非你知道自己在干什么;
  • 给关键OSD数据盘做smartctl定时巡检,把"盘坏了"和"盘符变了"在第一时间分开。

我自己的一个小习惯是,新接手一个集群,第一件事就是把所有节点的/etc/fstab、systemctl list-units | grep ceph和ceph-volume lvm list全都瞟一遍,把任何写死的/dev/sdX揪出来改掉。这花不了半小时,但能省掉后面无数次凌晨三点被叫醒的麻烦。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询