
1. 高性能计算里说的“负载均衡”和 Web 负载均衡根本不是一回事先把结论放前面如果你带着 Nginx 那套“转发请求、轮询后端”的思路来做高性能计算HPC的负载均衡大概率会把整个集群搞崩。我自己早年就干过这种蠢事——拿一套标准的 Web 七层负载均衡方案硬套在 GPU 训练集群前面结果训练任务跑起来之后机器间的通信延迟忽高忽低几个节点直接训练超时。后来才想明白HPC 的负载均衡核心痛点从来不是“谁的请求多分给谁”而是数据流怎么拆、怎么聚合网卡和交换机的瓶颈怎么绕开以及怎么让异构算力吃满。高性能计算场景下负载均衡面对的东西通常有三种多节点多卡通信流量比如大模型训练时几百张 GPU 卡之间的 AllReduce 集合通信流量一张卡可能同时朝几十张卡发数据流量是扇出扇入并存而不是 Web 那种一问一答。存储与计算之间的数据搬运读训练数据集、写 checkpoint、加载权重动辄几十 GB 甚至 TB 级的数据进出单条链路绝对扛不住。计算任务本身的切分把一个大任务拆成多个小任务分发到不同节点上跑再把结果汇总。这层面讲究的是怎么让“每台机器干完活的时间尽量一致”也就是算力层面的均衡。这才是 HPC 负载均衡的真正含义——不均衡的代价不是延迟高几毫秒而是整个集群的利用率断崖式下跌。比如 MoE 大模型里某几个 Expert 节点被海量 token 砸中其他节点闲得发慌那你的 PUE 再漂亮、单卡算力再强整体吞吐还是上不去。我见过最夸张的案例是某团队跑 MoE 模型时最忙的 Expert 卡占用率 97%最闲的只有 20%整个训练任务活活慢了四倍。这不是硬件问题纯粹是负载均衡没做好。所以接下来的篇幅我打算沿着几个核心方向逐一拆高性能计算集群的流量模型到底和 Web 差在哪多网卡场景下的负载均衡怎么做LACP 动态聚合是不是万能解MoE 这类模型里的“等开销负载均衡”是什么怎么用代码实现LVS 这类经典技术为什么在 HPC 里还活着怎么用 DR 模式解决硬件瓶颈。我尽量用大白话讲但你得有个心理准备这块内容牵扯到网络协议栈、内核转发、集合通信库的底层行为如果中间有哪个词看不懂可以跳着读核心思路能跟上就够用。2. 流量模型决定方案选型为什么 Web 那套“轮询”在 HPC 里不好使2.1 Web 负载均衡的核心连接级别的分发先看看 Web 负载均衡在干什么。一个 Nginx 服务后面挂 10 台应用服务器来一个 HTTP 请求Nginx 按轮询、最小连接数或者哈希策略挑一台机器转发上去。这个模式的基本单位是连接也就是 TCP 连接或者 HTTP 请求。这类负载均衡的特点是请求之间独立转发完就不管了状态信息要么放在 Redis 里要么靠 Session 粘滞流量相对平滑单个请求的体量通常只有几十 KB 到几 MB。所以 Web 负载均衡的算法核心在于**“怎么挑一台最合适的机器”**挑完就完事。算法再复杂本质上就是一个调度器。2.2 HPC 流量模型完全不同数据流是“全对全”的HPC 的流量模型尤其在分布式训练的场景里简直是另一种生物。拿深度学习中最常见的 AllReduce 操作来说100 张卡每张卡有自己算出来的一份梯度数据最终目标是要让每张卡都拿到 100 份数据的和或者均值。这意味着每张卡都要往其余 99 张卡发数据同时也要收 99 张卡的数据。这种“全对全”All-to-All通信模式带来的是带宽的几何级消耗。主流集合通信库NCCL、OpenMPI为了解决这个问题通常会选择 Ring-AllReduce 这种拓扑让每张卡只和相邻的两张卡通信数据在这条环上转一圈完成归并。这个算法很聪明但引入了另一个问题环上某个节点就是整条链路的瓶颈只要有一张卡慢了、一张网卡断了或者一条链路拥塞了整个 Ring 的吞吐量就被拖到最低的那个节点水平。这决定了 HPC 负载均衡的算法不能是“挑一台机器转发”那么轻量它面对的是持续、高带宽、低延迟约束极强latency-sensitive的数据流。我举个生活化的例子Web 负载均衡像快递分拣中心每个包裹独立分到不同货车拉走HPC 负载均衡更像地铁环线调度所有列车在一条闭合轨道上跑谁晚点整条线都晚点所以调度策略必须考虑“整条线的总吞吐”而不是某个站点的处理速度。2.3 等开销负载均衡让每台机器“同时完工”HPC 里有个很核心的概念叫“等开销”Equal Cost意思是所有并行任务的开销尽可能相等。比如你把一个需要 100 小时的计算任务拆成 100 份丢到 100 个节点上理想情况下每个节点跑 1 小时同时完成。如果分配不均一个节点跑了 10 小时其他节点早就空转等待了——你浪费的不是 9 小时的算力而是整个集群 100 节点在这 9 小时内的全部算力这就是所谓的“木桶效应”。等开销负载均衡的思路就是不是让每台机器干一样多的活而是让每台机器的完工时间尽量一致。这个理念在 AI 训练场景里尤为关键。比如异构集群里有人工智能专用芯片GPU/NPU和普通 CPU 节点同样一份数据GPU 跑得比 CPU 快几十倍你按“数据量均分”是错的必须按“算力权重均分”。这块后面在 MoE 部分我会展开细聊那里有最经典的等开销均衡场景。3. 多网卡负载均衡实操从 Bonding 到 RDMA 场景的取舍3.1 为什么要做多网卡负载均衡单卡带宽永远不够用现在的服务器随便一张 800G 的网卡在高端机器里已经不少见了但对 HPC 来说仍然不够。直接算一笔账现代 GPU 的显存带宽动辄 2 TB/s虽然训练时数据不一定要全部经过网卡但一旦要跨节点通信比如 8 卡节点之间做梯度同步通常每个节点要吐出几 GB 到几十 GB 的数据。就算你有 800G 网卡理论极限 100 GB/s实际刨掉协议开销能跑 70%-80% 就不错了这在大规模 AllReduce 面前依然捉襟见肘。多网卡几乎是 HPC 服务器的标配。常见的有两种形态多张独立的物理网卡分别连接到不同交换机通过路由策略或者网卡 bonding 分担流量单张网卡但多端口比如双口 100G 网卡两个端口分别接到不同的 ToR 交换机。这两种形态对应的负载均衡方案不太一样。3.2 内核 Bonding 方案的实操细节与坑最传统的多网卡负载均衡是 Linux 内核的 Bonding 驱动也就是把多张物理网卡绑成一个逻辑网卡对外表现成一个 IP。Bonding 有多个 mode但 HPC 场景下真正用得上的就这么几个mode 0round-robin数据包按顺序轮流从不同网卡发出去。看着很均衡但实际的坑是接收方如果开启了乱序检测TCP 的性能会被严重拖垮因为同一个 TCP 流的包从不同网卡发出经过不同路径到达顺序很可能是乱的。这是我在生产环境里排过的一个比较典型的故障——起初看监控网卡流量分布很均匀但实际应用吞吐只有单卡的水平。mode 4LACP 802.3ad这是 HPC 场景最推荐的模式。它会根据流特征做哈希源 MAC 目的 MAC IP 端口四元组把不同的流分布到不同物理网卡上。这样既保证了同一条流的包始终从同一张网卡走不乱序又能在多流场景下线性叠加带宽。mode 2balance-xor本质上和 mode 4 很像但它不需要交换机做 LACP 协商只要交换机侧把所有口配成一个聚合组就行。如果你的交换机比较老或者不想开 LACP可以用这个。这里有个关键提醒mode 4 的哈希粒度是流不是包。如果你只有一条大流量 TCP 流比如单机单进程拷贝一个大文件那 LACP 聚合的带宽不会增加因为这条流只会哈希到一张网卡上。HPC 里经常遇到这种问题——集合通信库跟多网卡 bonding 的哈希策略不对付时会出现“绑了 4 张 100G 网卡但 AllReduce 只用了其中一张”的诡异现象。破解办法有三个层面要么在应用层开多流并行比如 NCCL 的NCCL_SOCKET_IFNAME配合多 NIC 分割人为创造多条 TCP 流要么调整交换机或者网卡的哈希种子让大流尽量均匀散列要么干脆不走内核协议栈直接用 RDMA 网卡。3.3 RDMA 场景哈希分担动态路由才是正解HPC 高性能计算发展到今天RDMA远程直接内存访问已经是大模型训练的标配了。RDMA 的特点是数据绕过 CPU 和内核协议栈直接从网卡进内存。在这个场景下内核 bond 那套 TCP 层面的负载均衡根本不适用——RDMA 的可靠连接RC是点对点的、有状态的不能像 TCP 那样随意把不同包路由到不同网卡。所以 RDMA 多网卡负载均衡通常走两个路子RoCEv2 的多路由基于 ECMP在交换机层面配置等价多路径把不同 RoCE 流哈希到不同物理链路上。这里的哈希同样是基于流特征的——目标 IP 目标端口 源 IP。RoCE 流的源端口往往由网卡自动生成带有自适应扰动可以在哈希不均时自动打散。网卡自带的 Traffic Splitting比如 NVIDIA 的 ConnectX 网卡支持将单条 RoCE 流拆到多个端口上前提是两端都支持这种能力。这个功能在 NCCL 里叫NCCL_BUFFSIZE和 GPUDirect RDMA 结合使用的多 rail 优化。实操层面我建议你在做 RDMA 集群网络规划时可以提前想清楚几件事每个节点通常配 8 张 200G 网卡分两组各连两台交换机常见做法是 rail-optimized 拓扑这样任意两张卡之间都有多路径可达NCCL 会自动探测并做多 rail 负载均衡。实际上 NCCL 对多网卡的支持相当成熟它会自动把 Ring 上的不同段分配到不同网卡上实现带宽叠加。做这部分配置时踩过的一个重要教训是所有网卡的速率、MTU、流控设置必须完全一致不然 NCCL 的探测逻辑会直接放弃部分网卡导致性能跌到单卡水平。4. MoE 场景下的等开销负载均衡从原理到可复现代码4.1 MoE 模型为什么天然不均衡MoEMixture of Experts混合专家这两年火的根源大家应该都听过把一个大模型拆成多个 Expert 子网络每个 token 只激活其中一部分专家从而用极少的计算量支撑极大的参数量。理想很丰满但现实很骨感token 在专家上的分布极度不均匀。举个例子在一个 8 专家Expert的 MoE 层里某些高频 token比如标点、冠词、常见动词可能大量集中到某一个专家里而某些冷门实体词稀疏地散布到其他专家。一旦专家的负载失衡不仅训练速度被最忙的专家拖慢这批专家对应的显存、带宽都成了瓶颈。这就是 MoE 领域讨论最多的问题之一专家负载均衡本质上是“让每个专家处理的 token 数和对应的计算量尽量接近”也就是标题里说的“等开销负载均衡”。4.2 两层配平Token 分配辅助损失业界常用的做法是两层配平。第一层是Token 分配策略——路由器Router/Gate给每个 token 在所有专家上打分然后选 Top-k 个专家。但如果 Router 的网络权重不合理打分就容易被某些专家主导所以要加负载均衡损失Load Balancing Loss。这个损失函数的作用是惩罚那些被过度使用的专家鼓励 Router 把 token 更均匀地撒出去。第二层是Expert 并行时的跨设备均衡——如果不同专家被放置在不同 GPU 上负载均衡等于额外加了一层约束token 不仅要在专家间均衡还要在设备间均衡。如果某个 GPU 上恰好分配了太多个热门专家且 token 都涌进去这个 GPU 会成为整个训练任务的热点。所以现在的 MoE 框架普遍提供 Expert 放置策略选项比如按使用率动态迁移专家需要显存预留和通信规划。4.3 一个等开销负载均衡的代码实现热词里有人搜“moe负载均衡代码”我直接给一个简洁的 PyTorch 实现它来自我实际使用过的简化版本核心逻辑清晰、适合大家去复现扩展。import torch import torch.nn.functional as F def load_balancing_loss(router_probs, expert_indices, num_experts): 计算 MoE 负载均衡损失。 思路: 如果专家被选中的次数(期望)越高, 则惩罚越大, 鼓励均匀选择。 参数: router_probs: [num_tokens, num_experts] float, router 输出的归一化概率 expert_indices: [num_tokens, top_k] long, 每个 token 选中的专家索引 num_experts: int, 专家数量 返回: balance_loss: 标量损失, 可加权加入总 loss num_tokens router_probs.shape[0] top_k expert_indices.shape[1] # 1. 计算每个专家的平均路由概率。 # 取 router_probs 中所有 token 对应选中专家的概率, 再对专家维度聚合求期望。 # 这里用数学期望等价于: 对于每个专家 e, 累计 sum(router_probs[t, e] for t where e in expert_indices[t]) # 简单实现方式: 构造 one-hot 并加权求和 expert_mask F.one_hot(expert_indices, num_classesnum_experts) # [num_tokens, top_k, num_experts] # 每个 token 对每个专家的选中概率贡献: # router_probs[t] 里取的是对应选中专家的概率 按 top_k 维度加和, 再按 token 维平均 # 但为了严格等价于学术论文常见的公式: # f_i (1 / (num_tokens * top_k)) * sum_t( count(tok t选择专家i) ) # P_i (1 / num_tokens) * sum_t( router_probs[t, i] ) # 目标: minimize num_experts * sum_i (f_i * P_i) f_i expert_mask.float().sum(dim1).sum(dim0) / (num_tokens * top_k) # [num_experts] # 注意: one_hot 在 top_k 维求和后再 sum(dim0) 得到每个专家被选中的原始计数 P_i router_probs.mean(dim0) # [num_experts] balance_loss num_experts * torch.sum(f_i * P_i) return balance_loss # 使用示例 if __name__ __main__: torch.manual_seed(42) num_tokens, num_experts, top_k 32, 4, 2 # 模拟 router logits logits torch.randn(num_tokens, num_experts) router_probs F.softmax(logits, dim-1) # 模拟 top-k 专家选择 expert_indices torch.topk(logits, ktop_k, dim-1).indices loss load_balancing_loss(router_probs, expert_indices, num_experts) print(fLoad balancing loss: {loss.item():.4f}) # 如果路由器完全均匀, 期望 loss 接近 1.0; 极端不均衡会显著 1.0这段代码的核心就一个公式把“每个专家被选中的实际比例 f_i”和“路由器给每个专家的平均概率 P_i”做内积然后乘上专家数量。当分布均匀时f_i 和 P_i 都接近 1/N内积是 N × (1/N²) 1/N再乘 N 后接近 1.0极度不均衡时这个值会显著变大反向传播会把 router 的权重往均匀方向拉。我在实际训练里一般把这个 loss 的权重设成 0.01太大容易让 router 忽略真实任务语义太小又压不住不平衡。如果你发现某个专家长期空转可以先检查这个 loss 有没有真的在下降——如果它在降但专家还是很闲说明你的 Top-k 采样里还有别的问题比如专家 dropout 或者序列 padding 的影响。4.4 分组均衡降低通信开销的偏方还有一种工程上很常用的偏方叫“分组均衡”Grouped Load Balancing。既然全量均衡会导致 token 在专家间频繁跨设备跳转通信开销太大那么就把 token 先分到固定的几个组里每组只在某几个专家子集上强制均衡。这样组内通信不出组、组间通信被限制通信开销降下来了虽然均衡度理论上不如全局最优但工程收益极好。我用这个方法把 MoE 训练里的跨设备通信量直接降了 40%代价是理论算力利用率损失不到 5%。很多线上框架比如 DeepSeek 的 MoE 实现路线都对这种思路有体现换汤不换药核心就是等开销思想加通信局部性。5. LVS 在 HPC 里的正确打开姿势DR 模式为什么还没被淘汰5.1 经典负载均衡-LVS 的老树新花很多人一搜“负载均衡-lvs”想到的还是十几年前的 LVSLinux Virtual Server。实话实说在这个 docker、k8s、service mesh 满天飞的时代LVS 确实不如当年那么显眼。但它在 HPC 领域的定位非常特殊LVS 是纯内核态四层转发性能极高而且转发过程不改数据内容。LVS 最经典的三种模式NAT 模式请求进 LVSLVS 改目标地址转发给后端回包再经过 LVS 改源地址。这种模式下 LVS 是瓶颈因为所有流量都要过它DRDirect Routing模式LVS 只负责把请求的 MAC 地址改成后端机器的 MAC然后直接转发到二层网络。后端机器配置一个虚拟 IPVIP直接回包给客户端响应流量完全绕过 LVS。TUN 模式靠 IP 隧道封装适合跨网段场景。在 HPC 集群里比如登录节点、管理节点、存储网关、推理服务入口这些服务的流量特征是“小请求大数据响应”——客户端发一个推理请求可能只有几 KB但模型的响应可能几百 KB 到几 MB。这种场景 DR 模式堪称完美请求进来时让 LVS 做负载均衡分发响应直接由后端 GPU 服务器回给客户端LVS 不碰响应流量压力极小。5.2 LVS 的调度算法在 HPC 里的选型LVS 自带的调度算法里HPC 场景我推荐这几个加权最少连接WRR / WLC适合异构算力。GPU 强的节点权重高请求自然多分一些。它比单纯轮询更符合“等开销”的理念——目标不是平均请求数而是平均“完工时间”。基于目的地址的哈希DH如果你的后端是带缓存的服务这个算法能把同一个客户端的请求固定到同一台机器提高缓存命中率。在推理场景很有用。最少延迟调度LVS 内核模块在较新版本里支持根据后端实时负载加权调度适合处理长尾请求。实操提一句DR 模式需要后端服务器把 VIP 配在 loopback 接口上并且关闭 ARP 响应通常用arp_ignore1和arp_announce2否则交换机 ARP 表会乱套。这个配置细节几乎每隔一阵就有人踩坑——现象是 LVS 配好了但 curl 访问时有时通有时不通重启网络服务就好了过一阵又复发。本质就是后端机器抢答了 VIP 的 ARP 请求。5.3 一个生产可用的 keepalivedLVS 配置片段如果你们还在用物理机或者虚拟机部署推理服务我直接给一段可以抄的 LVS DR 配置# 在 LVS 节点上, 使用 keepalived 管理 VIP 和转发规则 # /etc/keepalived/keepalived.conf vrrp_instance VI_1 { state MASTER # 主节点 interface eth0 # 承载 VIP 的网卡 virtual_router_id 51 priority 100 advert_int 1 virtual_ipaddress { 192.168.50.100/24 dev eth0 label eth0:0 } } virtual_server 192.168.50.100 8080 { delay_loop 5 lb_algo wrr # 加权轮询, 配合下方 weight lb_kind DR # Direct Routing protocol TCP real_server 192.168.50.11 8080 { weight 10 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 2 } } real_server 192.168.50.12 8080 { weight 5 # 弱一点的机器降低权重 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 2 } } }后端机器192.168.50.11/12上执行# 将 VIP 配置在 lo 接口, 且关闭 ARP 响应 sudo ip addr add 192.168.50.100/32 dev lo sudo sysctl -w net.ipv4.conf.lo.arp_ignore1 sudo sysctl -w net.ipv4.conf.lo.arp_announce2 # 将所有网卡的 ARP 行为一并收紧更稳妥 sudo sysctl -w net.ipv4.conf.all.arp_ignore1 sudo sysctl -w net.ipv4.conf.all.arp_announce2这里有个细节lb_algo wrr的权重写的 10 和 5不是随便拍的。你可以根据后端机器的 benchmark 得分调整或者在运维脚本里每 10 分钟探测一次后端负载动态改写 keepalived 配置。我在生产里就是这么干的用 Prometheus 抓 GPU 利用率利用率高就下调权重低就上调整体效果比静态配置提升明显不少。6. 高性能计算负载均衡的排查手段与实战经验6.1 网络监控层的核心指标做 HPC 网络运维光会配不会排查等于白干。我的经验是先盯几个核心指标再谈负载均衡做得对不对网卡丢包计数rx_dropped / tx_dropped只要这个数值在增长说明网卡或驱动层已经在丢包了。常见原因Ring Buffer 太小、中断合并策略不当、PCIe 带宽不足。TCP 重传率重传率超过 0.1% 就要警惕。HPC 长流重传的代价极大一秒的重传风暴可能让整个训练任务的同步等待直接翻倍。RDMA 的丢包率RoCEv2 是 lossless 网络通过 PFC 流控保证不丢包一旦 PFC 被触发往往伴随全局拥塞。如果交换机上 PFC 计数在快速上涨你需要马上检查是不是多打一incast流量过猛。链路利用率不均看所有网卡和交换机端口的实时带宽。如果某个链路已经打满其他链路才 20%说明哈希策略没把流量打散。6.2 从“慢任务”到“定位根因”的排查路径有次线上训练任务突然变慢最开始所有人都怀疑是数据加载的问题查了半天无果。后来我用perf看了一下网络软中断的分布发现全部软中断都集中在一个 CPU 核上——因为多网卡的中断没有做亲和性绑定所有网卡中断都打到同一个核这个核成了瓶颈。解决方式很简单用irqbalance或者手动设置smp_affinity把中断分散到不同核上性能立刻恢复。这类问题在 HPC 里非常隐蔽因为监控面板上网络带宽没降、CPU 占用也不高唯一的症状就是“任务变慢”。另一个高频问题交换机 ECMP 的哈希不均。光靠交换机哈希很多时候会出现两个大流量对打elephant flow撞在同一个出口。现象是交换机上有端口利用率 100%另外一堆端口只有 10%。这时候查 RoCE 流量的源端口是随机的还是固定的——如果网卡驱动把 RoCE 的源端口固定了哈希就会失真。NVIDIA 网卡有个自适应哈希adaptive hashing的功能旧固件版本默认不开升级固件并打开后RoCE 流会动态调整源端口ECMP 的分布会明显变好。6.3 一个完整的“多网卡负载均衡失效”排查清单我整理了一个速查表按出现频率排序基本覆盖了绝大多数多网卡负载均衡场景的坑症状可能原因排查方法解决思路聚合带宽上不去只有一张卡在忙哈希粒度过粗单条大流只打一张卡看各网卡流量分布对比是否极度不均应用层多开流调交换机哈希种子升级驱动开启 RoCE 动态源端口绑了 4 张卡但只看到 2 张有流量网卡速率/MTU 不一致bond 或 NCCL 探测放弃部分口检查 ethtool 各口速率、MTU统一配置后重启网络服务或重新初始化 NCCL任务稳定但延迟偶发飙高PFC 死锁或拥塞登录交换机看 PFC 计数看队列 buffer 丢包调整流控门限、减少多打一流量突发、增加动态路由多机训练速度极慢且 CPU 软中断满核中断不均RPS/RFS 未配置cat /proc/interrupts看中断落在哪个核手动设置 smp_affinity 或开启 irqbalanceLVS 转发时通时断后端 ARP 响应冲突arp -a看 VIP 对应 MAC 是否稳定后端 lo 接口配置 VIP 关闭 ARP 响应MoE 训练中部分 Expert 显存爆掉负载均衡损失未生效或权重过低打印各专家 token 计数观察 loss 曲线调高辅助损失权重、使用分组均衡、动态迁移专家6.4 实战心得先看数据再谈优化最后说点带个人色彩的心得。高性能计算负载均衡表面上是个网络问题实际上是个系统性问题。我在调优过程中遇到最多的情况是网络配置看着合理、交换机哈希看着合理、负载均衡算法看着合理但整体性能就是不达标。后来养成一个习惯任何优化动作之前先花 20 分钟把网卡中断分布、链路利用率、丢包计数、集合通信库的拓扑探测日志全看一遍基本能定位到七成的根因。还有个经常被忽略的点集合通信库NCCL/MPI会在启动时做拓扑探测它根据你网络环境的感知自动选择通信路径。如果你改动了网卡配置、bond 模式或者 VLAN一定要重启训练任务重新探测否则它可能还沿用旧的路径规划负载均衡配置再正确也白搭。我在这方面踩过不止一次坑强网卡下跑出来的性能惨不忍睹一查发现 NCCL 还在用老路径。如果你的集群规模不大先从 LVS DR 多网卡 bond 负载均衡损失三个方向入手就够覆盖绝大多数场景了。规模一旦上百卡再考虑在交换机层做动态路由和自适应哈希。这些内容我没法一遍讲透但核心思路是恒定的不均衡问题的本质是系统里木桶效应和热点效应的叠加只有盯着数据做持续调优才能真正把高性能计算的每一分算力榨干。