[关闭]
@donghanyuan0609 2022-10-02T13:23:42.000000Z 字数 5829 阅读 260

同网段双 IB 网卡解决方案

背景

实验室的服务器集群,每台机器安装有 2 个 InfiniBand(简称 IB)网卡,支持 RDMA 也兼容 TCP/IP 。

实验室的 IB 网卡 IP 地址 (IPoIB) 约定格式为:172.16.[主机编号].[网卡序号]/16 ,其中 网卡序号 为 1 或 2 ,对应网络接口 0 或 1 ,每台机器的双网卡以及不同机器的 IB 网卡均位于同一网段。

实验室通常每人使用两台机器,一台作为 cpu server 另一台作为 memory server 通过 IB 及 RDMA 进行 Far Memory 相关实验。下表为本人使用的机器配置:

主机名称 操作系统版本 IB 网卡接口名称 IPoIB
cpuserver16 Ubuntu 20.04.5 LTS
With Desktop
ibs5f0 172.16.16.1/16
ibs5f1 172.16.16.2/16
memserver34 Ubuntu 18.04.3 LTS
No Desktop
ib0 172.16.34.1/16
ib1 172.16.34.2/16

其他同学相应对照自己机器的配置,可使用 ip addressifconfig 命令查看自己机器上的 IB 网卡接口名称和 IP 地址。如果找不到 IB 网卡则需要安装 MLNX_OFED 驱动。

RDMA 连通性测试

在 RDMA 协议栈下使用 rping 工具测试网络连通性,该工具等价于 TCP/IP 的 ping 。与 ping 不同,rping 工作时需首先启动一个 server side 进程,然后从 client side 试图向 server side 发起连接(而 ping 不需要 server side )。

RDMA Server 端示例:

  1. rping -s -a 172.16.34.1 -p 9401 -v
  2. # -s: 启动 server 端进程
  3. # -a: 绑定 IP 地址 (某个 IPoIB,这里选取 memserver34 第一个 IB 网卡)
  4. # -p: 监听端口号 (缺省为 9400)
  5. # -v: 打印输出信息
  6. # server side 启动后首先阻塞等待 client 连接
  7. # 当有 client 完成连接并断开后 server side 才停止

RDMA Client 端示例:

  1. rping -c -I 172.16.16.1 -a 172.16.34.1 -p 9401 -v
  2. # -c: 启动 client 端进程
  3. # -I: 指定自身使用的 IPoIB 地址 (可缺省,如缺省则从路由表查找)
  4. # -a: 服务端的 IPoIB 地址
  5. # -p: 服务端监听的端口号
  6. # -v: 打印输出信息
  7. # client side 连接成功后将不停发送测试数据,按 Ctrl+C 停止
  8. # 可选 -C 选项限定发送测试数据次数,例如 -C 10

如果 RDMA 连接正常,server 和 client 的终端均会有数据输出,例如:

  1. ping data: rdma-ping-0: ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr
  2. ping data: rdma-ping-1: BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrs

问题描述与复现

在 Memory Server 上使用第二块 IB 网卡作为 RDMA Server 时,位于 CPU Server 的 RDMA Client 无法与 RDMA Server 通讯,发生 RDMA_CM_EVENT_REJECTED 错误。示例命令:

  1. # memserver34
  2. rping -s -a 172.16.34.2 -p 9401 -v
  3. # cpuserver16
  4. rping -c -a 172.16.34.2 -p 9401 -v

而后 client side 发生错误:

  1. cma event RDMA_CM_EVENT_REJECTED, error 8

路由表排查

使用 route -n 命令或 ip route 命令查看 memserver34 的路由表,结果如下:

  1. $ ip route
  2. default via 10.208.130.254 dev enp49s0f1 proto static
  3. 10.208.130.0/24 dev enp49s0f1 proto kernel scope link src 10.208.130.34
  4. 172.16.0.0/16 dev ib0 proto kernel scope link src 172.16.34.1
  5. 172.16.0.0/16 dev ib1 proto kernel scope link src 172.16.34.2

系统自动生成的路由表同时存在两条指向 172.16.0.0/16 的路由,但只有第一条被优先匹配,所以如果用第二块 IB 网卡作为 server 则无法成功连接。

分离路由规则

由于所有机器的 IB 网卡均应处于同一子网,默认的路由规则一定出现冲突,为了使两块网卡能同时使用,应让每块 IB 网卡分别通过各自的路由表,而不使用全局的路由表。

参考 这篇解答 给出路由表修改命令(以 memserver34 为例):

  1. # 以下命令必须以 root 用户或通过 sudo 执行!
  2. # 删除全局路由表中与 ib 网卡有关的所有条目,确保不重复
  3. ip route del 172.16.0.0/16 dev ib0
  4. ip route del 172.16.0.0/16 dev ib1
  5. # 指定 table 选项,加回 ib 网卡路由,table 编号为数字且双 ib 网卡之间不相同
  6. # 表明该条路由信息属于指定的路由表
  7. ip route add 172.16.0.0/16 dev ib0 proto kernel scope link src 172.16.34.1 table 941
  8. ip route add 172.16.0.0/16 dev ib1 proto kernel scope link src 172.16.34.2 table 942
  9. # 指定路由规则,两块 ib 网卡分别通过各自的路由表
  10. ip rule add from 172.16.34.1 table 941
  11. ip rule add from 172.16.34.2 table 942

cpuserver 的配置类似,替换下 IPoIB 地址即可。配置完成后的参考路由表如下:

  1. $ ip route
  2. default via 10.208.130.254 dev enp49s0f1 proto static
  3. 10.208.130.0/24 dev enp49s0f1 proto kernel scope link src 10.208.130.34
  4. $ ip rule
  5. 0: from all lookup local
  6. 32764: from 172.16.34.2 lookup 942
  7. 32765: from 172.16.34.1 lookup 941
  8. 32766: from all lookup main
  9. 32767: from all lookup default
  10. $ ip route show table 941
  11. 172.16.0.0/16 dev ib0 proto kernel scope link src 172.16.34.1
  12. $ ip route show table 942
  13. 172.16.0.0/16 dev ib1 proto kernel scope link src 172.16.34.2

此时 IB 网卡的路由信息已分到两张路由表 (ip route table) 并通过路由规则 (ip rule) 分别绑定了。

需要注意的是,当 IB 网卡按上述方法分离了路由规则(尤其是删除了全局路由)后,使用 rpingping 时必须显式指定本地使用的网络接口( -I 选项):

  1. # 16 ping 34
  2. ping -I ibs5f0 172.16.34.1
  3. ping -I ibs5f0 172.16.34.2
  4. ping -I ibs5f1 172.16.34.1
  5. ping -I ibs5f1 172.16.34.2
  6. # 16 rping 34(ib0)
  7. # runs on 34
  8. rping -s -a 172.16.34.1 -p 9401 -v
  9. # runs on 16
  10. rping -c -I 172.16.16.1 -a 172.16.34.1 -p 9401 -v
  11. rping -c -I 172.16.16.2 -a 172.16.34.1 -p 9401 -v
  12. # 16 rping 34(ib1)
  13. # runs on 34
  14. rping -s -a 172.16.34.2 -p 9401 -v
  15. # runs on 16
  16. rping -c -I 172.16.16.1 -a 172.16.34.2 -p 9401 -v
  17. rping -c -I 172.16.16.2 -a 172.16.34.2 -p 9401 -v

目前通过 ip route 命令对路由表的修改并未永久保存,重启后失效。可将上述命令存为 shell 脚本,重启系统首次登录后执行。

  1. $ vim ~/route-ib-mem34.sh
  2. #!/bin/bash
  3. set -e
  4. if [ $(whoami) != "root" ]; then
  5. echo "Error: Must run as root!"
  6. exit 1
  7. fi
  8. echo "delete global ib routes"
  9. ip route del 172.16.0.0/16 dev ib0
  10. ip route del 172.16.0.0/16 dev ib1
  11. echo "add ib routes with table"
  12. ip route add 172.16.0.0/16 dev ib0 proto kernel scope link src 172.16.34.1 table 941
  13. ip route add 172.16.0.0/16 dev ib1 proto kernel scope link src 172.16.34.2 table 942
  14. echo "add ip rule for ib interface"
  15. ip rule add from 172.16.34.1 table 941
  16. ip rule add from 172.16.34.2 table 942

永久保存配置

为了无需每次开机都手动执行上述脚本,需要将上述路由修改脚本部署为开机自动执行。更准确地来说,在 IB interface 建立连接 (up) 后执行。

不同版本的 Ubuntu 系统,甚至不同的具体机器,使用的网络管理软件可能是不同的,在配置前需要先弄清楚当前机器采用何种工具管理网络,否则容易配错。

NetworkManager / ifupdown

cpuserver16 这台机器的系统有桌面,所以网络由 NetworkManager 管理,相应的终端工具有 nmclinmtui ,IB 网卡的静态 IP 配置位于 /etc/NetworkManager/system-connections/

参考了 这篇解答 ,在 /etc/network/if-up.d 目录下添加名为 route-ib-cpu16 的脚本。if-up.d 目录下的脚本会在网络接口发生连接后自动执行,通过 IFACE 变量来传入当前的网络接口名称。

脚本内容(需要通过 IFACE 变量判断一下当前的网络接口):

  1. #!/bin/bash
  2. set -e
  3. if [ "$IFACE" == "ibs5f0" ]; then
  4. ip route del 172.16.0.0/16 dev ibs5f0
  5. ip route add 172.16.0.0/16 dev ibs5f0 proto kernel scope link src 172.16.16.1 table 941
  6. ip rule add from 172.16.16.1 table 941
  7. elif [ "$IFACE" == "ibs5f1" ]; then
  8. ip route del 172.16.0.0/16 dev ibs5f1
  9. ip route add 172.16.0.0/16 dev ibs5f1 proto kernel scope link src 172.16.16.2 table 942
  10. ip rule add from 172.16.16.2 table 942
  11. fi

脚本文件创建后,必须添加可执行权限: sudo chmod +x route-ib-cpu16 ,而后重启服务器即可令上述配置永久生效。

另外查阅了一些资料,如果当前 Linux 系统用 ifupdown 工具管理网络连接,上述方法可能也可以,但没试过。

netplan

与 cpuserver16 不同,memserver34 采用 netplan 管理网络连接。该台机器安装有 Ubuntu 18.04 LTS 系统且不含桌面,即没有 NetworkManager

经过一番尝试在 /etc/netplan/ 目录找到了 IB 网卡的静态 IP 配置:

  1. $ ls /etc/netplan/
  2. 01-netcfg.yaml 99-netcfg.yaml
  3. $ cat /etc/netplan/99-netcfg.yaml
  4. network:
  5. version: 2
  6. renderer: networkd
  7. ethernets:
  8. ......
  9. ib0:
  10. addresses: [172.16.34.1/16]
  11. ib1:
  12. addresses: [172.16.34.2/16]

对于 netplan ,参考 这篇解答 ,网卡连接后执行脚本位于 /etc/networkd-dispatcher/ 中,具体地,有关路由的操作放在 routable.d 子目录下。脚本编写规范同上,netplan 通过传入 IFACE 变量指定当前网口。

/etc/networkd-dispatcher/routable.d/ 目录下创建 route-ib-mem34

  1. #!/bin/bash
  2. set -e
  3. if [ "$IFACE" == "ib0" ]; then
  4. ip route del 172.16.0.0/16 dev ib0
  5. ip route add 172.16.0.0/16 dev ib0 proto kernel scope link src 172.16.34.1 table 941
  6. ip rule add from 172.16.34.1 table 941
  7. elif [ "$IFACE" == "ib1" ]; then
  8. ip route del 172.16.0.0/16 dev ib1
  9. ip route add 172.16.0.0/16 dev ib1 proto kernel scope link src 172.16.34.2 table 942
  10. ip rule add from 172.16.34.2 table 942
  11. fi

同理需要 sudo chmod +x 添加可执行权限,重启后该路由配置永久生效。

如果不确定永久配置是否正确,在重启后可以用 分离路由规则 章节提到的 ip ruleip route show table 命令测试一下。

参考资料与致谢

  1. linux下双网卡能不能设置同一网段? - 袁昊洋的回答 - 知乎
  2. how-do-i-make-the-script-to-run-automatically-when-tun0-interface-up-down-events
  3. netplan-how-to-run-script-on-if-up-of-specified-device-only
  4. verify-rdma-working

感谢 肖佳伟 师兄指导 rdma 工具使用、帮助排查路由表以及分享双网卡路由配置方案(知乎回答)。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注