文档中心>容器服务>实践教程>AI>在 TKE 上运行基于 NCCL 的 RDMA 分布式训练任务

在 TKE 上运行基于 NCCL 的 RDMA 分布式训练任务

最近更新时间:2025-03-10 17:11:32

我的收藏

简介

RDMA(Remote Direct Memory Access,远程直接内存访问)是一种高速网络互联技术,该技术旨在减少在数据传输过程中收发端的处理延迟以及资源消耗。RDMA 技术使计算机能够直接访问远程计算机的内存,在内存层面进行数据传输而无需 CPU 频繁介入,从而显著增强网络通信性能。使用 RDMA,能够提高网络吞吐,降低网络时延,从而有效提升大规模分布式训练任务的训练效率。
本文档介绍在 TKE 上如何使用基于 NCCL 的 RDMA 运行分布式训练任务。

环境准备

1. 已创建并部署好 TKE 集群。如果您还没有集群,请参见 创建集群
2. 已购买 RDMA 实例,RDMA netns 为 shared 模式,且加入的 高性能计算集群(THCC)已支持 /28 及以上 RDMA 网段。
3. 部署安装 rdma-agent 组件。可以在 容器服务控制台集群 > 组件管理进行安装,组件安装详情请参见 rdma-agent 说明

4. 将欲部署的 RDMA 实例节点加入节点池,修改节点池配置添加 label 从而部署 rdma-agent。操作详情请参见 部署节点设置 label


环境验证

1. 组件部署后,等待安装成功,确保 tke-rdma-shared-agent 皆为 Running。
kubectl -nkube-system get po -o wide -lk8s-app=tke-rdma-shared-agent
2. 检查 RDMA 节点 Capacity 和 Allocatable 资源,确保其中的 tke.cloud.tencent.com/tke-shared-rdma 不为 0。
kubectl describe node <nodeName> | grep tke-shared-rdma


提交训练任务

YAML 关键配置

1. 网络配置。
指定 CNI,RDMA 网络平面和 VPC 网络平面是两个网络平面,为了使 Pod 能够同时连接 VPC 网络和 RDMA 网络,需要在工作负载的 Pod template 中指定以下 annotation 配置:
GlobalRouter(全局路由)网络模式集群:
tke.cloud.tencent.com/networks: "tke-bridge,tke-rdma-ipvlan"
VPC-CNI 共享网卡模式集群:
tke.cloud.tencent.com/networks: "tke-route-eni,tke-rdma-ipvlan"
集群的网络模式可以通过集群里的 cni-agent 配置来确定:
kubectl -nkube-system get cm tke-cni-agent-conf -oyaml | grep defaultDelegates
如全局路由模式集群的配置:

2. 指定 securityContext 使能进程间通信,设置 RDMA 和 GPU resource 资源请求。
securityContext:
capabilities:
add: [ "IPC_LOCK" ]
resources:
limits:
nvidia.com/gpu: "8"
tke.cloud.tencent.com/tke-shared-rdma: 1
3. 为了支持 NCCL 共享数据,需要为 IPC 和固定(页面锁定)系统内存资源共享系统内存。
volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- emptyDir:
medium: Memory
sizeLimit: 64Gi
name: dshm

YAML 样例

以 statefulset 为例,具体的样例如下:
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
k8s-app: mofed-test
name: mofed-test
namespace: default
spec:
replicas: 2
selector:
matchLabels:
k8s-app: mofed-test
qcloud-app: mofed-test
serviceName: ""
template:
metadata:
annotations:
tke.cloud.tencent.com/networks: "tke-bridge,tke-rdma-ipvlan"
creationTimestamp: null
labels:
k8s-app: mofed-test
qcloud-app: mofed-test
spec:
containers:
- command:
- sh
- -c
- |
ls -l /dev/infiniband /sys/class/infiniband /sys/class/net
sleep 1000000
image: ccr.ccs.tencentyun.com/tke-eni-test/mofed-5.4-3.1.0.0:ubuntu20.04-amd64-test
imagePullPolicy: Always
name: mofed-test-ctr
securityContext:
capabilities:
add: [ "IPC_LOCK" ]
resources:
limits:
nvidia.com/gpu: "8"
tke.cloud.tencent.com/tke-shared-rdma: 1
volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- emptyDir:
medium: Memory
sizeLimit: 64Gi
name: dshm

注意事项:获取 gid

在基于 NCCL 的训练任务中,NCCL_IB_GID_INDEX 环境变量是关键配置,需要配置为 RDMA 设备分配到的 GID。由于主机侧的网卡和容器侧网卡会同时运行,容器内的 GID 不是固定值3,且同一个节点上的每个 Pod 各不相同,需要为每个 Pod 单独获取和设置。RDMA 设备的 GID 可以通过 show_gids 命令获取
请注意,只有在 NCCL 2.21 版本以前需要设置这个变量,而在 2.21 版本及以后,该值会被动态设置,不需要设置,详情请参见 NCCL 库官网文档

测试验证

部署测试用 Statefulset,可以部署上述样例中的 mofed-test。Pod 创建以后,可以登录 Pod 内进行测试:
kubectl exec -it mofed-test-0 -- bash

基础功能测试

使用 ibv_devinfo 命令确认 RDMA 设备功能正常:
# ibv_devinfo
hca_id: mlx5_bond_0
transport: InfiniBand (0)
fw_ver: 22.33.1048
node_guid: 946d:ae03:00a9:3ed0
sys_image_guid: 946d:ae03:00a9:3ed0
vendor_id: 0x02c9
vendor_part_id: 4125
hw_ver: 0x0
board_id: MT_0000000359
phys_port_cnt: 1
port: 1
state: PORT_ACTIVE (4)
max_mtu: 4096 (5)
active_mtu: 4096 (5)
sm_lid: 0
port_lid: 0
port_lmc: 0x00
link_layer: Ethernet

hca_id: mlx5_bond_1
transport: InfiniBand (0)
fw_ver: 22.33.1048
node_guid: 946d:ae03:00ab:d6dc
sys_image_guid: 946d:ae03:00ab:d6dc
vendor_id: 0x02c9
vendor_part_id: 4125
hw_ver: 0x0
board_id: MT_0000000359
phys_port_cnt: 1
port: 1
state: PORT_ACTIVE (4)
max_mtu: 4096 (5)
active_mtu: 4096 (5)
sm_lid: 0
port_lid: 0
port_lmc: 0x00
link_layer: Ethernet
...
使用 show_gids 命令获取 gid,这里可以看到对应的 gid 为 7:
# show_gids
DEV PORT INDEX GID IPv4 VER DEV
--- ---- ----- --- ------------ --- ---
mlx5_bond_0 1 4 fe80:0000:0000:0000:401d:55ff:fe14:6ce8 v1 bond0
mlx5_bond_0 1 5 fe80:0000:0000:0000:401d:55ff:fe14:6ce8 v2 bond0
mlx5_bond_0 1 6 0000:0000:0000:0000:0000:ffff:1e3d:b4ac 30.61.180.172 v1 bond0
mlx5_bond_0 1 7 0000:0000:0000:0000:0000:ffff:1e3d:b4ac 30.61.180.172 v2 bond0
mlx5_bond_1 1 4 fe80:0000:0000:0000:0c0d:00ff:fe48:33aa v1 bond1
mlx5_bond_1 1 5 fe80:0000:0000:0000:0c0d:00ff:fe48:33aa v2 bond1
mlx5_bond_1 1 6 0000:0000:0000:0000:0000:ffff:1e3d:b4bc 30.61.180.188 v1 bond1
mlx5_bond_1 1 7 0000:0000:0000:0000:0000:ffff:1e3d:b4bc 30.61.180.188 v2 bond1
...

连通性测试

使用 ibv_rc_pingpong 命令测试 RDMA 连通性:
Server 端配置:
# ibv_rc_pingpong -d mlx5_bond_0 -g 7
local address: LID 0x0000, QPN 0x003a22, PSN 0xc882ae, GID ::ffff:30.61.130.20
remote address: LID 0x0000, QPN 0x001234, PSN 0x58320b, GID ::ffff:30.61.180.172
8192000 bytes in 0.01 seconds = 7687.51 Mbit/sec
1000 iters in 0.01 seconds = 8.52 usec/iter
Client 端配置,注意 Client 端和 Server 的 gid 不一定相同。
# ibv_rc_pingpong -d mlx5_bond_0 -i 1 30.61.130.20 -g 7
local address: LID 0x0000, QPN 0x001234, PSN 0x58320b, GID ::ffff:30.61.180.172
remote address: LID 0x0000, QPN 0x003a22, PSN 0xc882ae, GID ::ffff:30.61.130.20
8192000 bytes in 0.01 seconds = 7924.55 Mbit/sec
1000 iters in 0.01 seconds = 8.27 usec/iter

带宽测试

使用 ib_write_bw 命令测试 RDMA 带宽:
Server 端配置:
# ib_write_bw -d mlx5_bond_0 -x 7

************************************
* Waiting for client to connect... *
************************************


---------------------------------------------------------------------------------------
RDMA_Write BW Test
Dual-port : OFF Device : mlx5_bond_0
Number of qps : 1 Transport type : IB
Connection type : RC Using SRQ : OFF
CQ Moderation : 100
Mtu : 4096[B]
Link type : Ethernet
GID index : 7
Max inline data : 0[B]
rdma_cm QPs : OFF
Data ex. method : Ethernet
---------------------------------------------------------------------------------------
local address: LID 0000 QPN 0x3a25 PSN 0x8877cd RKey 0x00ca88 VAddr 0x007fb84630d000
GID: 00:00:00:00:00:00:00:00:00:00:255:255:30:61:130:20
remote address: LID 0000 QPN 0x1235 PSN 0xe2acbc RKey 0x007c3e VAddr 0x007f8f164b7000
GID: 00:00:00:00:00:00:00:00:00:00:255:255:30:61:180:172
---------------------------------------------------------------------------------------
#bytes #iterations BW peak[MB/sec] BW average[MB/sec] MsgRate[Mpps]
65536 5000 97.50 97.49 0.185950
---------------------------------------------------------------------------------------
Client 端配置:
# ib_write_bw -d mlx5_bond_0 -x 7 30.61.130.20 --report_gbits
---------------------------------------------------------------------------------------
RDMA_Write BW Test
Dual-port : OFF Device : mlx5_bond_0
Number of qps : 1 Transport type : IB
Connection type : RC Using SRQ : OFF
TX depth : 128
CQ Moderation : 100
Mtu : 4096[B]
Link type : Ethernet
GID index : 7
Max inline data : 0[B]
rdma_cm QPs : OFF
Data ex. method : Ethernet
---------------------------------------------------------------------------------------
local address: LID 0000 QPN 0x1235 PSN 0xe2acbc RKey 0x007c3e VAddr 0x007f8f164b7000
GID: 00:00:00:00:00:00:00:00:00:00:255:255:30:61:180:172
remote address: LID 0000 QPN 0x3a25 PSN 0x8877cd RKey 0x00ca88 VAddr 0x007fb84630d000
GID: 00:00:00:00:00:00:00:00:00:00:255:255:30:61:130:20
---------------------------------------------------------------------------------------
#bytes #iterations BW peak[Gb/sec] BW average[Gb/sec] MsgRate[Mpps]
65536 5000 97.50 97.49 0.185950
---------------------------------------------------------------------------------------

NCCL 测试

准备工作

NCCL 测试需使用带有 NCCL 相关库的测试镜像,可使用如下样例 YAML:
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
k8s-app: nccl-test
name: nccl-test
namespace: default
spec:
replicas: 2
selector:
matchLabels:
k8s-app: nccl-test
qcloud-app: nccl-test
serviceName: ""
template:
metadata:
annotations:
tke.cloud.tencent.com/networks: "tke-bridge,tke-rdma-ipvlan"
creationTimestamp: null
labels:
k8s-app: nccl-test
qcloud-app: nccl-test
spec:
containers:
- command:
- sh
- -c
- |
ls -l /dev/infiniband /sys/class/infiniband /sys/class/net
sleep 1000000
image: ccr.ccs.tencentyun.com/qcloud-ti-platform/nccl_ofed:cu12.0
imagePullPolicy: IfNotPresent
name: nccl-test
securityContext:
capabilities:
add: [ "IPC_LOCK" ]
resources:
limits:
nvidia.com/gpu: "8"
tke.cloud.tencent.com/tke-shared-rdma: 1
volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- emptyDir:
medium: Memory
sizeLimit: 64Gi
name: dshm

测试步骤

1. 创建部署测试 Pod。按照上述 yaml 部署生成了两个 Pod:nccl-test-0,nccl-test-1。
2. 配置 ssh 免密登录。
mpirun 需要通过 ssh 登录到其他节点执行相关命令,所以需要配置免密登录,首先在 nccl-test-0 上执行:
# 生成密钥
ssh-keygen
# 查看公钥
cat ~/.ssh/id_rsa.pub
然后在 nccl-test-1 上执行下述命令,配置免密登录:
mkdir -p /run/sshd
# 启动 sshd
/usr/sbin/sshd
mkdir -p ~/.ssh
# <id_rsa.pub> 为 nccl-test-0 Pod 里 /root/.ssh/id_rsa.pub 的文件内容
echo "<id_rsa.pub>" >> ~/.ssh/authorized_keys
在 nccl-test-0 上测试是否可以免密登录 nccl-test-1,假设 nccl-test-0 的 ip 为 172.21.4.8,nccl-test-1 的 ip 为 172.21.5.6:
ssh root@172.21.5.6
3. 启动 nccl 测试任务。
在 nccl-test-0 上执行 mpirun 可启动 nccl all_reduce_perf 测试任务:
mpirun --allow-run-as-root -np 16 \\
-npernode 8 -H 172.21.4.8:8,172.21.5.6:8 \\
-mca btl_tcp_if_include bond0 \\
-x NCCL_IB_DISABLE=0 \\
-x NCCL_IB_GID_INDEX=5 \\
-x NCCL_DEBUG=INFO \\
/root/nccltest/all_reduce_perf -b 1G -e 1G -f 2 -g 1 -n 20
部分参数说明:
btl_tcp_if_include:指定 openmpi 运行时通信使用的网卡,由于网络环境同时有多个网段(每个 RDMA 独属一个网段,eth0 一个网段),所以需要指定只使用 bond0 或者 eth0 进行通信。若该变量不设置会出现报错:Open MPI failed to TCP connect to a peer MPI process.
NCCL_IB_DISABLE:0 代表 NCCL 使用的 IB/RoCE 传输,1 代表禁止 NCCL 使用的 IB/RoCE 传输。此时 NCCL 将退回到使用IP套接字。
NCCL_IB_GID_INDEX:指定 RoCE 模式中使用的全局 ID 索引。获取方法请参考本文中的 注意事项:获取 gid。只有在 NCCL 2.21 版本以前需要设置这个变量,而在 2.21 版本及以后,该值会被动态设置,不需要设置。
NCCL_DEBUG:NCCL_DEBUG 变量控制从 NCCL 显示的调试信息。
以上测试任务成功之后的回显结果类似为:
#
# out-of-place in-place
# size count type redop root time algbw busbw #wrong time algbw busbw #wrong
# (B) (elements) (us) (GB/s) (GB/s) (us) (GB/s) (GB/s)
1073741824 268435456 float sum -1 6455.8 166.32 311.85 0 6426.3 167.09 313.29 0
# Out of bounds values : 0 OK
# Avg bus bandwidth : 312.571
#