Mellanox Connect-X 5 网卡 SN2700 交换机 NFS Over RDMA (RoCEv2) 配置指北
目前 CX555 网卡和 SN2700 交换机的价格已经能让 100G 以太网走进寻常百姓家。配置 RoCEv2 和 NFS Over RDMA 其实并不复杂,但网上的资料比较零散,而且有些已经过时。本文记录一下我个人的配置经验,供大家参考。
环境
- 网卡:所有服务器使用 Mellanox ConnectX-5 EN 网卡(本例中使用 惠普版 cx555a)
- 交换机:Mellanox Spectrum SN2700,运行 NVIDIA Onyx OS
3.10.4006 - 存储服务器:Proxmox VE 9.1.4,内核
6.17.4-1-pve。别用 Truenas SCALE,Truenas SCALE 的 NFS Over RDMA 特性只在付费版本中支持 - 客户端服务器:Ubuntu 22.04 LTS,使用 HWE 内核
6.8.0-90-generic。建议从 Ubuntu 22.04 的 GA 内核升级到 HWE 内核,从 5.15 升级到 6.8 补充了相当多的 NFS 相关特性和修复
交换机配置
需要在交换机上启用 RoCEv2 相关特性。请参阅官方文档
自从 Onyx OS 3.8.2008 版本起,交换机自带了 roce 命令,可以一键启用 RoCEv2 特性。使用 SSH 或 Console 登陆交换机命令行,执行以下命令:
1 | enable |
即可自动应用 PFC,ECN,DSCP,DCBX 等相关配置。在交换机和网卡侧都启用 DCBX 特性后,网卡会自动与交换机协商 PFC 和 ETS 配置,无需手动配置。在交换机上运行 show roce 命令可以查看自动应用的 RoCEv2 配置模版:
1 | RoCE mode : lossless |
如果 Exception list 非空,说明当前交换机上有配置与 RoCEv2 冲突。重启交换机可能会清除这些冲突配置,或者直接恢复交换机出厂设置,然后重新应用 roce 命令。
驱动安装
总的来说,我充分地信任 apt 并倾向于使用发行版自带的内核和驱动,或者通过 apt 在线仓库来安装和更新驱动。我不建议使用各种
install.sh和make install脚本,这些东西往往会导致系统变得不可维护,在某次apt upgrade之后爆炸。
下面几乎所有命令都需要 root 权限,请根据需要加上 sudo,或直接 sudo -i 切换到 root 用户。
Proxmox VE
Proxmox VE 9.1.4 默认使用一个相当新版本的内核(来自 Ubuntu 25.04,6.17.4-1-pve),自带的 mlx5_core 和 mlx5_ib 已经对 RoCEv2 和 NFS Over RDMA 有很好的支持,安装 DOCA_OFED (原 MLNX_OFED,NVIDIA 收购后更名)反而会导致诡异的问题。如果需要使用 DOCA_OFED 内的 Infiniband 相关的用户态工具,可以先安装 DOCA_OFED 后再卸载 dkms 包:
1 | export DOCA_URL="https://linux.mellanox.com/public/repo/doca/3.2.0/debian13/x86_64/" |
卸载多余的 dkms 驱动包
1 | apt-get -y remove --purge isert-dkms mlnx-ofed-kernel-dkms iser-dkms knem-dkms srp-dkms |
其中 kernel-mft-dkms 包可以保留,它不与内核自带的驱动冲突,并且我们需要用到 MFT 功能。
另外,在 PVE 上也 不要 安装 mlnx-nfsrdma-dkms 包,装了会出问题。
关于我是怎么发现这个问题的:我配了两台 PVE 服务器做测试,一开始都安装了
doca-networking,结果先装的那台服务器装成了 Debian 的内核头文件linux-headers-amd64,而不是 PVE 内核的头文件proxmox-default-headers,导致 dkms 驱动实际上没装上,后面那台服务器装对了头文件。结果第一台服务器能用,第二台服务器有 dmesg 报错才发现在 PVE 上不需要 doca 的 dkms 驱动,卸载掉就好了。
1 infiniband mlx5_0: create_qp:3317:(pid 4751): Create QP type 2 failed
Ubuntu 22.04
首先查看内核版本
1 | uname -r |
如果还在 5.15 内核上,建议升级到 HWE 内核(6.8 版本),内核从 5.15 升级到 6.8 补充了相当多的 NFS 相关特性和修复
1 | apt update |
如果需要管理大量服务器,建议创建 udev 规则来统一所有机器上 Mellanox 网卡的设备命名:
1 | vim /etc/udev/rules.d/70-persistent-net.rules |
根据网卡 mac 地址添加 udev 规则,例如:
1 | SUBSYSTEM=="net", ACTION=="add", ATTRS{address}=="94:40:c9:xx:xx:xx", NAME="mce0" |
mce0这个命名大概来自于某些 BSD 系统对 Mellanox 网卡的命名习惯,可以根据个人喜好修改
与 Proxmox VE 不同,在 Ubuntu 22.04 上需要安装完整的 DOCA_OFED 套件:
1 | export DOCA_URL="https://linux.mellanox.com/public/repo/doca/3.2.0/ubuntu22.04/x86_64/" |
DOCA 3.2.1 在 Ubuntu 22.04 上有一些问题,需要同时安装
gcc-12包才能成功编译内核模块。上面用的是 DOCA 3.2.0,暂时没有这个问题。
安装完成后重启服务器,主要目的是应用 udev 规则来统一网卡命名。
IP 和 QoS 配置
首先启用网卡的硬件 DCBX 特性:
1 | mst start |
如果网卡设备不是
/dev/mst/mt4119_pciconf0,请使用mst status命令查看正确的设备路径
配置后需要重启服务器才能生效,可以等到后续配完再重启。理论上用下面的命令能免重启重置网卡
1 | mlxfwreset -d /dev/mst/mt4119_pciconf0 r |
接下来配置网卡的 IP 地址和 QoS。交换机自带的 RoCEv2 配置模版中不使用 VLAN,我们可以直接在网卡上配置普通的 IP 地址。不同的系统需要用不同的方法持久化配置并触发 QoS 设置脚本
NetworkManager (Ubuntu Desktop)
Ubuntu 22.04 自带 netplan 和 NetworkManager,建议使用 netplan 编写配置文件。编辑
1 | vim /etc/netplan/01-network-manager-all.yaml |
写入
1 | network: |
并确保文件权限正确
1 | chmod 600 /etc/netplan/01-network-manager-all.yaml |
写入 Post Up 脚本来补充 QoS 设置(Ubuntu 的 NetworkManager 自带一个 shim 脚本来兼容 ifupdown 的脚本):
1 | vim /etc/network/if-up.d/mlnx-roce-qos |
写入以下内容:
1 |
|
保存后赋予可执行权限:
1 | chmod +x /etc/network/if-up.d/mlnx-roce-qos |
使用下面的命令应用 netplan 配置:
1 | netplan try |
出现提示后按回车确认应用配置。
systemd-networkd (Ubuntu Server)
仍然是编辑 netplan 配置文件
1 | vim /etc/netplan/99-static-mce0.yaml |
写入
1 | network: |
保存后赋予正确权限
1 | chmod 600 /etc/netplan/99-static-mce0.yaml |
写入 systemd-networkd dispather 脚本的 routable 事件来补充 QoS 设置:
1 | vim /etc/networkd-dispatcher/routable.d/mlnx-roce-qos |
写入以下内容:
1 |
|
保存后赋予可执行权限:
1 | chmod +x /etc/networkd-dispatcher/routable.d/mlnx-roce-qos |
使用下面的命令应用 netplan 配置:
1 | netplan try |
出现提示后按回车确认应用配置。
ifupdown (Proxmox VE)
在 Proxmox VE 的 Web 面板中就可以配置静态 IP 地址并把 MTU 设置为 9000(在高级选项中)。不建议直接编辑 /etc/network/interfaces 文件,因为 PVE 会在 Web 面板中覆盖这个文件。
写入 if-up 脚本来补充 QoS 设置:
1 | vim /etc/network/if-up.d/mlnx-roce-qos |
写入以下内容:
1 |
|
保存后赋予可执行权限:
1 | chmod +x /etc/network/if-up.d/mlnx-roce-qos |
应用配置:
1 | ifdown mce0 && ifup mce0 |
RoCEv2 测试
到现在重启服务器,理论上 RoCEv2 就能通信了。做一些基本的检查:
1 | mlnx_qos -i mce0 |
关键是确认 DCBX Mode 是 Firmware Controlled。刚刚开机后可能还没有接受到具体的 DCBX 配置,可以等几分钟再检查一次,正确的输出应该类似于:
1 | DCBX mode: Firmware controlled |
可以看到已经自动设置了 PFC 和 ETS。
下一步查看 show_gids 输出,确认 RoCEv2 GID 已经分配:
1 | show_gids |
正确的输出能够看到有 IPv4 地址的行:
1 | DEV PORT INDEX GID IPv4 VER DEV |
如果没有看到 IPv4 地址的行,但 ip a 能看到网卡有正确的 IP 地址,可能网络管理器和 rdma-ndd.service 服务的启动顺序有问题。rdma-ndd.service 应当在网卡 link up 之前启动。尽管一般情况下网络管理器声明为在 rdma-ndd.service 之后启动(After=network-pre.target),但 rdma-ndd.service 只有在网卡的 infiniband 驱动加载后才会被 udev 规则触发,这就可能导致 rdma-ndd.service 在网卡 link up 之后才启动,修起来感觉比较麻烦。如果确认是这个问题,重新 link up 可以解决:
1 | ip link set dev mce0 down |
再次运行 show_gids,应该就能看到 IPv4 地址的行了。
systemd 的服务依赖顺序有时会比较难以控制,尤其是当服务的启动时机依赖于硬件状态(如网卡 link up)时。解决这个问题的一种思路是创建一个自定义的 systemd 服务单元,专门在启动阶段的最后重新把网卡 link down 然后 link up. 创建 /etc/systemd/system/fix-roce-networkmanager.service 文件,写入以下内容:
1 | [Unit] |
并启用该服务:
1 | systemctl daemon-reload |
接下来可以运行 ib_write_bw 来测个速。在一边运行
1 | ib_write_bw --report_gbits |
在另一边运行(双向传输,测试 5 秒钟,目标 IP 地址根据实际情况修改):
1 | ib_write_bw -b -D 5 --report_gbits 192.168.6.106 |
看到这个结果就是接近跑满 100GbE 了(最下面一栏 BW average):
1 | WARNING: BW peak won't be measured in this run. |
如果差的比较多,检查网卡是否降级到了 PCIE 3.0 x8 链接,这样带宽会被限制在 50Gbps 左右:
1 | dmesg | grep -i mlx5 |
这个输出就是被 PCIE 卡脖子了:
1 | [ 8.387515] mlx5_core 0001:43:00.0: firmware version: 16.35.4030 |
NFS Over RDMA 配置
如果把以上的 RoCEv2 配置都做好了,NFS Over RDMA 的配置就非常简单了。首先,客户端和服务端都加大 rpc 条目表来提升性能:
1 | echo "options sunrpc tcp_slot_table_entries=128" >> /etc/modprobe.d/sunrpc.conf |
服务端
在 Proxmox VE 上安装 NFS 服务器组件:
1 | apt update |
实测 Proxmox VE 自带的 NFS 服务器组件已经支持 NFS over RDMA,无需额外安装 mlnx-nfsrdma-dkms 包,装了会出问题。
如果使用 ZFS,可直接通过 ZFS 生成 NFS 导出:
1 | zfs set sharenfs='[email protected]/22,[email protected]/24,async,no_subtree_check,all_squash,anonuid=999,anongid=10000' poolname/dataset |
简要说明一下这一串参数:
[email protected]/22允许读写访问的 IP 段,可以添加多个async启用异步写入以提升性能no_subtree_check禁用子树检查以提升性能all_squash,anonuid=999,anongid=10000将所有客户端的用户映射为999:10000用户,防止不同机器上 uid 和 gid 不一致导致权限问题。你可以根据需要修改为其他 uid 和 gid。简单起见,可以将挂载点给 chown 成这个用户,并chown 2755. 在这个配置下,还可以在 NFS 服务器上用 root 用户创建所有者为 root 的文件,从而实现客户端只能读取但不能修改的效果。
也许在 PVE 主机上使用
100000:100000也是个好主意,这是非特权 PCT 容器和 virtiofs 的默认映射用户
如果不使用 ZFS,可以手动编辑 /etc/exports 文件来添加 NFS 导出:
1 | /mnt/data 192.168.4.0/22(rw,sync,no_subtree_check,all_squash,anonuid=999,anongid=10000) 192.168.123.0/24(rw,sync,no_subtree_check,all_squash,anonuid=999,anongid=10000) |
修改 nfs.conf 来启用 RDMA 支持:
1 | vim /etc/nfs.conf |
修改其中的 [nfsd] 段,启用 rdma:
1 | [nfsd] |
20049 是 NFS over RDMA 的默认端口,可以根据需要修改。
重启 NFS 服务:
1 | systemctl restart nfs-server |
理论上不需要手动加载任何内核模块,该加载的自己会加载。
客户端
需要额外安装 mlnx-nfsrdma-dkms 包来启用 NFS over RDMA 支持:
1 | apt update |
临时使用 mount 命令挂载 NFS over RDMA:
1 | mkdir -p /mnt/nfs_rdma |
将 <server_ip> 替换为 NFS 服务器的 IP 地址,/poolname/dataset 替换为实际的导出路径。执行命令行即可访问挂载点中的文件。
通过 /etc/fstab 来持久化挂载配置,并添加更多优化性能的参数:
1 | vim /etc/fstab |
添加一行:
1 | <server_ip>:/poolname/dataset /mnt/nfs_rdma noauto,rdma,port=20049,vers=4.2,nconnect=16,rsize=1048576,wsize=1048576,hard,proto=rdma,timeo=600,retrans=2,noatime,nodiratime,actimeo=60,x-systemd.automount,x-systemd.idle-timeout=600,x-systemd.mount-timeout=30,_netdev 0 0 |
这里还使用了 systemd 的自动挂载功能(x-systemd.automount),避免开机时因为网络未就绪而导致挂载失败的问题。
注释
- 从内核 5.15 起,nfs 客户端支持对同一服务器共享连接,创建 rdma 协议挂载点后,原有的 tcp 协议挂载点会自动升级为 rdma 协议
- 从内核 5.18 起,内核支持跨挂载点创建 reflink 链接,也支持通过 nfs 创建底层 zfs 的 reflink 链接。但由于 linux 内核仍未解除对跨 superblock reflink 的限制,尽管 zfs 本身支持跨 dataset 的 block clone,linux 上无法跨越 zfs dataset 创建 reflink 链接。参见 OpenZFS 讨论
- RDMA 网络流量不经过 CPU 和内核处理,因此无法被常规的流量监控工具(如 iftop, nload)监控到。
- Docker 挂载点不能透传 systemd 的自动挂载功能,在上面的例子中,不能使用
docker run -v /mnt:/data来在容器中访问/data/nfs_rdma,但是使用docker run -v /mnt/nfs_rdma:/data是可以的。