☰
800卡A100集群两层CLOS与Rail-Optimized组网实践
2026/10/23 1:05:23 网站建设 项目流程

一个 800 卡 A100 的集群,真正先在图纸上把人逼疯的不是算力、不是存储,而是网络。算力好堆,卡插满就行;网络不行,端口数、收敛比、跳数、布线长度、光模块型号,哪一项算错都要推倒重来。我最近在推演一个 800 卡规模的训练集群,核心约束给得很死:只用 64 端口的交换机,走两层拓扑,做 Fat-Tree 架构。这个题目听起来很像教科书里的经典 CLOS 练习,但真把数字代进去算一遍,你会发现端口账、收敛比、Rail 亲和性三者互相咬着,一步算偏就得返工。

这篇文章就把我这几轮推演的过程完整拆开:从 GPU 通信模式倒推带宽需求,到两层 CLOS 的端口公式怎么列,再到为什么最后选了 Rail-Optimized 的布线方式,以及上电之后 RoCE 怎么调、NCCL 怎么验证、哪些坑是我在别的项目里真踩过的。内容偏工程实操,适合正在做集群网络规划、或者准备从三层 Fat-Tree 收敛到两层 CLOS 的同行参考,刚接触 RDMA 组网的读者也能顺着算一遍看懂逻辑。

1. 先把账算清楚:800 卡到底需要什么样的网络

做拓扑设计最容易犯的错,是先选交换机再想业务,结果算到一半发现端口不够,只能加收敛比凑数。正确的顺序反过来:先看 GPU 之间怎么通信,通信需要多少带宽、多少跳、什么样的时延,倒推出网络的硬指标,再去匹配交换机和端口数。800 卡这个规模卡在一个很微妙的位置:三层 Fat-Tree 明显过剩,单层又接不下,两层 CLOS 几乎是唯一解,但两层的具体形态有好几种,选错了性能和成本都难看。

1.1 从 AllReduce 流量模型反推单卡带宽需求

大模型训练的主流并行方式无非三种:数据并行、张量并行、流水并行。这三种对网络的胃口完全不同,得分开看。

数据并行的核心操作是 AllReduce,Ring 算法下传输量约等于2(N-1)/N × M,N 是参与通信的 GPU 数。800 卡的时候 (N-1)/N 约等于 0.9987,基本就是 2M,也就是说每一份梯度数据要在网络上走两遍。这意味着 AllReduce 的时间几乎完全由最慢的那条链路决定,任何一条链路出现拥塞或者 ECMP 哈希不均,整个通信环就被拖住,800 张卡一起等。

张量并行是另一种画风。TP 组内的 GPU 要做 AllGather 和 ReduceScatter,通信频繁、消息小、时延敏感,对跳数特别挑剔。每多一跳交换机,端到端时延大概增加 0.8 到 1.2 微秒(含转发和排队),TP 组内如果跨了三跳,累积起来就是好几微秒,对计算占比高的算子影响很明显。这也是为什么 TP 一般限制在单机 8 卡内,靠机内高速互联解决。

流水并行是点对点,带宽需求相对温和,但对带宽稳定性要求高,因为 PP 的 bubble 时间直接取决于相邻 stage 之间的传输抖动。

三种模式叠加,网络的设计目标就很清楚了:AllReduce 要带宽和均衡,TP 要低跳数,PP 要稳定。落到具体数字上,单卡配 200G 网卡是一个比较均衡的起点——8 卡节点就是 1.6Tbps 的出口带宽,配合两层 CLOS,任意两张卡之间最多两跳交换机,端到端时延能压在 5 微秒以内。

这里有个容易被忽略的点:单卡 200G 并不意味着 AllReduce 能跑到 200G。Ring AllReduce 的有效带宽只有链路带宽的一半左右,因为数据要绕环走两遍。所以 200G 网卡的 AllReduce 实测 algbw 通常在 11 到 12.5GB/s 之间,busbw 才能接近 25GB/s。验收的时候别拿 algbw 对标线速,会误判。

1.2 为什么是两层 CLOS,而不是经典三层 Fat-Tree

教科书上的 Fat-Tree 是三层结构:core、aggregation、edge,k 端口交换机可以支持 k³/4 个主机。k=64 的时候理论上是 65536 台主机,规模大得离谱,但代价也大——每台主机要经过三跳交换机,core 层的交换机和光模块数量惊人。

800 卡拆成 100 台 8 卡服务器,每台 8 个 200G 端口,总共 800 个接入端口。这个量级用三层是杀鸡用牛刀,而且多一跳就多一份时延和一份故障概率。两层 CLOS(也就是常说的 Spine-Leaf)在端口数和跳数之间取得了更好的平衡:Leaf 层接服务器,Spine 层只做 Leaf 之间的全互联,任意两台服务器之间固定两跳。

真正让两层方案成立的关键,是上行端口和下联端口的比例可以被精确控制。一台 64 端口交换机,如果拿 32 口下联、32 口上联,那单台 Leaf 的下联带宽和上联带宽天然是 1:1,无阻塞就是设计出来的,不是调出来的。三层 Fat-Tree 反而很难做到这么干净的比例,因为 core 层的端口要分摊给多个 pod。

1.3 三条硬指标:无阻塞、低时延、可预测

把需求收敛成可验收的指标,我一般会写三条:

指标目标值验收方式
网络收敛比1:1(Leaf 层上下行对称)端口带宽核算
任意两卡端到端时延≤ 5μs(不含网卡处理)打流仪逐对测量
AllReduce busbw≥ 线速的 85%NCCL 全网压测
故障切换收敛时间≤ 1s(ECMP 重哈希)拔纤注入测试

“可预测”这条比较虚,但对训练集群特别重要。所谓可预测,是指同样的通信模式下,每次跑出来的带宽波动不超过 5%。波动大通常来自 ECMP 哈希不均或者 PFC 反压扩散,这两个问题在后面的配置章节会重点讲。

2. 端口账怎么算:两层 CLOS 的三种可行解

端口计算是整个设计里最像做数学题的部分,但它有唯一解吗?没有。同样的 64 端口交换机、同样的 800 卡规模,至少有三种自洽的方案,差别在收敛比、布线复杂度、扩展余量上。这一段我把公式列出来,然后把三种方案摆在一起对比。

2.1 三条约束公式,先把可行域框住

设 Leaf 交换机数量为 L,每台 Leaf 下联端口数 d,上联端口数 u,Spine 交换机数量 S。约束条件有三条:

  • 端口守恒:d + u = 64,因为一台交换机就 64 个口。
  • 接入覆盖:L × d ≥ 800(考虑 breakout 的话是 L × 2d ≥ 800)。
  • 上行收敛:S × 64 ≥ L × u。

把这三条摆开,再用“上下行对称”这个额外约束(u = d = 32),能立刻得到 L ≥ 25,S ≥ 12.5 也就是 13。但这只是数学可行,工程上未必是最优。

如果放弃严格对称,允许一定收敛比,可行域会宽松很多。比如 L=20、d=40、u=24,此时 S ≥ 20×24/64 = 7.5,取 8,每台 Leaf 到每台 Spine 正好 3 条链路,布线极其规整,收敛比是 40:24 即 1.67:1。

收敛比的算法要说清楚:收敛比 = 下联总带宽 ÷ 上联总带宽。1.67:1 的意思是所有下联端口同时满速发送时,上行方向会有 1.67 倍的超订。对于 AllReduce 为主的流量,全网同时满速的概率不高,1.67:1 在不少生产集群里是可以接受的,但要做 ECN 和 PFC 调优兜底。

2.2 方案对比:标准 CLOS 与 Rail-Optimized

矩阵算完,落到具体形态其实有两个大方向,这两个方向的差别不在端口数,而在哪张 GPU 卡接到哪台 Leaf。

标准 CLOS的做法是:一台服务器上的 8 个网卡,随便分给不同的 Leaf,只要保证每台 Leaf 下联的服务器数量均衡。好处是布线灵活,任意一台 Leaf 故障只影响它下面挂的那几十张卡。

Rail-Optimized的做法更讲究:所有服务器的第 0 号 GPU 都接到同一组 Leaf(称为 rail 0),第 1 号 GPU 接到 rail 1 的 Leaf,以此类推。这样的好处是,同一 rail 内的 GPU 通信只走一跳 Leaf,跨 rail 才走 Spine。对于 TP 组和部分 AllReduce 场景,这个优化能显著降低时延。

我把两种方案的核心参数列出来对比:

对比项标准 CLOSRail-Optimized
Leaf 数量13 台(按 800 口算)16 台(8 rail × 2 台)
Spine 数量8 台8 台
单 Leaf 下联64 个 200G 口64 个 200G 口
单 Leaf 上联32 个 400G 口32 个 400G 口(满载时)
同 rail 通信跳数1 跳(同 Leaf 内)1 跳(稳定保证)
接入端口利用率96%(800/832)78%(800/1024)
扩展性差,端口几乎用满好,可扩到 1024 卡
布线复杂度中等较高,需跨柜

标准 CLOS 接入效率高,但没余量;Rail-Optimized 浪费了 22% 的接入端口,换来的是确定的通信路径和扩展空间。做训练集群,我倾向于后者——训练集群的扩容是常态,今天 800 卡,明年可能就是 1024 卡,端口预留的价值远大于那点交换机成本。

2.3 最终定稿:16 Leaf + 8 Spine 的参数表

定下来的方案是 Rail-Optimized 的两层结构,完整参数如下:

参数数值说明
服务器数量100 台每台 8 卡,共 800 卡
单卡网卡200G RoCEv2每卡一个端口
单服务器上行8 × 200G = 1.6Tbps8 个独立网卡
Leaf 交换机16 台,64 × 400G8 个 rail,每 rail 2 台
Leaf 下联32 口 400G 拆 64 × 200G每台接 64 张卡
Leaf 上联32 口 400G(满载)8 台 Spine 各 4 条
Spine 交换机8 台,64 × 400G满配时 64 口全下联
Spine 下联当前 48 口,满配 64 口预留扩展
总接入能力1024 卡当前使用 800 卡
全网收敛比1:1Leaf 层上下行对称

当前 800 卡的实际配置是:8 个 rail 中,每 rail 的第一台 Leaf 接 64 张卡(上联 32 口),第二台 Leaf 接 36 张卡(上联先配 16 口)。这样每台 Spine 收到 8×4 + 8×2 = 48 条链路,用 48 个端口,留 16 个口给未来扩容。

补齐到 1024 卡的时候,每 rail 第二台 Leaf 也会接满 64 张卡,上联从 16 口升到 32 口,全网链路数从 384 条变成 512 条,8 台 Spine 各 64 口正好用满,一根线都不用重新布。这个设计的前瞻性就体现在这里——当前配置不需要把机框插满,但机框和端口的分配必须按满载来规划。

3. 关键设计细节:从端口拆分到机柜布线

拓扑图好看,落地全是细节。400G 端口怎么拆成 200G、光模块选 MPO-12 还是 MPO-16、线缆怎么走、标签怎么打,这些东西在图纸上看不出来,但在机房里能让你多干三天。

3.1 400G 拆 200G:breakout 的四个坑

用 64 口 400G 交换机接 200G 网卡,必须用 breakout(端口拆分)。一个 400G 口通过分支光缆拆成 2 个 200G 口,这是最省交换机的做法,一台 64 口交换机相当于 128 个 200G 口。

第一个坑是光模块和分支缆的匹配。400G 端如果用的是 8 通道并行(比如 400G-SR8 或者 400G-DR4 的并行形态),拆成 2 个 200G 时,分支缆一端是 MPO-12 或 MPO-16 接交换机,另一端分出来两个 MPO-12 接网卡。极性(Type A / Type B / Type C)必须和主干保持一致,接反了不通,而且不通得毫无提示,灯亮着但链路起不来。

第二个坑是FEC 配置。200G 走 PAM4,前向纠错默认是 RS-FEC(544),某些网卡在特定固件下需要显式打开或者降级到 FC-FEC,否则误码率高得离谱,表现为链路 up 但一跑流量就丢包。我一般是先在两端手动对齐 FEC 模式,确认跑通,再交给自动协商。

第三个坑是分支缆长度。分支缆本身有一定长度(常见 1 到 3 米),从交换机端口分出来后要在机柜内绕到配线架。如果分支缆选短了,端口位置和配线架位置对不上,只能整根换。买之前拿卷尺在机柜上量一遍,比看图纸靠谱。

第四个坑是同一 400G 口的两个 200G 子口不能提前混用。一个 400G 口拆开后,两个子口必须都归属同一台 Leaf 的同一组 rail,不能一个接 rail 0 一个接 rail 3,否则 breakout 的物理通道就乱了。在规划表里要把“物理端口号 → 逻辑端口号 → 服务器编号 → GPU 序号”四者的映射一次性定死。

3.2 上行链路怎么分配:LAG 还是 ECMP

每台 Leaf 有 32 个 400G 上行口,8 台 Spine,每台 4 条链路。这 4 条链路有两种接法:

第一种是LAG(链路聚合),把到同一台 Spine 的 4 条链路捆成一个逻辑口。好处是配置简单,坏处是哈希粒度粗,同一个 LAG 内部容易极化,而且一台 Spine 故障时整个 LAG 起不来,重收敛慢。

第二种是ECMP(等价多路径),4 条链路各自是独立的三层接口,跑 eBGP 或者 OSPF,靠路由的等价路径做负载。这是大集群的主流做法,哈希粒度细到每一条物理链路,单条链路故障只影响 1/32 的上行带宽,重收敛在毫秒级。

我选 ECMP。配置上的关键是把哈希字段配对。RoCEv2 的流量是 UDP 封装,哈希必须包含源 IP、目的 IP、UDP 源端口、UDP 目的端口四个字段,少配一个都会导致大量流量落在同一条链路上。有些交换机默认只哈希 IP 五元组里的三层信息,对 RoCE 这种源端口随机的流量反而不友好,得手动打开四层哈希。

3.3 线缆规划与机柜布局

13 个计算柜放 100 台服务器(12 柜满配 8 台,1 柜 4 台),Leaf 集中放在两个网络柜里。这种放法的问题是服务器到 Leaf 的距离可能超过 30 米,铜缆和 AOC 都够呛。

实际方案是:服务器到 Leaf 走MPO 主干光缆 + 配线架。每个机柜顶部配一个高密度光纤配线架,服务器出来的分支缆先上配线架,通过主干光缆拉到网络柜,再从网络柜的配线架跳线到 Leaf。这样线缆长度可控,后期改线也只需要动跳线,不用重新穿线。

主干光缆的数量按满载算:当前 400 根下联分支缆、384 根上联主干。满载时是 512 根下联 + 512 根上联。主干光缆一次性按满载敷设,分支缆按需买,这是比较省钱的节奏。

线缆类型数量(当前)数量(满载)备注
400G 转 2×200G 分支缆400 根512 根服务器侧
400G 上联主干384 根512 根Leaf 到 Spine
200G 网卡侧模块800 个1024 个与网卡同厂兼容性最好
配线架15 个15 个每柜一个,含网络柜

强烈建议主干光缆按满载一次布好。后期扩容时最贵的不是光模块,是停机穿线的人工和风险。线缆在桥架里多躺一年不花钱,等到要扩的时候再穿,得停半层楼的业务。

3.4 命名规范:让排障时间缩短一半

设备命名和端口标签看起来是小节,实际上是排障效率的分水岭。我用的规则是:

Leaf 命名为lf-r{rail}-{序号},比如lf-r0-01、lf-r0-02;Spine 命名为sp-01到sp-08。服务器命名为node-{三位编号},每个网卡端口标签写成node-001-g0到node-001-g7,g 后面的数字就是 GPU 序号,也是 rail 号。

这样做的直接好处是:NCCL 报错说mlx5_3通信异常,你能立刻定位到第 3 号 rail 的 Leaf,再顺着标签找到具体端口和服务器。没有这套命名,机房排障就是大海捞针。

端口描述也要写进交换机配置里,格式统一为to:node-001-g0或者to:sp-03:e1/1。几个月后回头看配置,这些描述能救命。

4. 上电到通流:配置顺序与调优参数

设备上架、线缆插好、灯全绿,这只是物理层通了,离“能跑训练”还差一整套配置。这一段按我实际的操作顺序走一遍,每一步都说明为什么这么做。

4.1 网络层设计:为什么用 L3 到 Leaf

大集群的主流做法是L3 到 Leaf,也就是服务器网关放在 Leaf 上,Leaf 和 Spine 之间跑三层路由。早期的 L2 大二层方案(VXLAN、STP 或者 TRILL)在 800 卡这个规模下问题太多:广播域过大、STP 收敛慢、故障域不可控。

L3 到 Leaf 的好处是每个 Leaf 就是一个小型广播域,故障被限制在局部。Leaf 和 Spine 之间跑 eBGP,每台 Leaf 用自己的私有 ASN,Spine 用一个公共 ASN,配置量不大但扩展性好。

ASN 规划我一般这么分:Spine 用 AS 65000,8 台都用一个;Leaf 从 65001 排到 65016,一台一个。eBGP 邻居关系是每台 Leaf 和 8 台 Spine 全互联,一台 Leaf 上有 8 个邻居,一台 Spine 上有 16 个邻居。

# Leaf 侧配置示意(伪代码,不同厂商语法有差异) router bgp 65001 neighbor 10.0.0.1 remote-as 65000 neighbor 10.0.0.2 remote-as 65000 ! ... 一直到 10.0.0.8 address-family ipv4 unicast network 10.100.1.0/24 ! 本 Leaf 下的服务器网段 maximum-paths 8 ! 开启等价多路径

maximum-paths 8这行是关键,不开的话 BGP 只选一条最优路径,ECMP 就废了,32 条上行链路只用 1 条,带宽直接掉到 1/32。

4.2 ECMP 哈希与全局负载均衡

普通 ECMP 有个毛病叫哈希极化:不同的 Leaf 之间流量模式不一样,有的 Leaf 上行流量集中在少数几条链路上,虽然总体带宽够用,但局部已经打满了。根本原因是 ECMP 哈希用的是固定的五元组,同一个流永远走同一条路,而大量同源同目的的大流会让哈希分布严重偏斜。

解决手段是GLB(全局负载均衡)。它的做法是在转发面动态感知每条链路的利用率,把流量往空闲链路上引导,而不是死守哈希结果。开启 GLB 之后,长流的分配会随负载动态调整,实测在 AllReduce 场景下,链路利用率标准差能从 18% 降到 5% 以内。

代价是 GLB 会导致少量的乱序。对 RoCE 来说,乱序本身不是问题,问题是乱序被网卡当成丢包触发重传,反而恶化。所以开 GLB 之前必须确认网卡侧支持乱序容忍(大多数现代 RDMA 网卡都支持),并且把乱序容忍的窗口配大一点。

4.3 RoCEv2 的三个核心参数:PFC、ECN、DCQCN

RoCEv2 的流量控制是三件套,缺一不可:

PFC(优先级流控)兜底,防止缓冲区溢出丢包。配置时要单独划分一个无损队列,用 DSCP 值标记。我一般用 DSCP 26 对应优先级 3,服务器侧和交换机侧必须完全一致,有一处不一致就会变成有损队列。

ECN(显式拥塞通知)主动标记,让发送端提前降速。阈值设置有一套算法,核心是带宽时延积(BDP)。400G 链路的 BDP 大约是 400Gbps × 1μs = 50KB。ECN 的 Kmin 设成 BDP 的 3 倍(150KB 左右),Kmax 设成 BDP 的 30 倍(1.5MB 左右),标记概率从 0 线性升到 100%。

DCQCN是网卡侧的速率控制算法,靠 ECN 标记驱动。参数里最关键的是降速因子和恢复速率,配得太激进会导致速率震荡,配得太保守则收敛慢。

参数推荐值调整依据
PFC 队列Prio 3(DSCP 26)全网一致
PFC 触发门限缓冲区 30%配合 headroom 调整
ECN Kmin150KB3 × BDP
ECN Kmax1.5MB30 × BDP
ECN 标记概率0-100% 线性默认
DCQCN 降速因子0.5实测震荡明显时上调到 0.7
DCQCN 恢复速率5 Mbps/流大规模时降低
MTU9000(或 4200)全网一致

MTU 一定要全网统一。交换机和服务器两侧有一边是 9000 一边是 1500,会表现为“小包能通、大包丢”,排查起来极其费时。我的做法是在建网阶段就用大包 ping 逐段验证,ping -M do -s 8972。

4.4 NCCL 拓扑识别与 rail 亲和性验证

网络配好了,不代表 NCCL 用对了路径。NCCL 启动时会自己探测拓扑,但如果 Linux 侧看不到正确的网卡到 GPU 的亲和关系,它会选错网卡,导致流量走错 rail。

验证方法很直接,先看亲和性映射:

# 查看每个网卡对应的 NUMA 和 GPU nvidia-smi topo -m # 检查 NCCL 识别到的网卡 NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,NET \ all_reduce_perf -b 8 -e 128M -f 2 -g 8 2>&1 | head -50

输出里会打印NET/IB : Using [0]mlx5_0:1这类信息,把用到的网卡列表和topo -m的映射对一遍,如果 NCCL 用的网卡和预期不一致,用环境变量锁定:

export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_4,mlx5_5,mlx5_6,mlx5_7 export NCCL_IB_GID_INDEX=3 # RoCEv2 常用 export NCCL_SOCKET_IFNAME=bond0 # 控制面走普通网卡 export NCCL_IB_TC=26 # 和 DSCP 26 对应

NCCL_IB_TC这个参数很多人会漏,它的作用是把 NCCL 发出的流量打到正确的无损队列上。不配的话

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询