简介:这份文档面向在 Linux 服务器上做存储运维的工程师与学习者,聚焦一个高频故障场景:同一块存储设备挂载到两台服务器后,其中一台无法挂载或提示存储正忙。资源围绕 Linux 存储管理机制展开,先分析设备文件与多机访问冲突的成因,再给出基于 Device-Mapper 框架的 dmsetup 命令处理思路,包括用 dmsetup remove_all 清理映射后重新挂载,并延伸介绍快照、clone、镜像等映射管理能力,帮助读者建立从现象定位到命令处置的排错路径。压缩包内共 1 个 docx 文档,约 11KB,篇幅精炼,适合作为故障速查与命令参考随手查阅。该资源已有 1650 人学习,说明多机共享存储冲突是不少运维人员实际遇到的痛点,可配合实验环境对照验证,加深对存储映射关系的理解。
1. 共享存储挂载冲突:为什么一台掉线,另一台就“存储正忙”
两台服务器挂同一个块设备,一台先挂上,另一台执行mount直接报device is busy或者干脆卡死;更邪门的是,把先挂的那台卸载甚至关机,第二台还是挂不上,dmsetup里残留一堆mpath映射删不掉。这不是文件系统坏了,而是 Device-Mapper 的映射表没清干净,内核还认为那块盘“有人在用”。我见过最典型的一次,某实验室的共享盘在 A 机umount之后,B 机lsblk能看到盘,mount却一直Structure needs cleaning,最后靠dmsetup remove_all才把死锁的映射关系掀掉。这篇笔记就围绕这个场景,把dmsetup这套工具从“它到底在管什么”讲到“什么时候能放心remove_all、什么时候会翻车”,适合正在被多机共享存储折腾的运维和存储方向从业者照着复现。存储这玩意儿,玄学起来是真玄学,但底层逻辑其实很硬。
2. 先搞懂 Device-Mapper:/dev/sda背后还藏着一层映射
2.1 块设备不是“一个文件”,而是一条映射链
很多人以为/dev/sda就是那块物理盘,mount /dev/sda1 /data天经地义。但在用了多路径(multipath)、LVM、加密卷或者 iSCSI 的环境里,你看到的/dev/sda很可能只是 Device-Mapper 吐出来的一个“虚拟块设备”,真正的物理路径藏在/dev/mapper/下面。内核的块层是这样串起来的:物理 HBA/网卡 → SCSI 设备 →sdX→ DM 映射(dm-0、dm-1)→ 文件系统。mount操作的是最上层那个 DM 设备,而dmsetup管的就是中间这层映射表。
这就解释了一个反直觉现象:你在 A 机umount /data,文件系统层确实释放了,但 DM 映射还挂着,内核的holders计数没归零。B 机通过共享链路看到同一块 LUN,SCSI 层会做 reservation 检查,发现这块盘还被别的 initiator 持有,于是返回BUSY。dmsetup remove_all干的事,就是把当前主机上所有 DM 映射强制拆掉,让内核彻底放手,B 机才有机会重新认到干净的设备。
2.2 多机共享块设备的三种典型拓扑
不是所有“两台机器挂同一个存储”都一样,拓扑不同,坑的位置也不同。下面这张表是我按实际排障经验整理的,先对号入座再动手:
| 拓扑类型 | 典型场景 | 冲突表现 | 是否适合remove_all |
|---|---|---|---|
| 裸 LUN 双挂(无集群文件系统) | 两台机各自mount同一块 ext4 | 第二台mount报 busy 或只读 | 谨慎,先确认无写入 |
| 多路径 + 集群文件系统 | 双机接同一 LUN,跑 OCFS2/GFS2 | 节点 fencing、DLM 锁超时 | 不建议,会触发集群重组 |
| iSCSI 目标共享 | 一台做 target,多台 initiator | initiator 侧残留dm-multipath映射 | 适合,先清 initiator 映射 |
| LVM 共享 VG | 双机激活同一 VG | vgchange -ay报 “VG is already active” | 适合,但先vgchange -an |
关键判断点:有没有集群文件系统或集群卷管理器在协调。如果有,remove_all等于把协调层的地基抽了,轻则挂载点变僵尸,重则触发节点重启。如果没有,纯粹是两台机抢一块裸盘,那remove_all就是那把后悔药。
2.3dmsetup常用子命令与参数速查
dmsetup本身不复杂,难的是知道什么时候该看哪个输出。下面这些是我排障时按顺序会敲的命令,参数含义一并标清楚:
# 1. 看当前所有 DM 映射,重点看 Name 和 Maj:Min dmsetup ls --tree # 2. 看某个映射的详细信息,包括 open count 和 holders dmsetup info /dev/mapper/mpatha # 3. 看映射表内容,确认它指向哪个底层设备 dmsetup table mpatha # 4. 看内核里谁在持有这个 DM 设备 ls -l /sys/block/dm-0/holders/ # 5. 强制移除单个映射(先试这个,别一上来就 remove_all) dmsetup remove mpatha # 6. 移除所有映射(最后手段) dmsetup remove_alldmsetup ls --tree的树形输出能一眼看出mpatha底下挂的是sdX还是sdY,多路径场景下还能看到两条物理路径。dmsetup info里的Open count是关键:如果它不是 0,说明还有文件系统或进程占着,直接remove会报Device or resource busy。/sys/block/dm-0/holders/目录里如果有dm-1之类的条目,说明有上层 DM 叠着,得从上层往下拆。这些输出看着枯燥,但每一条都对应一个“为什么删不掉”的答案。
3. 复现与处置:从“存储正忙”到干净挂载的完整操作链
3.1 先复现问题:两台机抢一块盘的现场还原
要验证处置手法有效,最好先在一个可控环境里把故障复现出来。我用两台虚拟机接同一块 iSCSI LUN 模拟过,步骤不复杂,但每一步的观察点要记清楚:
# A 机:登录 iSCSI 目标并挂载 iscsiadm -m node -T iqn.2024-01.example:storage.lun1 -p 10.0.0.10 -l lsblk # 确认多出 sdb mkfs.ext4 /dev/sdb mkdir -p /data && mount /dev/sdb /data # B 机:登录同一目标,尝试挂载 iscsiadm -m node -T iqn.2024-01.example:storage.lun1 -p 10.0.0.10 -l lsblk # 也能看到 sdb mount /dev/sdb /data # 预期报错:device is busy 或 Structure needs cleaning这里有个容易忽略的细节:B 机lsblk能看到sdb,不代表它能安全挂载。SCSI 层的 persistent reservation 会让 B 机的mount在open()阶段就被拒。如果 B 机之前挂过这块盘、后来异常断开,/dev/mapper/下还会残留mpath映射,这时候lsblk显示的可能是dm-0而不是sdb,mount报的错也会变成wrong fs type或者直接 hang 住。复现时把 A 机umount再poweroff,B 机依然挂不上,就说明残留映射没清,这正是remove_all要处理的局面。
3.2 处置顺序:先软后硬,remove_all是最后一步
血泪经验是:别一上来就remove_all。它确实能解决“存储正忙”,但它不区分映射上有没有正在写的文件系统,强制拆掉可能丢数据。我一般按下面这个顺序走,每一步都确认再进下一步:
# 第一步:确认没有进程占用挂载点 lsof +D /data 2>/dev/null fuser -mv /data # 第二步:正常卸载文件系统 umount /data # 如果报 busy,用 lazy 卸载兜底(注意:lazy 不保证数据已落盘) umount -l /data # 第三步:停掉多路径服务,让 DM 映射自然释放 systemctl stop multipathd multipath -F # 刷新所有多路径映射 # 第四步:检查残留映射 dmsetup ls --tree dmsetup info -c --noheadings -o name,open # 第五步:逐个移除,确认 open count 为 0 再删 dmsetup remove mpatha # 第六步:仍有删不掉的,才动用 remove_all dmsetup remove_all逻辑说明:lsof和fuser是确认“谁在占”,很多时候umount失败只是因为一个tail -f或者没退出的 shell 停在挂载点里。multipath -F比直接remove_all温和,它会让 multipathd 自己把映射收回去,open count归零后dmsetup remove就能成功。dmsetup info -c的-c是 column 格式输出,-o name,open只列名字和打开计数,适合脚本里判断。只有当前五步都试过、映射依然赖着不走,才轮到remove_all兜底。参数上没什么可调的,remove_all不接受过滤条件,这也是它危险的地方——它会把当前主机上所有DM 映射一锅端,包括你没想到的 LVM 卷。
3.3 清完之后:重新扫描与安全挂载
remove_all执行完,/dev/mapper/会空掉,但物理设备不一定自动重新出现。需要手动触发 SCSI 重扫,让内核重新识别 LUN:
# 重新扫描 SCSI 总线,让内核重新发现设备 for host in /sys/class/scsi_host/host*/scan; do echo "- - -" > "$host" done # 确认设备回来 lsblk multipath -ll # 如果用了多路径,确认路径恢复 # 重新挂载,建议先只读挂载验证数据完整性 mount -o ro /dev/sdb /data ls /data # 确认无误后再读写挂载 umount /data && mount /dev/sdb /dataecho "- - -" > scan里的三个短横线分别代表 host、channel、target 的通配,意思是“重新扫描这个 HBA 下的所有目标”。这一步在多路径环境里尤其必要,因为remove_all把dm-multipath的映射拆了,但 SCSI 层的sdX可能还在,重扫能让 multipathd 重新聚合路径。挂载前先mount -o ro是我个人的习惯,remove_all之后文件系统有没有受损不好说,只读挂一遍、ls一下关键目录,比直接读写挂上去然后fsck要稳妥得多。如果只读挂载就报Structure needs cleaning,那说明文件系统元数据已经不一致,得先fsck.ext4 -n干跑一遍看损坏范围,别急着-y修复。
4. 避坑与排查:remove_all前后最容易翻车的五件事
4.1 坑一:remove_all把 LVM 卷一起端了
现象:执行dmsetup remove_all之后,本机/home、/var突然变成只读或者直接 I/O error,df -h里那些 LV 全没了。原因:LVM 的 LV 本身就是 DM 映射(dm-0、dm-1…),remove_all不区分“共享存储的 mpath”和“本地根分区的 LV”,一视同仁全拆。如果根分区或关键数据卷恰好是 LVM,系统当场半瘫。解决:执行前先dmsetup ls --tree把映射列表打出来,对照lsblk和/etc/fstab,确认哪些是本地卷、哪些是共享盘。只删共享盘对应的映射,用dmsetup remove <name>逐个来。真要用remove_all,至少先确认本地关键卷不在 DM 层,或者提前把dmsetup ls的输出存一份,出事能照着重建。
4.2 坑二:umount -l之后以为数据已经落盘
现象:umount报 busy,顺手umount -l返回成功,接着remove_all,重启后共享盘上出现大量零字节文件或目录项错乱。原因:-l是 lazy unmount,它只是把挂载点从命名空间里摘掉,已经打开的文件描述符和未刷盘的脏页还在后台跑。此时remove_all把底层 DM 映射抽掉,脏页写回直接失败,文件系统元数据就烂了。解决:umount -l之后别急着拆映射,先sync,再grep一下/proc/*/mountinfo确认没有进程还引用那个挂载点,必要时kill掉残留进程。等dmsetup info里open count真正归零再动手。共享存储上我宁可多等两分钟,也不愿意事后跑fsck。
4.3 坑三:两台机同时remove_all导致 reservation 冲突
现象:A、B 两台机几乎同时执行remove_all,然后两边都挂不上,dmesg里刷reservation conflict或ABORT_TASK。原因:SCSI persistent reservation 是盘端的锁,两台 initiator 同时发重置类命令,盘端 reservation 状态机进入不确定态,两边都拿不到注册键。解决:共享存储的排障必须串行。约定好先在一台机上操作,确认multipath -ll路径正常、mount -o ro能读,再动另一台。如果已经撞了,在一台机上用sg_persist --clear(需要 sg3_utils)清掉 reservation,另一台保持不动,等第一台挂载稳定后再处理第二台。这个顺序不能反。
4.4 坑四:multipathd没停就删映射,映射自动重建
现象:dmsetup remove mpatha返回成功,几秒后dmsetup ls里mpatha又回来了,mount依旧 busy。原因:multipathd守护进程在监控路径状态,发现映射被删会按配置自动重建,你删得还没它建得快。解决:删映射前先systemctl stop multipathd,或者至少multipath -F让它主动刷新。确认ps aux | grep multipathd没有活动进程后再dmsetup remove。处理完重新挂载前再systemctl start multipathd,让它按正常流程聚合路径。这个顺序搞反了,就会陷入“删了又建”的死循环。
4.5 坑五:重扫 SCSI 后设备名漂移,mount挂错盘
现象:remove_all加重扫之后,原来的/dev/sdb变成了/dev/sdc,脚本里写死的mount /dev/sdb挂到了别的盘上。原因:SCSI 设备名是按发现顺序分配的,重扫或重启后顺序可能变,sdX从来就不是稳定标识。解决:挂载用/dev/disk/by-uuid/或/dev/mapper/下的稳定符号链接,别用sdX。多路径环境用/dev/mapper/mpathX,LVM 用/dev/vgname/lvname。/etc/fstab里如果写的是sdX,趁早改成 UUID,blkid查一下就能拿到。这个坑不只在remove_all场景出现,但重扫之后特别容易踩。
5. 进阶:用dmsetup做快照验证与挂载前自检
dmsetup除了“删映射”,还能在处置前后做两件很有用的事。第一件是挂载前用只读快照验证数据:在确认共享盘可以安全读写之前,先给目标设备做一个 DM 快照,挂快照而不是挂原盘,确认文件系统结构没问题再切到原盘。命令不复杂:
# 创建一个只读快照,COW 设备用一块小 LV 或回环文件 dd if=/dev/zero of=/tmp/cow.img bs=1M count=512 LOOP=$(losetup -f --show /tmp/cow.img) dmsetup create data_snap --table "0 $(blockdev --getsz /dev/sdb) snapshot /dev/sdb $LOOP P 8" # P 表示 persistent,8 是 chunk size(单位扇区) mount -o ro /dev/mapper/data_snap /mnt/check--table里的格式是start length target_type target_args,snapshot目标的前两个参数是源设备和 COW 设备,P让快照在重启后仍可识别,8是 COW 的 chunk 大小。挂快照做ls、fsck -n,原盘一个字节都不动,验证完dmsetup remove data_snap再losetup -d收尾。这招在“不确定remove_all有没有伤到文件系统”的时候特别管用,相当于给自己留了一颗后悔药。
第二件是把挂载前自检固化成脚本。我现在处理共享存储,挂载前必跑一遍下面这个检查链,任何一步不过就不往下走:
| 检查项 | 命令 | 通过标准 |
|---|---|---|
| DM 映射是否干净 | dmsetup ls --tree | 无残留mpath或dm-孤儿 |
| 打开计数是否归零 | dmsetup info -c -o name,open | 目标映射 open 为 0 |
| 多路径路径是否齐全 | multipath -ll | 预期路径数全部active ready |
| 文件系统是否可读 | mount -o ro+ls | 关键目录可列,无 I/O error |
| UUID 是否稳定 | blkid /dev/mapper/mpathX | 与fstab记录一致 |
这套自检看着啰嗦,但每一条都对应上面某个坑。从那以后我每次动共享存储的映射之前,都强制走一遍“停 multipathd → 确认 open count → 逐个 remove → 重扫 → 只读挂载验证”这条链,再也没出现过两台机互相锁死、只能靠重启救场的情况。存储排障没有银弹,把顺序和确认点守住,比记住任何一条命令都管用。希望帮到你。
本文还有配套的精品资源,点击获取