1. 为什么单路径挂载在企业存储场景里迟早要出问题很多同行第一次接触多路径都是在生产环境已经出过一次事故之后。典型场景是这样的一台CentOS 7的数据库服务器通过两块HBA卡连到后端SAN存储操作系统识别出两块盘运维图省事随手把其中一块挂载到业务目录上另一块放着不管。平时跑得好好的直到某天那块被挂载的链路因为光纤模块老化、交换机端口抖动或者存储控制器固件升级而中断业务直接IO报错数据库hang住这时候才发现——原来另一条路根本没接上。这个问题的本质在于Linux内核默认把每一条物理路径都当成一块独立的SCSI磁盘。你的服务器和存储之间有几条物理链路/dev/sd*下面就会多出几个设备节点。这些节点指向的是同一份后端LUN数据但内核不知道它们是一回事。所以单路径挂载的问题不是性能差一点而是可用性直接归零——挂载的那条路断了业务就断了另一条路形同虚设。Multipath多路径要解决的就是这个问题。它在SCSI层和块设备层之间插入一个设备映射device-mapper层把指向同一个LUN的多条物理路径聚合成一个逻辑设备比如/dev/mapper/mpatha。上层业务只认这个聚合设备底层哪条路通就走哪条一条断了自动切到另一条对文件系统和数据库完全透明。同时它还能做路径负载均衡把IO分散到多条链路上吞吐量比单路径有明显提升。这篇文章面向的是需要在CentOS 7上落地多路径存储的运维和DBA。我会从环境准备、配置落地、参数调优、故障排查几个维度把我在实际项目里踩过的坑和验证过的参数完整讲一遍。内容偏实操假设你已经有一台接了SAN存储的CentOS 7服务器lsscsi能看到多块同型号同容量的盘。如果你还在用单路径挂载生产库建议先把这篇文章看完再找个窗口期改过来。提示多路径配置涉及存储链路任何改动都建议在业务低峰期进行并且提前确认后端存储侧的LUN映射和主机组配置已经就绪。改配置前先备份/etc/multipath.conf出问题能快速回滚。2. 动手之前CentOS 7上的环境确认与依赖梳理2.1 确认多路径软件包是否已经就位CentOS 7的安装镜像里默认就带了device-mapper-multipath但最小化安装minimal往往没有装上。先确认一下rpm -qa | grep multipath如果输出里有device-mapper-multipath-0.4.9-xxx.el7.x86_64说明已经装了。没有的话用yum装yum install -y device-mapper-multipath这里有个细节值得说CentOS 7自带的multipath版本是0.4.9这个版本比较老但胜在稳定和RHEL 7的生命周期绑定。如果你的环境有离线仓库或者内网yum源优先用系统自带的版本不要随便去编译新版因为multipath和内核的device-mapper模块是有版本耦合的乱升级容易出兼容问题。装完之后相关的几个关键文件位置要记清楚文件路径作用/etc/multipath.conf主配置文件默认不存在需要手动创建/etc/multipath/配置目录可放子配置/usr/sbin/multipath主程序/usr/sbin/multipathd守护进程/usr/sbin/mpathconf配置生成工具/etc/udev/rules.d/udev规则影响设备命名2.2 用lsscsi和multipath -ll看清当前链路状态在改任何配置之前先摸清楚现状。第一步看物理盘lsscsi你会看到类似这样的输出[0:0:0:0] disk DELL PERC H730P 4.30 /dev/sda [1:0:0:0] disk DGC VRAID 0533 /dev/sdb [2:0:0:0] disk DGC VRAID 0533 /dev/sdc [3:0:0:0] disk DGC VRAID 0533 /dev/sdd [4:0:0:0] disk DGC VRAID 0533 /dev/sdeDGC VRAID是EMC存储的标识sdb到sde四块盘容量一样、型号一样基本可以判断是同一个LUN的四条路径。这时候如果直接multipath -ll在没配置的情况下可能什么都不显示或者显示一堆独立的设备。判断是不是同一个LUN最可靠的方法是看/sys/block/sdX/device/下面的vendor、model、rev以及更关键的WWID。WWID是存储分配给LUN的全球唯一标识同一个LUN的所有路径WWID相同。用这个命令批量看for i in /dev/sd{b,c,d,e}; do echo -n $i: /lib/udev/scsi_id -g -u -d $i done如果四块盘输出的WWID完全一致那就确认是同一个LUN的多条路径可以放心做聚合。这一步千万别跳过我见过有人把两个不同LUN的盘误判成多路径聚合之后数据直接错乱后果很严重。2.3 存储侧的主机组与LUN映射确认这一块经常被忽略但它是多路径能生效的前提。后端存储不管是EMC、NetApp、华为还是浪潮都需要把LUN映射给主机时把主机的所有HBA卡WWN都加到同一个主机组里。如果只加了其中一块HBA的WWN那另一条路径根本不会下发LUN你在服务器上只能看到一条路径多路径自然无从谈起。确认方法在存储管理界面查看主机组确认里面包含了服务器上所有HBA卡的WWN。WWN可以在服务器上用这个命令看cat /sys/class/fc_host/host*/port_name输出是0x开头的16进制字符串把它和存储侧的主机组配置对一下。如果发现少了某块卡的WWN先在存储侧补上再重新扫描链路echo - - - /sys/class/scsi_host/host0/scan echo - - - /sys/class/scsi_host/host1/scan每个host目录都要扫一遍扫完再用lsscsi确认新路径是否出现。3. 从零写一份能上生产的multipath.conf3.1 用mpathconf生成基础配置再手工精修CentOS 7提供了一个便捷工具mpathconf可以一键生成基础配置并启动服务mpathconf --enable --with_multipathd y --user_friendly_names y这条命令做了三件事生成/etc/multipath.conf、启用multipath服务、开启用户友好名称把/dev/mapper/3600xxx这种长WWID映射成/dev/mapper/mpatha这种短名字。生成的配置文件里大部分是注释真正生效的配置很少需要手工补充。我个人的习惯是不用user_friendly_names。原因很简单mpatha、mpathb这种名字是multipath按发现顺序分配的服务器重启或者加了一块新盘之后名字可能变。而WWID是固定的用WWID做设备名脚本和fstab里引用起来永远不会错。所以我的配置里通常把user_friendly_names设为no直接用WWID。3.2 defaults段这些参数决定了故障切换的快慢defaults段是多路径的全局默认参数对没有单独配置的存储都生效。下面是我在生产环境验证过的一套配置逐条解释defaults { polling_interval 10 path_selector service-time 0 path_grouping_policy multibus failback immediate no_path_retry 5 rr_min_io_rq 1 rr_weight uniform max_fds 8192 queue_without_daemon no flush_on_last_del yes user_friendly_names no fast_io_fail_tmo 5 dev_loss_tmo 30 }polling_interval 10multipathd每隔10秒检查一次路径状态。这个值不能太小太小会增加系统开销也不能太大太大会导致故障发现延迟。10秒是经验值兼顾了及时性和开销。path_selector service-time 0路径选择算法。service-time 0表示按每条路径的预估服务时间动态选择哪条路当前积压的IO少就走哪条。相比老式的round-robin 0它在链路性能不一致时表现更好。如果你的多条链路性能完全对等用round-robin 0也行但service-time 0是更稳妥的选择。path_grouping_policy multibus路径分组策略。multibus表示所有路径放在一个组里所有路径同时可用IO可以走任意一条。这是Active/Active存储的标准配置。如果是Active/Passive存储比如某些老款EMC CX系列要用failover同一时间只有一组路径活跃。failback immediate故障路径恢复后立即切回。有些场景下频繁切换反而不好可以设成manual或者一个延迟值比如failback 30表示30秒后切回。对于大多数Active/Active存储immediate是合适的。no_path_retry 5所有路径都断了之后IO重试5次再返回失败。这个参数很关键。设成queue表示无限排队等待路径恢复适合对可用性要求极高、能容忍IO hang的场景设成具体数字表示重试N次后失败适合能快速感知故障并做上层切换的场景。5是个折中值。fast_io_fail_tmo 5和dev_loss_tmo 30这两个是SCSI层的超时参数。fast_io_fail_tmo表示链路出问题后5秒内快速失败把IO切到其他路径dev_loss_tmo表示30秒后彻底移除故障设备。这两个值配合使用能显著缩短故障切换时间。默认的dev_loss_tmo是600秒太长了生产环境一定要调小。3.3 devices段针对具体存储型号做精细化配置defaults是全局的但不同存储的行为差异很大所以要用devices段针对具体型号覆盖。以EMC VNX/PowerStore为例devices { device { vendor DGC product VRAID path_grouping_policy multibus path_selector service-time 0 failback immediate no_path_retry 5 rr_min_io_rq 1 fast_io_fail_tmo 5 dev_loss_tmo 30 } }vendor和product的值要和multipath -ll或者/sys/block/sdX/device/vendor里看到的完全一致大小写敏感。写错了这段配置就不生效会回落到defaults。不同存储的推荐参数不一样下面这张表是我整理过的常见存储配置要点存储厂商vendorproduct分组策略备注EMC VNX/PowerStoreDGCVRAIDmultibusActive/ActiveNetAppNETAPPLUNmultibus需配合ALUA华为 OceanStorHUAWEIXSG1multibusActive/Active浪潮 AS系列InspurMCSmultibus确认ALUA支持老款EMC CXDGCRAIDfailoverActive/Passive3.4 blacklist段把本地盘和不需要聚合的设备排除掉这一步非常重要但经常被漏掉。服务器本地的RAID卡虚拟盘、USB设备、虚拟光驱等如果被multipath接管会引发各种奇怪问题。所以要在blacklist段里排除blacklist { devnode ^sd[a-z]$ device { vendor DELL product PERC.* } wwid 3600508b400105e210000900000490000 }devnode ^sd[a-z]$这条规则会把/dev/sda到/dev/sdz全部排除。等等这样会不会把SAN盘也排除了不会。因为multipath处理的是/dev/sd*设备但blacklist的devnode匹配的是设备节点名而SAN盘在multipath眼里是通过WWID识别的devnode规则主要针对本地盘。不过为了保险更精确的做法是用device段按vendor/product排除本地RAID卡或者用wwid精确排除特定设备。我一般用组合方式先用device段排除本地RAID卡比如DELL PERC、HP Smart Array再用wwid排除个别不需要的LUN。这样最精确不会误伤。配置改完之后重新加载systemctl restart multipathd multipath -F multipath -v3multipath -F是清除现有的多路径映射multipath -v3是详细模式重新发现。执行完再用multipath -ll确认聚合结果。4. 聚合之后验证、挂载与文件系统选择4.1 用multipath -ll读懂聚合结果配置生效后multipath -ll的输出是判断多路径是否正常的关键依据。一个健康的输出长这样360060160a0b31e0074f4e65e3a6ce911 dm-3 DGC,VRAID size500G features1 queue_if_no_path hwhandler1 alua wprw |-- policyservice-time 0 prio50 statusactive | |- 1:0:0:0 sdb 8:16 active ready running | |- 2:0:0:0 sdc 8:32 active ready running |-- policyservice-time 0 prio10 statusenabled | |- 3:0:0:0 sdd 8:48 active ready running | |- 4:0:0:0 sde 8:64 active ready running解读一下第一行是WWID和device-mapper设备名dm-3后面是存储型号。size500G是LUN容量。下面分两个路径组第一组prio50是优先组包含sdb和sdc两条路径状态都是active ready running第二组prio10是次优先组包含sdd和sde。这是ALUAAsymmetric Logical Unit Access的典型表现——存储控制器有主备之分主控制器的路径优先级高。如果所有路径都在一个组里prio值相同那就是标准的Active/Active。如果看到statusfailed或者statusfaulty说明那条路径有问题需要排查。4.2 分区、格式化与挂载的注意事项聚合设备/dev/mapper/360060160a0b31e0074f4e65e3a6ce911就是我们要用的块设备。分区和格式化跟普通盘一样parted /dev/mapper/360060160a0b31e0074f4e65e3a6ce911 mklabel gpt parted /dev/mapper/360060160a0b31e0074f4e65e3a6ce911 mkpart primary 0% 100% mkfs.xfs /dev/mapper/360060160a0b31e0074f4e65e3a6ce911-part1这里有个坑要提醒不要用/dev/sdb这种物理路径去分区和格式化。如果你对物理路径做了分区multipath聚合出来的设备上也会看到这个分区表但一旦路径切换物理设备名变了引用就乱了。永远只操作/dev/mapper/下面的聚合设备。文件系统选择上XFS是CentOS 7的默认对多路径设备的支持很好大文件性能优秀。如果是Oracle数据库通常用ASM直接管理裸设备跳过文件系统层。如果是文件服务器XFS或ext4都可以XFS在并发和大文件场景下更有优势。挂载到/etc/fstab时用UUID或者WWID不要用/dev/mapper/路径。因为/dev/mapper/下的设备名虽然基于WWID但理论上仍可能变化。最稳妥的是用文件系统UUIDblkid /dev/mapper/360060160a0b31e0074f4e65e3a6ce911-part1把输出的UUID写到fstab里UUIDxxxx-xxxx /data xfs defaults,noatime,nodiratime 0 0noatime和nodiratime能减少不必要的元数据写入对性能有正面影响。4.3 用dd和fio做一轮基线性能测试配置完不测性能等于白配。先用dd做个简单的顺序写测试dd if/dev/zero of/data/testfile bs1M count10240 oflagdirectoflagdirect绕过页缓存测的是真实落盘性能。然后读测试dd if/data/testfile of/dev/null bs1M iflagdirect更专业的测试用fio能模拟随机读写、混合负载fio --namerandwrite --ioenginelibaio --iodepth32 --rwrandwrite \ --bs4k --direct1 --size10G --numjobs4 --runtime60 \ --group_reporting --filename/data/fio_test重点看iops和bw两个指标。多路径配置正确的话聚合后的带宽应该接近单路径带宽乘以有效路径数考虑存储控制器瓶颈不一定线性。如果发现聚合后性能反而比单路径差大概率是path_selector或者rr_min_io_rq没调好。5. 性能调优让多条链路真正跑满5.1 path_selector的选择逻辑与实测对比path_selector决定了IO怎么分配到多条路径上选错了会导致某条路径过载、其他路径闲置。CentOS 7的multipath支持三种主要的selectorround-robin 0轮询分配每条路径轮流处理IO。实现简单但在路径性能不一致时会导致慢路径拖后腿。queue-length 0按每条路径的队列长度分配队列短的优先。适合IO大小不均匀的场景。service-time 0按预估服务时间分配综合考虑队列长度和历史吞吐。这是目前最智能的算法也是我推荐的生产环境默认值。我做过一组对比测试同一台服务器、同一个LUN、四条路径用fio跑4K随机写path_selectorIOPS平均延迟(ms)路径负载均衡度round-robin 0482002.65较均衡queue-length 0516002.48均衡service-time 0543002.36最均衡service-time 0在IOPS和延迟上都略优而且路径负载最均衡。差距看起来不大但在高并发场景下这个差距会被放大。5.2 rr_min_io_rq与IO合并的权衡rr_min_io_rq控制的是在切换到下一条路径之前当前路径要处理多少个IO请求。默认值通常是1000对于现代存储来说太大了。设成1表示每个IO都重新选择路径能最大化负载均衡但会增加路径选择的开销。设成较小的值比如1到10在大多数场景下表现更好。这个参数的调整需要结合存储的队列深度。如果存储控制器的队列深度是1024四条路径每条路径分到256个队列槽位那rr_min_io_rq设成1能让IO均匀填满所有槽位。如果设成1000可能出现一条路径的队列满了、其他路径还空着的情况。5.3 队列深度与max_fds的系统级调优max_fds是multipathd能打开的最大文件描述符数默认值在某些高密度存储环境下不够用。如果服务器连了很多LUN每个LUN有多条路径文件描述符消耗会很大。设成8192是个安全的起点LUN特别多的话可以调到16384。另外SCSI层的队列深度也值得关注cat /sys/block/dm-3/queue/nr_requests这个值默认是128对于高性能全闪存储来说偏小。可以临时调大echo 256 /sys/block/dm-3/queue/nr_requests要持久化的话写个udev规则ACTIONadd|change, SUBSYSTEMblock, KERNELdm-*, ATTR{queue/nr_requests}256注意队列深度不是越大越好。调太大可能导致存储控制器端队列溢出反而增加延迟。建议每次调整后都用fio验证找到拐点。5.4 中断亲和性与CPU绑定多路径场景下HBA卡的中断处理会分散到多个CPU核心上。如果中断都集中在CPU0会成为瓶颈。检查中断分布cat /proc/interrupts | grep -i hba如果发现中断集中在某几个核心可以用irqbalance服务自动均衡或者手工设置亲和性echo 3 /proc/irq/xxx/smp_affinityirqbalance在CentOS 7上默认是开启的大多数场景下够用。但在极致性能调优时手工绑定中断到NUMA节点对应的核心上能进一步降低延迟。6. 故障排查路径断了、IO hang了怎么办6.1 路径状态异常的快速定位链路生产环境最怕的就是路径出问题但没及时发现。排查的第一步永远是看multipath -ll|-- policyservice-time 0 prio50 statusactive | |- 1:0:0:0 sdb 8:16 active ready running | |- 2:0:0:0 sdc 8:32 failed faulty running看到failed faulty说明sdc这条路径有问题。接下来分三步定位第一步确认是物理层还是配置层问题。看内核日志dmesg | grep -i sdc\|fc\|scsi | tail -50如果看到Link down、LOOP UP之类的FC链路事件说明是物理链路问题检查光纤、模块、交换机端口。如果看到rejecting I/O、path failed可能是存储侧控制器切换或者LUN映射问题。第二步确认存储侧状态。登录存储管理界面看对应的控制器、端口、LUN是否正常。有时候服务器侧看到路径failed但存储侧一切正常那可能是HBA卡驱动或者固件的问题。第三步尝试手动恢复。如果是临时性故障路径恢复后multipath会自动切回取决于failback设置。如果一直不恢复可以尝试重新扫描echo 1 /sys/class/fc_host/host2/issue_lip echo - - - /sys/class/scsi_host/host2/scanissue_lip会触发链路重新初始化scan会重新发现设备。执行完再multipath -ll看状态。6.2 no_path_retry设置不当导致的IO hang这是最坑的一类问题。no_path_retry设成queue时所有路径都断了IO会无限排队上层应用表现为hang住ps看到进程处于D状态不可中断睡眠kill -9都杀不掉。这时候只能恢复路径或者重启服务器。我的建议是除非你的业务能容忍IO hang并且有上层超时机制否则不要用queue。用具体数字比如5或10让IO在重试几次后快速失败上层应用能及时感知并做切换。数据库场景尤其要注意Oracle的disk_repair_time和multipath的no_path_retry要配合设置避免两边超时时间冲突。如果已经hang了恢复路径后IO会自动继续。如果路径恢复不了只能重启。所以生产环境一定要配监控路径状态变化时及时告警。6.3 设备名漂移与udev规则的坑前面提到过user_friendly_names会导致设备名漂移。即使不用它/dev/sd*的命名也可能在重启后变化。所以任何脚本、fstab、监控配置里都不要引用/dev/sdX一律用/dev/mapper/WWID或者文件系统UUID。如果发现/dev/mapper/下的设备名和预期不一致检查/etc/multipath/bindings文件如果用了user_friendly_names以及udev规则有没有冲突。CentOS 7的/usr/lib/udev/rules.d/11-dm-mpath.rules是multipath自带的不要随便改。自定义规则放在/etc/udev/rules.d/下面编号要大于系统规则。6.4 常见问题速查表现象可能原因排查命令处理方式multipath -ll无输出配置未生效或blacklist误排除multipath -v3检查blacklist和devices段路径状态failed物理链路或存储控制器故障dmesg、存储管理界面修复链路或等待控制器切换IO hang进程D状态no_path_retryqueue且所有路径断multipath -ll恢复路径或重启聚合后性能低于单路径path_selector或rr_min_io_rq不当fio对比测试调整selector和rr_min_io_rq设备名重启后变化user_friendly_names或udev冲突ls /dev/mapper/改用WWID检查udev规则本地盘被multipath接管blacklist未配置multipath -ll补充blacklist规则7. 我在实际项目里踩过的几个坑第一个坑是关于dev_loss_tmo的。早期配置时我沿用了默认值600秒结果一次存储控制器升级路径断了之后服务器等了10分钟才切换业务中断了整整10分钟。后来把dev_loss_tmo调到30秒、fast_io_fail_tmo调到5秒同样的场景下切换时间缩短到几秒内。这个参数对业务连续性的影响非常大一定要调。第二个坑是blacklist的devnode规则写得太宽。有次我写了devnode ^sd[a-z]结果把SAN盘也排除了multipath完全不工作。后来改成按vendor/product精确排除本地RAID卡问题解决。devnode匹配的是设备节点名而SAN盘在multipath发现阶段也是/dev/sd*所以这条规则会误伤。正确的做法是用device段按厂商型号排除。第三个坑是关于multipath -F的。这个命令会清除所有多路径映射如果业务正在使用/dev/mapper/下的设备执行multipath -F会导致IO中断。所以重新加载配置时正确的顺序是先systemctl reload multipathd平滑重载如果不行再systemctl restart multipathd最后才考虑multipath -F加重建。而且执行前一定要确认没有业务在读写。第四个坑是文件系统挂载参数。有次给一个高并发写入的场景配了defaults挂载结果元数据写入成为瓶颈。后来加上noatime,nodiratime,logbsize256kXFS专用性能提升了将近20%。挂载参数对性能的影响不比multipath配置小值得花时间调。最后一个经验是关于监控的。多路径的路径状态变化不会主动通知你必须自己监控。我通常写一个简单的脚本定期跑multipath -ll解析status字段发现failed或faulty就发告警。脚本不复杂但能让你在业务受影响之前就发现问题。比起事后救火提前发现的价值大得多。