简介:Linux 存储管理中,同一存储设备挂载到两台服务器后,常出现一台已挂载、另一台却无法挂载或提示“存储正忙”的故障。这份资源面向系统运维、存储管理员及需要处理共享存储的工程师,提供了一套完整的问题分析与解决参考。文档从 Linux 设备文件与 Device-Mapper 映射机制切入,清晰解释多服务器同时访问同一存储时产生冲突的底层原因;在此基础上,详细讲解 dmsetup 命令的用法,包括使用 dmsetup remove_all 清除全部设备映射后重新挂载的排错流程,并延伸介绍快照、clone、镜像等存储管理技巧,兼顾原理理解与实操落地。资源为单个 docx 格式技术文档,压缩包内共 1 个文件,大小仅 11KB,内容紧凑,便于快速阅读和查阅。目前已有 1648 人学习下载,适合需要排查存储冲突、学习 dmsetup 命令或提升 Linux 存储管理能力的运维人员;文档虽短,但覆盖了从故障现象、原理分析到解决命令的关键链路,实用性强。
1. 同一个存储挂两台服务器:报错“存储正忙”到底卡在哪
两台服务器共享同一个存储LUN,A节点因为断电、内核panic或者强制重启挂了,B节点接管业务准备挂载这个共享存储,却得到了“存储正忙”或者“device is busy”的报错。这个场景在双机热备、数据库主备切换以及SAN多路径组网里非常普遍,尤其是故障节点不是优雅下电而是被硬生生拉闸的时候,几乎每次都能撞上。很多人遇到这种报错,第一反应是存储阵列坏了,然后换线、换HBA卡、甚至重启存储控制器,绕了一大圈回来,报错还是原样。实际上,根子通常不在硬件端,而在Linux的Device-Mapper映射残留——系统里还留着旧节点对应路径的设备映射,新节点的挂载请求就被内核挡在了块设备层。下面要讲的,就是这一类“共享存储挂不上、提示正忙”的定位思路、处置命令和事后预防,核心操作就一条:dmsetup remove_all。
2. 根因分析:Device-Mapper 残留映射是如何挡住第二条挂载路径的
2.1 先分清“存储正忙”和“无法挂载”:两个报错指向不同层级
在双机共享存储的环境里,B节点报错通常有两种形态:一种是在系统日志里看到“device-mapper”相关告警,随后mount命令返回“device is busy”;另一种是挂载时直接提示“存储正忙”或者“资源暂时不可用”。这两类现象看起来差不多,其实指向的层级完全不一样。“存储正忙”更接近SCSI层面的预留冲突,比如故障节点在存储阵列上留下了持久预留(Persistent Reservation);而“无法挂载、设备忙”则是操作系统块设备层的状态问题——Device-Mapper映射还挂在某个dm设备上,内核认为该设备正处于被占用状态,于是拒绝新的open操作。
我一般收到这类工单时,会让现场工程师先抓三样东西:报错原文、dmesg -T的尾部日志、以及dmsetup ls的输出。这三样基本能决定走哪条排查路线。如果是SCSI层的预留冲突,光在操作系统里删映射是没用的,还要配合存储端释放预留;如果是DM映射残留,才轮到dmsetup命令登场。不少踩坑的人就是把这两类问题混在一起处理,结果在该清理映射的时候去折腾存储,在该释放预留的时候反复删映射,白白浪费几个小时。
2.2 根因拆解:故障节点没有“善后”,映射状态留在了内核里
Device-Mapper是Linux内核的块设备映射框架,LVM、多路径软件(device-mapper-multipath)、dm-crypt这些上层模块都建立在它之上。当两台服务器通过光纤或iSCSI共享同一个LUN时,每台服务器上的多路径软件会基于这个LUN的物理路径(多块HBA卡、多个控制器端口)创建一条逻辑映射,最终呈现为一个类似/dev/mapper/mpathX的设备。
正常情况下,A节点优雅关机时,会依次停止多路径服务、清理DM映射、把SCSI层上下文复位。但如果A节点是断电、内核panic或者被强制重启,这些清理动作全部没有执行。问题在于:共享存储的连接不一定立刻断开,A节点留下的DM映射表项、SCSI指令上下文,会继续被存储端目标器记录。B节点此刻去挂载,内核在发起路径发现、去重、绑定新映射的过程中,发现设备状态与已有上下文冲突,于是直接拒绝,最终表现为“设备忙”或“存储正忙”。
还有一个容易被忽略的细节:如果B节点之前也挂载过这个LUN,并且因为A节点故障触发了多路径切换,那么B节点自己的DM映射表里也可能残留着指向旧路径的表项。这种情况下,即便在B节点上直接执行mount,内核也会因为映射表项冲突而拒绝。所以dmsetup remove_all要清理的,不只是对端留下的脏映射,还包括本机历史运行留下的残留状态。
2.3 dmsetup remove_all 的执行逻辑:为什么它能解开这个死结
dmsetup是Device-Mapper框架的用户态管理工具,负责创建、查询、移除内核中的DM映射设备。dmsetup remove_all的作用是遍历当前系统中所有由Device-Mapper管理的映射设备,逐个尝试删除。它的执行逻辑并不复杂:向内核发送移除指令,内核遍历设备表,只要设备没有处于打开状态(没有被文件系统挂载、没有被进程占用),就移除映射并释放设备号。
这条命令之所以能解决“存储正忙”,本质上是把本机残留的、处于休眠状态的DM映射全部清干净。映射清掉之后,内核里那个“设备已被占用”的状态位随之消失,之后无论是用multipath重新加载路径,还是直接用新映射挂载,都不会再撞上旧上下文。这里有一个关键点需要强调:dmsetup remove_all默认遇到仍在使用的设备会跳过并报“Device or resource busy”,所以安全做法是先卸载文件系统、停止多路径守护进程,再执行清理,这个顺序问题会在第5章详细展开。
# 查看系统当前的 DM 映射设备清单,判断是否有残留 dmsetup ls执行后如果看到很多以mpath开头、或与目标LUN不对应的设备名,就说明存在残留映射。还可以用dmsetup table /dev/mapper/xxx查看某条映射的底层物理设备信息,确认它是否指向你正在排障的那个LUN。这一步多看两分钟,能避免后面误删本地盘映射。
3. 标准处置流程:确认映射、dmsetup remove_all、重新挂载三步走
3.1 挂载前体检:lsblk、dmsetup ls、multipath -ll 三件套
收到“存储正忙”报错后,不要急着删映射。先在B节点上做一轮体检,把设备状态摸清楚。我习惯按顺序执行下面三条命令:
lsblk dmsetup ls multipath -ll这三条命令各有侧重。lsblk负责看块设备树,重点确认目标LUN是否已被操作系统识别,识别出来的设备名是什么;dmsetup ls列出所有DM映射设备,排查是否存在和目标LUN同源的残留映射;multipath -ll则查看多路径软件对这条LUN的路径聚合情况,确认哪几条物理路径在线、哪几条已经掉线。
体检结果分两种情况。如果multipath -ll里还能看到完整的路径组,说明存储链路本身没断,问题大概率在DM映射冲突;如果multipath里根本看不到目标LUN,或者显示“no path”,那就要先解决链路问题,包括光模块、交换机zone、存储端口的状态,再回来处理映射残留。我遇到过不少案例,工程师上来就跑dmsetup remove_all,但实际是链路down了,清理映射完全不解决问题,反而把正常的LVM映射也一并删掉了,平白扩大了故障面。
3.2 执行 dmsetup remove_all:确认环境后一次清理
确认是DM映射残留之后,才轮到清理动作。执行前必须确认两件事:这台服务器上是否有正在使用的本地LVM卷、是否有其他业务挂载点直接走了DM设备。因为remove_all会删除所有DM映射,包括本地LVM创建的系统卷映射和业务盘映射。正确做法是先把能卸载的文件系统都卸载掉,停掉多路径守护进程(如果有),再执行:
# 停掉多路径守护进程,避免边清理边重建映射 systemctl stop multipathd # 清理所有 DM 映射设备 dmsetup remove_all # 查看清理结果,此时应该没有残留输出 dmsetup ls这段命令的逻辑是先停掉multipathd,防止它在我们清理的过程中自动重建映射;dmsetup remove_all此时遍历所有DM设备并移除;最后的dmsetup ls用来确认列表已经空了。注意,我这里故意没有加--force参数。如果执行过程中有设备被进程占用,命令会报“Device or resource busy”并跳过那个设备,这是内核的保护机制,宁可让它跳过,也不要强行删除正在使用的映射。
注意:如果确实有设备报busy,需要回到第5章的操作,先用
lsof/fuser找到占用者,卸载干净后再重新执行remove_all。我在生产环境见过太多事故是工程师图省事,直接dmsetup remove_all --force,结果正在写入的文件系统数据瞬间损坏,连恢复的机会都没有。
3.3 重新加载路径并挂载:partprobe、multipath 刷新与新挂载
清理完成后,不要以为直接mount就一定成功。还要让系统重新发现路径并生成新的映射。这一步根据环境不同有两种选择:如果这台服务器不做多路径聚合,直接重新扫描分区:
# 重新读取分区表,让内核重新识别 LUN 上的分区 partprobe /dev/sdX # 挂载文件系统 mount /dev/sdXp1 /data如果使用多路径软件,则重新加载多路径映射:
# 重新加载多路径映射 systemctl start multipathd multipath -r # 确认新映射设备已经生成 multipath -ll这里的逻辑是:上一步我们把multipathd停了,现在要显式启动它,再通过multipath -r触发内核重新扫描该LUN的所有在线路径,并根据多路径配置文件生成新的dm设备,通常是/dev/mapper/mpathX。之所以先systemctl start multipathd再跑multipath -r,是因为如果顺序颠倒,守护进程可能在刷新过程中自动重建一份不一致的映射。新映射生成后,挂载时优先使用映射名而不是裸设备名,因为多路径场景下裸设备名会随路径变化漂移,而映射名是稳定的。
3.4 挂载后的验证:不只是能mount,还要能写
挂载成功不等于万事大吉。我一般会做三次验证:第一,dmesg -T | tail看有没有新的I/O报错;第二,用dd写一个测试文件再读回来,确认读写链路通;第三,stat -f /挂载点看文件系统容量和可用空间是否正确。
# 写入 100MB 测试文件并强制落盘,验证写链路 dd if=/dev/zero of=/data/test.bin bs=1M count=100 conv=fsync # 读回测试文件,验证读链路 dd if=/data/test.bin of=/dev/null bs=1M count=100这三步都过了,才敢对业务说“存储恢复了”。很多人在这一步翻过车:mount成功了,但一写大文件就报错,回头看dmesg才发现路径组里只剩一条脏路径,读没问题写就失败。所以验证一定要包含实际写入,不能只停在“能看到文件系统”这一层。
4. 共享存储挂载避坑:四条最容易翻车的操作记录
4.1 现象:dmsetup remove_all 报 “Device or resource busy”,删不掉
原因:DM映射设备正被文件系统或进程占用,remove_all的保护机制让它跳过正在使用的设备,结果命令执行完了一看,目标映射还在。很多人在这一步反复执行命令,甚至重启系统,但问题依旧。
解决:先umount /挂载点把文件系统卸掉,再用lsof/fuser找出占用进程,停掉业务后再重新执行dmsetup remove_all。如果文件系统挂在根分区上没法卸载(例如LVM卷承载了系统盘),那就不能直接清理,要把系统引导参数里的该LVM移除,或者从救援系统处理。这里最容易踩的坑是把本地系统盘的LVM映射当成“残留”去清理,一旦删掉根分区映射,系统直接挂起。
4.2 现象:删除映射后,LVM 卷组和逻辑卷全部消失了
原因:dmsetup remove_all把系统里所有DM映射都删了,包括LVM卷组创建的映射。卷组的元数据仍然存在磁盘上,但/dev/mapper/vg-lv这个设备节点没有了,系统看起来就像“丢盘”。如果不清楚LVM的恢复机制,这时候很容易误判为数据丢失。
解决:执行vgscan重新扫描卷组元数据,再执行vgchange -ay激活卷组,系统会重新创建逻辑卷的DM映射。如果环境里开了lvmetad缓存,还要先重启lvm2-lvmetad服务。这个操作本身不丢数据,但容易让人慌,一慌就容易做出更危险的动作,比如对磁盘重新分区。
4.3 现象:remove_all 和重新挂载都做了,还是提示存储正忙
原因:这是SCSI持久预留(Persistent Reservation)在存储端锁着,操作系统层面清理映射不解决根本问题。共享存储一般会配置SCSI-3 PR,故障节点持有预留key,不释放的话,其他节点访问该LUN时会被存储端拒绝。
解决:使用sg3_utils工具包里的sg_persist命令查看预留信息,先确认预留key归属,再在故障节点已被隔离的前提下,用sg_persist --out --release释放预留。释放后重新挂载即可。这里有一条硬性前提:必须确保故障节点不会重新启动并抢占存储,否则会有双写风险,那比报错本身危险得多。
4.4 现象:两台服务器同时挂载同一个共享盘,文件系统几分钟后损坏
原因:ext4、xfs这类普通文件系统不支持多节点并发读写。块设备层可以同时挂载,但文件系统层没有锁协调机制,两边的元数据缓存互相覆盖,很快就会出现目录结构损坏、文件不可见之类的症状。
解决:共享存储只能使用集群文件系统(如GFS2),或者严格的主备模式,同一时刻只有一台服务器挂载。不要靠dmsetup来解决并发写的问题,这不是映射层能管的。遇到“两台都想同时用一块存储盘”的需求,先问架构:到底是需要并发读写,还是只做主备切换?并发读写就该上集群文件系统,主备切换则要保证故障节点被彻底隔离。
5. 高级场景:设备占用、多路径与清理顺序的联动处置
5.1 umount 不掉时如何找到真正的占用者
清理映射的最大障碍是“设备正在使用”。出现这种情况时,不要反复执行umount,先定位占用人。命令顺序如下:
# 查看哪些进程持有该挂载点或设备文件的句柄 lsof /data fuser -v /datalsof会列出进程名和PID,fuser -v会直接显示持有该文件系统的进程。找到进程后,优先正常停止业务进程,而不是kill -9,尤其是数据库类服务,强杀进程可能留下需要回滚的事务,恢复起来比清理映射更麻烦。业务进程实在停不下来时,再考虑fuser -km /data,这个命令会终止所有访问该目录的进程,副作用大,必须确认当前没有关键写操作才能用。这一步做完,umount才能顺利通过,之后再执行dmsetup remove_all,就不会再撞到busy。
5.2 多路径环境:stop multipathd 与 dmsetup remove_all 为什么必须成对出现
在多路径场景中,dmsetup remove_all执行到一半时,如果multipathd还活着,它会立刻检测到映射被删除,又重新创建一份新映射,导致remove_all永远删不完,看起来就像命令失效。这就是为什么第3章的处置流程要先停multipathd。
但停掉multipathd之后,系统的路径状态又没人维护,如果多路径配置文件里写了自动路径分组,重新启动multipathd后生成的映射可能和清理前不一致,需要再一次multipath -r来稳定映射结构。这套顺序,先停服务、再清理、再启动服务、再刷新路径,一步都不能跳。我见过有人在生产环境只做了dmsetup remove_all,没有停multipathd,结果命令跑完映射又被守护进程重建,业务挂载依然失败,还以为是命令无效。
5.3 生产环境操作顺序:不可逆的教训——先停应用,而不是先碰映射
在生产双机系统上做这类操作,我强制要求操作顺序是:停上层应用、卸载文件系统、停多路径服务、清理映射、重新加载路径、挂载文件系统、启动应用。这个顺序不能颠倒,理由很直接:任何一步倒过来,都可能让数据处于不一致状态。
特别是“先清映射再停应用”这种操作,应用进程还在读写的时候底层设备突然消失,文件系统直接进入异常状态,轻则要fsck,重则业务数据损坏。备份和回滚能力也要提前准备好,如果清理过程中出现意外,至少知道如何从备用节点切回。操作前把当前的dmsetup ls输出保存到文件,这就是天然的“后悔药”——出了问题能对照着看哪些映射该在、哪些不该在。
6. 快速定位共享存储故障:三步排查顺序与验证方法
遇到“存储正忙/无法挂载”这类报错时,我建议按固定顺序排查,不要东一榔头西一棒子。
第一步,收集报错现场。抓取dmesg -T | tail -50、mount命令的完整报错原文、以及systemctl status multipathd的输出。这三个信息能区分问题在SCSI层、DM层还是多路径服务本身。
第二步,查映射与路径状态。依次执行lsblk、dmsetup ls、multipath -ll,把三份输出并排比对。如果dmsetup ls存在与目标LUN相同的映射但multipath -ll不完整,走“清理-重建”路径;如果multipath正常但挂载仍报busy,优先怀疑SCSI持久预留,用sg_persist验证。
第三步,按照下表的对应关系确定处置方向。
| 报错现象 | 优先检查 | 处理方向 |
|---|---|---|
| 挂载提示 device is busy | dmsetup ls、lsof | 清理DM映射残留 |
| multipath -ll 无路径 | 链路、光模块、zone配置 | 先解决物理链路 |
| 挂载成功但写入异常 | dmesg、路径组状态 | 刷新路径或更换故障链路 |
| 报错提示存储正忙 | sg_persist查询预留 | 确认并释放持久预留 |
验证方法上,我自己的习惯是:任何涉及共享存储的节点,在重启或关机前,强制要求先看一遍dmsetup ls和multipath -ll的状态并留档;在部署时就提前准备好清理脚本和回滚脚本,放到根目录下,而不是出了故障再临时翻手册。这样做的原因是,我在这类问题上翻过几次车,最惨的一次是收到业务方电话说存储正忙,我没做任何体检就执行了dmsetup remove_all,结果把另一台正常节点的LVM映射也清了,导致逻辑卷全部失效,业务中断了比直接故障更长的时间。从那以后,我每次都强制按“先体检、再定性、后清理”的顺序来,再也没有犯过同样的错。希望这篇笔记能帮你少走一次这个弯路。
本文还有配套的精品资源,点击获取