简介:围绕同一存储设备挂载到两台服务器时,一台正常、另一台无法挂载或提示“存储正忙”的典型故障,这份文档面向 Linux 运维工程师与系统管理员,从设备文件与映射机制入手梳理冲突成因,并给出基于 Device-Mapper 框架的处置思路。读者可了解 dmsetup 命令在存储设备映射管理中的实际用法,包括用 remove_all 清理残留映射后重新挂载的排错路径,以及快照、克隆、镜像等能力在数据备份与冗余中的应用价值,适合作为日常排障时的参考笔记。资源包内为 1 个 docx 文档,约 11KB,体量轻巧、主题集中,便于随查随用。目前已有 1650 人学习下载,可作为存储多机挂载问题的入门对照材料。
1. 一台机器挂了,另一台提示“存储正忙”
半夜接到电话:共享存储上的一块 LUN,原本挂在 A 机上跑得好好的,A 机重启卡在某个服务上不动了,业务要切到 B 机。B 机上umount干净、mount却始终不成功——有时是mount: /dev/mapper/mpatha is already mounted or /data busy,有时干脆光标停在原地十分钟不返回。lsblk卡住,multipath -ll转圈,dmesg里刷着I/O error, dev dm-3。这不是个例,只要你用过共享 LUN、双机接管、或者手工在两台机器上轮流挂同一块盘,早晚会撞上。它折腾人的地方在于:所有提示都在说“忙”,可你翻遍ps也找不到谁在忙。这篇笔记按我实际的排查顺序走一遍,从dmsetup ls --tree看到/sys/block,最后落到那条真正止住故障的命令dmsetup remove_all,以及围绕它必须先做的准备和事后必须补的检查。
2. 为什么“存储正忙”:一块盘的映射链比你想的长
2.1 从 /dev/sda 到 /data,中间隔了四层引用
新手最容易犯的错,是把“这块盘”当成一个东西。在 Linux 里它不是。一块从共享存储映射过来的 LUN,要穿过四层才变成你能cd进去的目录:后端链路(FC 或 iSCSI)把 LUN 交给内核 SCSI 层,内核生成/dev/sdX;多路径软件把同一个 LUN 的多条路径合并成一个/dev/mapper/mpathX;如果上面还跑了 LVM,那又是vg-lv这一层 dm 设备;最上面才是 ext4/xfs 文件系统和挂载点。
每一层都有自己的引用计数,规则很简单也很残酷:上层不放,下层删不掉。你在挂载点这一层看到的是“忙”,但真正攥着不放的,可能是三层的 dm 设备,也可能是内核里一个卡在 D 状态、永远不会返回的 I/O 请求。所以umount成功了不等于盘空了,dmsetup remove报忙也不等于你漏了哪个进程。
看一张对照表,后面每一步排查都从这张表出发:
| 层级 | 设备形态 | 查看命令 | 典型的占用表现 |
|---|---|---|---|
| 链路层 | iSCSI session / FC rport | iscsiadm -m session、ls /sys/class/fc_remote_ports | session 还在,但路径全 down |
| SCSI 层 | /dev/sdX | lsblk、cat /proc/partitions | I/O error、命令 hang |
| 多路径层 | /dev/mapper/mpathX | multipath -ll | 路径 faulty,映射删不掉 |
| device-mapper | /dev/dm-N | dmsetup ls --tree | device or resource busy |
| 文件系统 | /data | findmnt、/proc/mounts | target is busy |
熟手看这张表的价值不在记忆,而在判断顺序:报错文案落在哪一行,就从那一行往上查,而不是一上来就reboot。
2.2 一台机器“挂了”之后,残留是怎么留在内核里的
A 机上的进程被kill -9杀掉、或者整机断电重启,很多人以为“机器都没了,锁自然就释放了”。但共享存储的场景里,有几种东西不会自动消失。
第一种是没返回的 I/O。A 机在掉电前发出了一个写请求,链路断在半路,内核里那个请求的结构体还挂着。这时候进程可能进入 D 状态(不可中断睡眠),kill -9对它完全无效,它持有文件系统的引用,dm 设备就删不掉。
第二种是SCSI-3 持久预留(persistent reservation)。这是存储侧的一套锁机制,防止两台主机同时写同一块 LUN。如果 A 机在拿到预留之后异常退出,预留可能还挂在存储控制器上,B 机发起的 I/O 会直接被拒绝,内核日志里能看到reservation conflict,表现出来就是mount卡住不返回。
第三种是残留的 dm 表项。dm 设备的表存在于内核内存里,和 udev 的/dev节点是两套东西。A 机重启后,如果/dev/mapper/mpatha这个节点还在、对应的表项还在,B 机侧看到的现象就是“这块盘已经有主了”。项目正文里那句dmsetup remove_all,清理的正是这一类东西——把内核里那一串挂着的映射关系全部拆掉,让设备回到“没被任何层接管”的初始状态。
2.3 五种报错文案,分别指向哪一层
我处理过的现场里,报错文案其实相当有规律,认准了能省一半时间:
mount: /dev/mapper/mpatha is already mounted or /data busy:本地挂载点或某个进程攥着这块设备。先查/proc/mounts和fuser,跟存储侧没关系。mount: wrong fs type, bad option, bad superblock on /dev/mapper/mpatha:多路径没就绪,或者你挂的其实是另一块盘的设备节点。先multipath -ll确认路径状态。dmsetup: device or resource busy:dm 层被上层持有,通常是挂载点没卸干净,或者 LVM 还激活着这个 PV。blk_update_request: I/O error, dev dm-3:链路层问题,路径不通、iSCSI session 掉了,或者预留冲突。- 命令敲下去没有任何输出,光标不返回:最麻烦的一种,几乎可以断定是卡在 I/O 上,去看
ps -eo pid,stat,wchan:30,cmd | awk '$2 ~ /D/'。
这五种现象看起来都是“挂不上”,处置路径却完全不同。把“报错文案 → 层级 → 首查命令”这条链路背下来,比记十个偏方有用。
2.4 先分清是“接管”还是“真共用”
有一个反直觉的点值得单独说:绝大多数“存储正忙”的现场,其实不是两台机器同时挂载,而是 A 机没干净退出,B 机想接管。这两件事的处理方式完全相反。
如果是接管,目标是彻底清掉 A 机留下的所有痕迹(映射、预留、session),让 B 机从零开始挂。这时候dmsetup remove_all是合理的,因为你要的就是“全部拆干净”。
如果是真共用,也就是两台机器同时读写同一个文件系统,那么无论怎么清映射都没用——ext4 和 xfs 根本没有分布式锁,两台机器各自缓存元数据,写下去就是互相覆盖。集群文件系统(gfs2、ocfs2 这类)才允许并发挂载,普通文件系统只能单机。我见过有人为了“让两台都能挂”,反复dmsetup remove_all、反复mount,最后盘是挂上了,文件系统元数据也烂了,恢复花的钱比买存储还贵。
判断方法很简单:findmnt看一眼另一台机器上有没有挂载点。如果 B 机是空的、A 机是死的,那是接管,往下走第 3 章;如果两边都有挂载点,先停一台,别急着修。
3. 定位根因:三条命令看清谁占着这块盘
3.1 第一层:挂载点和进程占用
任何“忙”的排查都从最上面开始,因为这一层最便宜,一条命令就能排除。
# 1) 看目标挂载点到底挂没挂、挂的是什么设备 findmnt /data # 2) 看内核眼里的挂载表(findmnt 读的就是它,但这里能看到更多细节) grep -E '/data|mpatha' /proc/mounts # 3) 谁在占用这个挂载点,-m 同时显示进程和挂载点,-v 详细输出 fuser -mv /data # 4) 谁直接打开了这个块设备(有些进程绕开挂载点直接读设备) lsof /dev/mapper/mpatha 2>/dev/null | head -20 # 5) 别忘了 bind mount:容器和某些服务会把目录再挂一次 findmnt -R /datafuser -mv输出里那一列 PID 就是答案,但要留意两点。一是有些进程属于容器命名空间,宿主机上的fuser看不到,得进容器里查;二是处于 D 状态的进程,fuser会显示但kill不掉,这种情况只能等 I/O 超时返回或者重启。
findmnt -R这条我强烈建议加上,它递归列出所有子挂载。曾经有一台机器umount /data报 busy,查了半小时,最后发现是某个服务把/data/cache单独 bind mount 到了别处,父目录自然卸不掉。
如果这一层查出来是干净的——findmnt没输出、fuser没进程——那就别在这耗了,直接往下走。
3.2 第二层:device-mapper 映射树
这一层是整篇文章的核心,也是大多数“存储正忙”真正的根因所在。dmsetup是 Device-Mapper 框架的用户态工具,它能直接看到内核里有哪些映射、谁依赖谁。
# 树状展示所有 dm 设备及其依赖关系,-tree 是关键参数 dmsetup ls --tree # 表格形式,一次看全:名字、是否被挂起、只读、major:minor dmsetup info -c # 看某个映射的具体表项,能看出它底层指向哪个设备 dmsetup table mpatha # 内核视角:某个 dm 设备被哪些上层设备持有 ls /sys/block/dm-*/holders/dmsetup ls --tree的输出大概长这样(不同环境名字不同,但结构一致):
vg_data-lv_app (253:5) └─ (253:3) └─ mpatha (253:2) ├─ (8:16) └─ (8:32)从下往上读:mpatha是两条 SCSI 路径(8:16 和 8:32)合并出来的多路径设备,上面又叠了一个 LVM 逻辑卷vg_data-lv_app。这时候如果你想删mpatha,必须先让vg_data-lv_app消失,否则一定报device or resource busy。
ls /sys/block/dm-*/holders/是另一个视角,它直接告诉你“这个 dm 设备被谁占着”。空目录代表没人用,可以删;有内容就是占用者的名字。这两条命令配合着看,基本不会判断失误。
还有个小细节:dmsetup info -c里有一列Open,非 0 就代表有引用计数。Open是 0 但remove仍然报忙,说明引用来自内核内部(比如一个卡住的 I/O),这就不是用户态能解决的了,得看第 4.4 节。
3.3 第三层:多路径与后端链路
如果 dm 层是干净的,问题就在更下面。这一层的判断标准是“路径是否活着”,而不是“有没有占用”。
# 看多路径拓扑和每条路径的状态,faulty 表示这条链路不通 multipath -ll # iSCSI 场景:看 session 是否还在、哪条链路掉了 iscsiadm -m session -P 3 | grep -E 'Target|State|Iface' # 检查这块盘上有没有 SCSI 持久预留(需要 sg3_utils) sg_persist -c -d /dev/mapper/mpatha # 内核最近的 I/O 报错,重点看 dm- 和 sd 开头的行 dmesg -T | grep -Ei 'I/O error|reservation|dm-|sd[a-z]+' | tail -30multipath -ll的输出里,status=active或者ready代表路径正常,faulty或者failed代表这条路径废了。如果四条路径全是 faulty,那这块盘本质上已经不可用了,再怎么删映射也挂不上——得先修链路。
sg_persist -c这条命令很多资料里不提,但在双机接管场景里非常关键。-c是 report capabilities,如果返回里有Persistent Reservation Type之类的字样,说明这块盘被预留了。预留不清,另一台机器发起的写请求全部返回reservation conflict,表现出来就是挂载卡死。清理的办法是sg_persist --clear -d /dev/mapper/mpatha,但执行前必须确认持有预留的那台机器已经彻底下线,否则会破坏对方的数据一致性。
dmesg -T加-T是为了显示人类可读的时间戳,排查“什么时候开始出问题”时特别有用。
3.4 排查顺序表:别跳过中间层
把三层的判断串起来,形成一张固定的顺序表:
| 步骤 | 动作 | 判定 | 下一步 |
|---|---|---|---|
| 1 | findmnt /data+fuser -mv | 有进程 → 停进程;无 → 继续 | 继续 |
| 2 | dmsetup ls --tree | 有上层映射 → 先删上层;只有目标 → 继续 | 继续 |
| 3 | ls /sys/block/dm-*/holders/ | 非空 → 找持有者;空 → 继续 | 继续 |
| 4 | multipath -ll | 全 faulty → 修链路;有 active → 继续 | 继续 |
| 5 | sg_persist -c | 有预留 → 清预留 | 继续 |
| 6 | dmsetup remove_all | 清空 dm 残留 | 重扫挂载 |
这个顺序的逻辑是从软到硬、从可逆到不可逆。前四步都不改变任何状态,只是观察;第五步开始动手,一旦出错影响面就大了。新手最容易犯的错是直接从第 6 步开始——不行就reboot,不行就remove_all,运气好能解决,运气不好就是第 5 章里那些事故。
4. 解决:dmsetup remove_all 的分寸
4.1 动手前的三件保命事
dmsetup remove_all这条命令的名字已经说明了它的行为:删除所有device-mapper 映射。注意是“所有”,不是“目标那块盘的所有”。这条命令在项目正文里被直接给出,但我必须把风险摆在前面。
第一件事,确认目标盘不是承载根分区的盘。现代发行版默认把根分区放在 LVM 上,/dev/mapper/vg_root-lv_root就是一个 dm 设备。你执行remove_all的那一刻,根分区的映射被拆掉,机器立刻 I/O error,剩下的操作只能在救援模式里做。执行前必须跑一遍:
# 确认根挂载点的设备名,如果不是 /dev/mapper/ 开头,风险低一些 findmnt -no SOURCE / # 完整列出所有 dm 设备,逐个确认哪些是系统盘 dmsetup ls --tree > /var/tmp/dm_snapshot_$(date +%F_%H%M).txt cat /var/tmp/dm_snapshot_$(date +%F_%H%M).txt把快照存下来是第二件事。这份文件在事后恢复时有奇效——万一映射删多了,至少知道原来有哪些、底层指向什么。
第三件事,确认业务可以中断。清映射意味着这块盘上的所有 I/O 都会失败,正在写的文件可能直接损坏。如果/etc/fstab里有这块盘的自动挂载项,先注释掉,否则重启时会因为挂载失败卡在紧急模式。
提示:如果根分区在 LVM 上,又必须清理 dm 残留,正确做法是只删目标映射,用
dmsetup remove <name>精确操作,绝不能用remove_all。第 4.3 节给了替代写法。
4.2 标准处置流程:十步照着敲
这套流程我在共享存储接管场景里跑过很多次,顺序不要调换,每一步都有存在理由。
# 步骤 1:停止业务,卸载挂载点。普通卸载失败时先用 lazy 卸载 umount /data || umount -l /data # 步骤 2:确认没有进程还占着挂载点,有就停掉 fuser -mv /data # 步骤 3:归档当前的 dm 映射树,出事好回查 dmsetup ls --tree > /var/tmp/dm_before_$(date +%F_%H%M).txt # 步骤 4:如果上面有 LVM,先停掉卷组,否则删不掉底层的 mpath vgchange -an vg_data 2>/dev/null || true # 步骤 5:停掉 multipathd,避免它在你删除的同时把映射重建回来 systemctl stop multipathd # 步骤 6:清空多路径映射(-F 是 flush all,慎用,确认只有目标盘) multipath -F # 步骤 7:清空 device-mapper 残留映射,项目正文里给出的关键一步 dmsetup remove_all # 步骤 8:确认真的清干净了,应该没有任何与目标盘相关的输出 dmsetup ls # 步骤 9:重新扫描 SCSI 总线,让内核重新发现 LUN rescan-scsi-bus.sh -r # 步骤 10:拉起 multipathd,重建多路径,然后重新挂载 systemctl start multipathd multipath -r multipath -ll mount /data && df -hT /data逐条说明为什么这么排。
第 1 步的umount -l是 lazy 卸载,它只是把挂载点从命名空间里摘掉,真正释放设备要等所有引用关闭。所以第 2 步紧接着查进程,这两个动作必须成对出现——只umount -l不查进程,后面dmsetup remove照样报忙,会让人误以为命令不生效。
第 4 步的vgchange -an是针对 LVM 场景的。LVM 逻辑卷本质上就是叠在 mpath 上面的一层 dm 设备,卷组不 deactivate,mpatha永远有上层持有者。2>/dev/null || true是为了在没装 LVM 或者卷组本来就不存在时不中断脚本。
第 5 步停 multipathd 是最容易被忽略的一步。multipathd是常驻进程,它会周期性检查路径状态,发现映射没了会自动重建。你删掉mpatha,几秒后它又回来了,现象就是“dmsetup remove_all执行成功但没效果”。先停服务再删,顺序不能反。
第 6 步multipath -F清的是多路径层的映射,第 7 步dmsetup remove_all清的是 dm 层的表项,两者有重叠但不完全等价。有些环境里multipath -F之后dmsetup ls仍然能看到残留的mpatha,那就必须靠第 7 步。
第 9 步rescan-scsi-bus.sh -r来自sg3_utils包。如果环境里没装,可以用等价的 sysfs 写法:
# 遍历所有 SCSI host,让每条总线重新扫描全部通道/目标/LUN for host in /sys/class/scsi_host/host*/scan; do echo "- - -" > "$host" done三个短横线分别代表 channel、target、LUN,全写-表示“全扫一遍”。这条命令是让内核重新发现存储侧暴露的 LUN,不走这一步,即使 dm 层清干净了,/dev/sdX也不会重新出现。
第 10 步的multipath -r是重新加载多路径配置并重建映射,multipath -ll再确认一遍路径状态。如果这时候multipath -ll返回空,说明存储侧根本看不到这块 LUN,问题不在主机侧,得去查 LUN 映射和 zone 配置。
4.3 remove_all 的三个风险,以及更精准的替代
dmsetup remove_all之所以危险,是因为它没有“确认”环节,也不会跳过系统设备。三个具体风险:
风险一:连带删除根分区映射。前面说过,vg_root-lv_root也是 dm 设备。一旦删除,内核立即失去根文件系统的块设备,表现是整机卡死、dmesg刷I/O error, dev dm-0,只能进救援模式用vgchange -ay重建。
风险二:有 D 状态进程时删除失败但状态混乱。remove_all会尝试删除每一个映射,删到被持有的那个会返回device or resource busy,但前面的已经删了、后面的没删,系统处于半完成状态,比不删更难诊断。
风险三:udev 事件没同步导致设备节点残留。dm 设备删除后,/dev/mapper/下的符号链接由 udev 异步清理。删除速度太快时,会出现“内核里映射没了、/dev/mapper/mpatha还在”的错位,后续mount会挂到一个不存在的设备上。
更精准的写法是逐个删除目标设备:
# 先确认目标设备的完整名字,不要凭记忆写 dmsetup ls --tree | grep -i mpath # 精确删除单个映射,-f 表示强制(仅在确认无上层依赖时使用) dmsetup remove mpatha # 如果报 busy,用 deferred 延迟删除:等最后一个引用关闭后自动清理 dmsetup remove --deferred mpatha # udev 同步卡住时,手动触发一次同步,避免设备节点残留 dmsetup udevcomplete_all--deferred这个参数值得单独记一下。它的语义是“现在标记删除,等所有打开者关闭后由内核自动执行”。对于那些引用来自内核内部、用户态根本无从释放的场景,这是唯一能优雅退出的方式——命令立即返回,设备在后台慢慢消失,不用一直挂着等。
dmsetup udevcomplete_all是善后用的。如果你在dmesg里看到dm-X: udev cookie之类的信息、或者dmsetup ls已经空了但/dev/mapper还有节点,跑一次这个命令让 udev 把积压事件处理完,再重新扫描设备。
4.4 卸不掉时的组合拳:lazy umount + deferred remove
有一种现场,前面所有步骤都做了,dmsetup remove还是报忙,fuser显示一个 D 状态进程,kill -9毫无反应。这时候硬刚是没用的,D 状态进程处于内核态不可中断睡眠,信号递不进去,只能等它的 I/O 超时或者返回。
判断方法:
# 列出所有 D 状态进程,wchan 显示它卡在哪个内核函数上 ps -eo pid,stat,wchan:30,cmd | awk '$2 ~ /D/' # 如果是块设备相关的等待,重点看 dm_ 和 blk_ 开头的 wchan看到wchan是dm_io、blk_mq_...之类,基本可以确认是卡在 I/O 上。这时候的组合打法是:
# 1) 先 lazy 卸载,断开名字空间引用 umount -l /data # 2) 用 deferred 方式标记删除,不阻塞等待 dmsetup remove --deferred mpatha # 3) 处理可能卡住的 udev 同步 dmsetup udevcomplete_all # 4) 观察几秒,确认映射是否已经消失 watch -n 2 'dmsetup ls'如果dmsetup ls中mpatha从列表里消失了,说明内核已经接受了删除请求,只是在等最后的引用归零,这时候可以继续做重扫和挂载。如果几分钟后它还在,说明卡住的 I/O 不会自然返回,只能重启——但要先sync,并且接受这块盘上未落盘数据丢失的事实。
注意:
umount -l之后立刻mount同一个挂载点是无效的,因为旧的文件系统实例还在后台挂着。必须等findmnt /data返回空,才算真正卸载完成。
5. 避坑清单:五个让我半夜爬起来的“存储正忙”
5.1 坑一:umount 显示成功,dmsetup 照样报 busy
现象:umount /data没有任何报错,返回码 0,紧接着dmsetup remove mpatha依然device or resource busy。
原因:umount成功只代表挂载点从当前命名空间摘除了,不代表引用计数归零。常见的三个隐藏持有者是:容器的 bind mount(宿主机findmnt看不到)、被删除了但仍被进程打开的文件(lsof +L1能查到)、以及 NFS 或 loop 设备的延迟释放。
解决:findmnt -R /data递归检查子挂载;lsof +L1 | grep mpatha找已删除但仍打开的文件;cat /proc/*/mountinfo 2>/dev/null | grep data从进程角度反查。实在找不到,dmsetup remove --deferred先标记,等引用自然归零。
5.2 坑二:两台机器真同时挂了,文件系统写坏
现象:接管操作做完,盘挂上了,但ls报Structure needs cleaning,xfs 直接Metadata corruption detected,dmesg里一堆 inode 相关告警。
原因:前面反复强调过,ext4 和 xfs 不是集群文件系统。两台机器各自维护自己的缓存和日志,同时写同一块盘,元数据互相覆盖,损坏是必然的,只是时间问题。
解决:第一步立刻在其中一台上mount -o remount,ro或umount,只保留一台可写;第二步备份能读出来的数据;第三步xfs_repair -n先做只读检查,确认损坏范围后再决定是否-L清日志。事后必须改架构——要么用集群文件系统,要么用应用层的主备切换,不要指望文件系统自己解决并发。
5.3 坑三:重启后盘符变了,挂错了盘
现象:机器重启后/dev/sdc变成了/dev/sdb,/etc/fstab里写死的设备名挂到了另一块盘上,业务读到了错的数据。
原因:/dev/sdX的编号由内核扫描顺序决定,跟插槽、链路建立时间都有关系,本来就不是持久标识。用它写 fstab,等于把挂载结果交给运气。
解决:改用 UUID 或 LABEL,blkid /dev/mapper/mpatha拿到 UUID 后写进 fstab;多路径环境更推荐直接用/dev/mapper/mpathX这种由multipath.conf里alias指定的固定名,因为mpathX的编号也是扫描顺序,真正固定的是你自己配的 alias,比如/dev/mapper/data_lun0。
5.4 坑四:remove_all 顺手清掉了系统盘映射
现象:执行dmsetup remove_all的瞬间终端失去响应,几秒后dmesg刷屏I/O error, dev dm-0,整机不可用。
原因:根分区在 LVM 上,vg_root-lv_root同样是 dm 设备,被remove_all无差别删除。
解决:只能进救援模式,vgchange -ay重新激活卷组,或者从快照恢复。预防手段是执行前的快照归档 + 用dmsetup remove <name>精确删除。这条坑我踩过一次,从那以后所有涉及 dm 的操作,第一步都是dmsetup ls --tree存档。
5.5 坑五:旧主机的 SCSI 预留没释放,新主机一直卡
现象:新主机上mount命令敲下去,光标停住不返回,dmesg里能看到reservation conflict,但multipath -ll显示所有路径都是 active。
原因:旧主机在拿到 SCSI-3 持久预留之后异常退出,预留没有主动释放,仍然挂在存储控制器上。新主机的 I/O 被存储侧直接拒绝。
解决:sg_persist -c -d /dev/mapper/mpatha确认有预留,sg_persist --clear -d /dev/mapper/mpatha清除。执行前必须百分百确认旧主机已经离线、不会再访问这块盘,否则清掉预留的瞬间旧主机的写入就会和新主机冲突。如果主机侧清不掉,就得在存储管理界面里强制释放,这一步通常会通知到存储管理员。
6. 快速定位:把“存储正忙”压进五分钟
现场处置最耗时的从来不是解决,而是判断“该往哪看”。前面几章的方法论落到实操,其实就是一张对照表——看到什么信号,跑哪条命令,得出什么结论。
| 现场信号 | 第一条命令 | 结论方向 |
|---|---|---|
mount报 already mounted / busy | findmnt -R /data && fuser -mv /data | 本地占用,跟存储无关 |
dmsetup remove报 busy | dmsetup ls --tree+ls /sys/block/dm-*/holders/ | 有上层映射没拆,先删上层 |
multipath -ll全 faulty | iscsiadm -m session -P 3或dmesg -T | grep fc | 链路问题,修链路再谈挂载 |
| 命令 hang 住不返回 | ps -eo pid,stat,wchan:30,cmd | awk '$2 ~ /D/' | 卡在 I/O,用 deferred 或重启 |
dmesg有 reservation conflict | sg_persist -c -d /dev/mapper/mpatha | SCSI 预留没释放 |
| 挂上后报元数据损坏 | xfs_repair -n/fsck -n | 两台同时写过,先停一台 |
这张表的用法是从上往下扫,命中即停,不要跳步。我见过太多人直接从第三行开始查链路,结果根因是第一行的fuser里那个没停的服务。
还有两个通用习惯值得养成。一是任何修改前先存档,dmsetup ls --tree、multipath -ll、findmnt三条命令的输出各存一份到/var/tmp,文件名带时间戳。出问题时这份存档就是黑匣子,能告诉接手的人“原来是什么样”。二是每次操作只改一个变量,删完映射先dmsetup ls确认,再重扫,再重挂,中间任何一步异常都能立刻定位到是哪个动作引起的。混在一起做,失败了你连是哪一步的锅都说不清。
验证修复是否彻底,不看mount成不成功,看三件事:findmnt /data的设备名是不是你预期的那个mpathX;multipath -ll里路径状态是不是全部 active;dmesg -T | tail -20里还有没有新的 I/O error。三条都干净,才算收工。只挂上了但底下路径还在抖,过几小时又会以“存储正忙”的形式回来找你。
从那以后我每次碰共享存储,不管多急,都强制走一遍“快照—停 multipathd—精确删—重扫—验证”这套动作,哪怕它看起来只是个小问题。血泪经验告诉我,跳过的每一步最后都会以另一种形式补回来。希望帮到你。
本文还有配套的精品资源,点击获取