ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

iSCSI多路径配置实战:双网卡双交换机实现链路冗余与负载均衡

iSCSI多路径配置实战:双网卡双交换机实现链路冗余与负载均衡

1. 项目缘起与整体设计思路

存储这玩意儿,单点永远是最脆弱的一环。我最早接触 iSCSI 是在一台老旧的备份服务器上,单网线单路径跑了大半年,平时风平浪静,直到某次机房交换机例行重启,备份任务直接挂死,恢复的时候发现卷已经只读了。那次之后我就下定决心,只要上 iSCSI,必须做多径。这次实验的目标很明确:用两台存储端(或者一台存储的两个网口)配合客户端的两块网卡,搭一套能扛链路故障、能聚合带宽的 iSCSI 多径环境,并且把链路切换、负载均衡、故障恢复这几个关键行为全部验证一遍。

所谓 iSCSI 多径,说白了就是客户端到存储端之间同时存在多条可达路径,操作系统层面通过 multipath 子系统把这些路径聚合成一个逻辑设备。单条路径断了,I/O 自动切到其他路径,业务基本无感。它解决的核心问题是链路单点故障和单路径带宽瓶颈。适合谁来参考?我觉得三类人最需要:一是中小机房运维,预算有限上不起光纤 SAN,想用千兆或万兆以太网凑一套靠谱存储网络;二是虚拟化平台管理员,ESXi、KVM 这类宿主对共享存储的稳定性要求极高;三是做数据库或备份的工程师,对 I/O 连续性有硬性要求。哪怕你只是想在实验室里把原理跑通,这套流程也能直接抄。

整体设计上,我选择的是“双网卡 + 双交换机 + 双存储口”的对称结构。为什么不用单交换机双网卡?因为交换机本身也是单点,一旦它挂了,两条路径同时失效,多径就失去意义了。双交换机虽然成本翻倍,但换来的是一整条链路层面的冗余。存储端我用的是某开源存储方案,导出两个 target 门户,分别绑定两个不同网段的 IP。客户端两块网卡也分属这两个网段,形成两条完全独立的路径。multipath 层用 Linux 自带的 device-mapper-multipath,策略选的是 round-robin,也就是轮询分发 I/O。这个选择后面会详细讲原因。

提示:实验环境里如果只有一台交换机,也可以先把多径跑通,但一定要清楚这只是功能验证,不具备真正的链路冗余能力。

2. 核心细节解析与实操要点

2.1 多径的三种典型拓扑与选型逻辑

在动手之前,先把拓扑想清楚。我总结下来常见的有三种。第一种是单主机双网卡接双交换机,存储端双口分别接两台交换机,这是最标准的冗余结构,任何一条链路断掉都不影响。第二种是单交换机双网卡,客户端和存储都接同一台交换机,这种只能防网卡故障,防不了交换机故障,但胜在成本低。第三种是客户端双网卡、存储单口,这种严格来说不算完整多径,因为存储端还是单点,只适合做客户端侧的链路聚合测试。

我这次选第一种,理由很直接:多径的价值在于消除所有单点,如果存储端只有一个口,那存储口一挂全完,前面做的冗余全是白费。选型的时候还要注意网卡类型,我强烈建议用独立物理网卡,不要用板载双口或者 VLAN 子接口凑数。板载双口很多时候共享同一个 PCIe 通道,真出问题可能一起挂;VLAN 子接口底层还是同一块物理网卡,物理链路断了两个子接口同时失效,多径形同虚设。

2.2 网段规划与 IP 分配

网段规划这块我踩过坑,第一次做的时候两条路径用了同一个网段,结果路由表里只有一条默认路由,multipath 死活认不出两条独立路径。后来改成两个不同网段,问题立刻解决。具体分配如下:路径 A 用 192.168.10.0/24,客户端网卡 A 是 192.168.10.10,存储门户 A 是 192.168.10.20;路径 B 用 192.168.20.0/24,客户端网卡 B 是 192.168.20.10,存储门户 B 是 192.168.20.20。两个网段之间不做路由互通,保证路径完全隔离。

为什么要隔离?因为如果两个网段能互通,操作系统可能会把去往存储 B 的流量从网卡 A 发出去,这样路径就交叉了,multipath 看到的路径状态会混乱。隔离之后,每条路径的流量严格走各自的网卡和交换机,状态清晰可判。另外 MTU 我统一设成 9000,也就是巨帧。iSCSI 走巨帧能显著降低 CPU 中断次数,提升吞吐,实测千兆环境下顺序读能提升大概 15% 到 20%。但要注意,路径上所有设备——网卡、交换机、存储端——MTU 必须一致,有一个环节是 1500 就会导致分片甚至丢包。

2.3 multipath 配置文件的关键参数

multipath 的核心配置在/etc/multipath.conf。默认配置里很多参数是注释掉的,需要自己放开。我重点调了这几个。path_grouping_policy设成multibus,意思是所有路径放在一个组里,一起参与负载均衡。如果设成failover,就只有一条路径干活,其他待命,带宽聚合就没了。path_selector用round-robin 0,这是轮询策略,I/O 依次分发到各条路径。rr_min_io_rq我设成 1000,表示每 1000 个请求切换一次路径。这个值太小会导致频繁切换增加开销,太大又起不到均衡效果,1000 是我在千兆环境下测出来比较舒服的值。

no_path_retry设成queue,意思是所有路径都失效时,I/O 进入队列等待而不是直接报错。这个参数非常关键,它决定了链路全断时业务是挂起还是崩溃。设成 queue 之后,只要在超时时间内有一条路径恢复,I/O 就能继续,业务无感。fast_io_fail_tmo设成 5 秒,表示路径失效后 5 秒内快速失败,避免 I/O 长时间卡住。dev_loss_tmo设成 30 秒,超过这个时间路径还没回来就从系统里移除。这几个超时参数需要配合业务容忍度来调,数据库这类敏感业务可以适当放宽。

2.4 存储端 target 的导出细节

存储端我用的是 LIO(Linux-IO)target,配置在/sys/kernel/config/target/iscsi下面。每个 target 门户绑定一个 IP,两个门户分别绑 192.168.10.20 和 192.168.20.20。这里有个细节:同一个 target IQN 可以导出多个门户,客户端发现的时候会同时看到两个入口,multipath 才能识别成同一设备的多条路径。如果导出成两个不同的 IQN,multipath 会当成两块不同的磁盘,那就不是多径了。

ACL 配置也要注意,客户端的两个发起端 IQN 都要加入 ACL,或者干脆用同一个发起端 IQN 从两块网卡发起。我推荐后者,因为同一个 IQN 对应同一个节点,multipath 识别起来更干净。认证方面实验环境可以关掉 CHAP,生产环境建议开启,但要注意两个门户的 CHAP 凭据要一致,否则一条路径认证失败会导致路径状态异常。

3. 实操过程与核心环节实现

3.1 基础网络与巨帧配置

先配网络。客户端两块网卡分别命名为 ens33 和 ens34,对应两个网段。用 nmcli 配置静态 IP,命令如下:

nmcli con add type ethernet con-name pathA ifname ens33 ip4 192.168.10.10/24 nmcli con add type ethernet con-name pathB ifname ens34 ip4 192.168.20.10/24 nmcli con up pathA nmcli con up pathB

配完 IP 之后设 MTU。用ip link set ens33 mtu 9000和ip link set ens34 mtu 9000,然后ip link show确认。存储端两个网口同样设 9000。交换机如果是可管理的,要在端口上开启巨帧支持,很多默认是关闭的。验证巨帧是否通,用ping -M do -s 8972 192.168.10.20,-M do 表示禁止分片,-s 8972 是 9000 减去 IP 头和 ICMP 头的开销。能 ping 通说明整条路径巨帧没问题。

注意:巨帧配置错误是 iSCSI 性能问题的高频原因。如果 ping 大包不通但小包通,基本就是路径上某个设备 MTU 没改。

3.2 存储端 target 创建与门户绑定

存储端先创建后端块设备,我用的是一个 100G 的逻辑卷。然后进入 configfs 创建 target:

mkdir /sys/kernel/config/target/iscsi/iqn.2024-01.com.example:storage/tpgt1 mkdir /sys/kernel/config/target/iscsi/iqn.2024-01.com.example:storage/tpgt1/portal

绑定两个门户:

echo "192.168.10.20:3260" > /sys/kernel/config/target/iscsi/iqn.2024-01.com.example:storage/tpgt1/portal/10.20 echo "192.168.20.20:3260" > /sys/kernel/config/target/iscsi/iqn.2024-01.com.example:storage/tpgt1/portal/20.20

创建 LUN 并关联后端设备,再配置 ACL 允许客户端 IQN 访问。完成后用targetcli ls检查结构,确认两个门户都在同一个 tpgt 下面。这一步如果门户建到了不同的 tpgt,客户端发现时会看到两个独立目标,多径就失败了。

3.3 客户端发现与登录

客户端安装iscsi-initiator-utils和device-mapper-multipath。先启动 iscsid 服务,然后发现目标:

iscsiadm -m discovery -t sendtargets -p 192.168.10.20:3260 iscsiadm -m discovery -t sendtargets -p 192.168.20.20:3260

两条发现命令执行后,iscsiadm -m node应该能看到同一个 IQN 对应两个门户。然后登录:

iscsiadm -m node -T iqn.2024-01.com.example:storage -l

登录后lsblk会看到两块磁盘,比如 sdb 和 sdc,这其实是同一个 LUN 的两条路径。这时候还没做多径聚合,直接挂载会有数据损坏风险,千万别急着格式化。

3.4 multipath 启用与聚合验证

编辑/etc/multipath.conf,加入设备配置:

defaults { path_grouping_policy multibus path_selector "round-robin 0" rr_min_io_rq 1000 no_path_retry queue fast_io_fail_tmo 5 dev_loss_tmo 30 } blacklist { devnode "^sd[a]$" }

blacklist 里把系统盘 sda 排除,避免误聚合。然后启动服务:

systemctl enable --now multipathd multipath -ll

multipath -ll的输出会显示一个 mpath 设备,下面挂着 sdb 和 sdc 两条路径,状态都是 active ready。这时候/dev/mapper/下会出现一个 mpathX 设备,这才是真正要用的设备。格式化并挂载:

mkfs.xfs /dev/mapper/mpatha mkdir /mnt/iscsi mount /dev/mapper/mpatha /mnt/iscsi

3.5 链路故障切换实测

验证环节我做了三组测试。第一组,拔掉路径 A 的网线,观察multipath -ll输出,sdb 状态变成 failed,sdc 仍然是 active,挂载点读写正常,dd测试没有中断。第二组,把路径 A 网线插回,几秒后 sdb 自动恢复 active ready,路径重新加入轮询。第三组,两条网线同时拔掉,I/O 进入 queue 状态,dd命令卡住但不报错,插回任意一条网线后 I/O 继续,dd正常完成。这三组测试全部通过,说明多径的故障切换和恢复机制工作正常。

带宽聚合方面,我用dd做顺序读测试,单路径大概 110MB/s,双路径轮询下跑到 200MB/s 左右,接近千兆双口的理论上限。这里要注意,聚合效果取决于 I/O 模式,大块顺序读效果最好,随机小 I/O 提升不明显,因为轮询切换本身有开销。

4. 常见问题与排查技巧实录

4.1 多径识别失败排查表

现象可能原因排查方法解决方式
multipath -ll 只显示一条路径两条路径在同一网段检查 IP 配置和路由表改为不同网段
显示两块独立磁盘存储端导出了两个 IQNtargetcli ls 检查合并到同一 target
路径状态一直是 ghost门户绑定到了不同 tpgt检查 portal 目录结构重建到同一 tpgt
聚合后设备无法挂载未格式化或文件系统损坏dmesg 查看报错重新 mkfs
切换后 I/O 报错no_path_retry 设成了 fail检查 multipath.conf改为 queue

4.2 几个容易忽略的坑

第一个坑是rr_min_io_rq的单位。老版本内核里这个参数叫rr_min_io,单位是请求数;新版本改成rr_min_io_rq,单位还是请求数,但含义略有差异。如果配置里写错参数名,multipathd 会静默忽略,用默认值,你以为调了其实没调。改完配置一定要multipathd reconfigure然后multipath -ll确认参数生效。

第二个坑是 iSCSI 会话超时和 multipath 超时的配合。iSCSI 层有自己的node.session.timeo.replacement_timeout,默认 120 秒。如果这个值比 multipath 的dev_loss_tmo还大,路径失效后 iSCSI 层还在傻等,multipath 已经判定路径丢失,两边状态不一致会导致 I/O 异常。我的做法是把 replacement_timeout 设成 30 秒,和 dev_loss_tmo 对齐,行为就一致了。

第三个坑是巨帧和 iSCSI 的交互。开了巨帧之后,如果某条路径的交换机端口没开巨帧,iSCSI 登录可能成功,但大块 I/O 会超时重传,表现为性能骤降而不是直接报错,很难排查。我的经验是,配好多径后先跑一轮dd大块读写,确认吞吐正常再上业务。

4.3 性能调优的几个实操心得

除了巨帧和轮询,还有几个参数值得调。max_sectors_kb可以适当调大,默认 512,我设成 1024,让单次 I/O 传输更多数据,减少切换次数。nr_requests队列深度也可以调,但要看存储端能承受多少并发。另外网卡的中断亲和性建议绑定到不同 CPU 核,避免两块网卡的中断挤在同一个核上。用ethtool -L可以调网卡队列数,多队列配合多径效果更好。

提示:调优参数不要一次全上,每改一项测一次,否则出问题不知道是哪个参数导致的。

5. 实验环境与生产环境的差异说明

实验室里拔网线测切换很爽,但生产环境有几个差异必须考虑。第一,生产环境的交换机重启、固件升级、光模块老化都可能导致链路抖动,multipath 的抖动抑制参数要调得更保守,避免频繁切换。第二,生产环境通常有多台客户端同时访问同一存储,存储端的门户负载和会话数要提前规划,别让一个门户过载。第三,监控必须跟上,multipathd show paths的状态、iSCSI 会话状态、路径切换次数都要纳入监控,否则链路悄悄降级了你都不知道。

我在实际使用中发现,多径环境最怕的不是链路断,而是链路“半死不活”——丢包率高但没完全断。这种情况下 multipath 可能认为路径还是 active,I/O 却大量重传,性能暴跌。解决办法是开启 iSCSI 的 ping 检测,或者用iscsiadm的node.conn[0].timeo.noop_out_interval参数定期发 noop 探测,及时发现亚健康路径。这个细节常规文档很少提,但生产环境里非常实用。

最后再分享一个小技巧:做多径实验的时候,用watch -n 1 'multipath -ll'开一个窗口实时看路径状态,另一窗口跑dd或fio,拔插网线的瞬间能清楚看到状态变化和 I/O 行为,比事后看日志直观得多。这套环境跑通之后,后续扩展到四路径、跨交换机堆叠、甚至配合 RDMA 做 iSER,思路都是一脉相承的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进