ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

高性能计算负载均衡:从LVS到MoE专家并行的多层次实践指南

高性能计算负载均衡:从LVS到MoE专家并行的多层次实践指南 1. 高性能计算负载均衡不只是网络设备的事高性能计算负载均衡听起来像是机房交换机、四层代理干的事但真把高性能计算集群跑起来之后你会发现负载均衡这个命题至少横跨三层网络层、调度层、计算层。尤其是最近这些年大模型训练把MoE架构推上台面“负载均衡”又有了一个新的含义——专家并行下的token分配均衡。我最早是从LVS开始接触负载均衡的后来在GPU集群里被各种不均衡问题折磨到怀疑人生再后来看MoE论文里的auxiliary loss才真正意识到高性能计算领域的“均衡”本质上是在跟资源的木桶效应做斗争。这篇文章把我实际用过的、踩过坑的负载均衡方案完整拆一遍覆盖LVS在HPC前端集群的配置、多网卡绑定、作业调度器的资源感知以及现在最热的MoE等开销负载均衡代码实现。不管你是在管超算集群还是在训练百亿参数MoE模型这几层均衡问题大概率都会碰到提前把思路理顺比出了问题再救火舒服得多。2. 高性能计算的负载曲线为什么这么难均衡2.1 和传统Web负载均衡的本质差异传统Web负载均衡解决的是“请求均衡”每个请求通常短小、独立、互相无依赖调度器只要看连接数或CPU利用率做分发就能把压力摊平。但高性能计算里的任务完全是另一回事——一个作业跑起来可能要占满一整台机器的CPU、内存、甚至多张GPU卡作业之间还有依赖关系前面算完后面才能开始。这种场景下你用round robin去分无异于闭着眼睛分蛋糕切出来的块有大有小最后整条流水线等着最大那块。我见过一个典型的案例调度系统按CPU核数分配资源结果某个作业虽然只申请了16核但把节点的内存吃掉了80%导致后续作业排队等内存。这种资源维度之间的不匹配是HPC负载均衡的第一个难点。还有更隐蔽的问题任务在节点间通信时网络拓扑距离不同通信延迟能差出几倍均衡算法如果只看CPU很容易把通信密集型的两个任务放到跨交换机的位置整机性能直接被打折。所以高性能计算场景下负载均衡不是“均分”而是“多维资源的联合优化”。这个认知决定了后面所有手段的选择——单看某几个指标的策略在HPC里几乎都活不过一轮压力测试。2.2 三个层面要分开看我的习惯是把HPC负载均衡分成三个层面。网络层对外提供服务的前端节点登录节点、文件传输节点、API网关需要多台机器分担并发连接这是最传统的负载均衡场景LVS、Nginx、F5都干这活。调度层作业调度器SLURM、PBS、LSF决定一个作业放到哪些节点、哪块GPU上跑。调度器的均衡策略直接决定整个集群的利用率。计算层任务内部的数据/请求分配。在分布式训练框架里数据并行、模型并行、专家并行都需要在细粒度上做均衡尤其是MoE模型里每个样本被路由到不同专家如果路由一旦失衡单个专家成为热点整卡利用率就崩了。三个层面服务于不同的时间尺度网络层是毫秒级分发调度层是分钟到小时级的资源分配计算层是微秒级的张量路由。很多团队把这三个层面混在一起讨论最后方案往往顾此失彼。先把这个分层想清楚再往下聊具体实现。3. 网络层实战LVS在HPC集群中的配置细节3.1 为什么选LVS而不选Nginx在HPC前端集群里LVS是我用得最多也最推荐的四层负载均衡方案。它直接工作在Linux内核的IPVS模块上数据路径短、吞吐高能够在纯内存中做报文转发转发能力远高于Nginx这种七层代理。前端节点的流量特征主要是SSH、文件传输、作业提交指令这些请求量不像互联网那么夸张但单个连接持续时间长、吞吐要求高比如传训练数据集Nginx解析HTTP头部的开销在这种场景完全没必要LVS直接基于IP和端口转发效率要高出不少。LVS有三种工作模式NAT、DR、TUN。在HPC集群里我最推荐DR模式。原因是NAT模式进出流量都要经过调度器调度器会成为吞吐瓶颈DR模式下入方向的流量进调度器出方向的流量直接从后端节点返回客户端相当于调度器只处理一半流量瓶颈大幅缓解。TUN模式虽然可以跨网段调度但需要后端节点支持IP隧道配置复杂我在实际生产环境很少启用。3.2 DR模式配置步骤以两台登录节点、一台LVS调度器为例。VIP是10.10.0.100两个RS节点IP分别是10.10.0.11和10.10.0.12对外提供SSH和作业提交服务。调度器上安装ipvsadm然后绑定VIPip addr add 10.10.0.100/24 dev eth0 ipvsadm -A -t 10.10.0.100:22 -s wrr ipvsadm -A -t 10.10.0.100:10001 -s wrr ipvsadm -a -t 10.10.0.100:22 -r 10.10.0.11:22 -g -w 1 ipvsadm -a -t 10.10.0.100:22 -r 10.10.0.12:22 -g -w 2 ipvsadm -a -t 10.10.0.100:10001 -r 10.10.0.11:10001 -g -w 1 ipvsadm -a -t 10.10.0.100:10001 -r 10.10.0.12:10001 -g -w 2这里-g指DR模式-w是权重。SSH服务我给两台机器权重相同作业提交端口权重按机器性能分配比如新机器权重高一些老机器低一些。调度算法用wrr加权轮询基本够用。如果对连接持久性有要求可以改成sh源地址哈希保证同一来源IP总是落到同一台后端SSH会话不会闪断。RS节点上的配置才是最容易出错的地方VIP必须绑在回环接口lo上而且需要抑制ARP响应不让RS直接回应VIP的广播请求否则调度器就失去意义了。这是我每次配置DR模式最先检查的点。# 在两台RS节点上执行 ip addr add 10.10.0.100/32 dev lo sysctl -w net.ipv4.conf.all.arp_ignore1 sysctl -w net.ipv4.conf.all.arp_announce2 sysctl -w net.ipv4.conf.lo.arp_ignore1 sysctl -w net.ipv4.conf.lo.arp_announce2arp_ignore1表示只回应本接口IP的ARP请求arp_announce2表示尽量使用目标IP来回应ARP这两个参数配合起来RS就不会跟调度器抢VIP了。很多刚上手LVS的人漏了这一部客户端流量会绕过调度器直接打到其中某台RS导致均衡策略完全失效排查起来还特别隐蔽。3.3 多网卡场景的均衡经验很多HPC节点有两张或更多网卡。Windows服务器上做NIC Teaming网卡绑定通常有两种模式静态聚合和LACP动态聚合。Linux端bond驱动的mode4对应LACP需要交换机支持并开启LACP协商。我做跨节点存储互访时踩过一个坑用了mode0round-robin绑双网卡结果同一个TCP连接的两端MAC地址来回切换导致TCP校验和出错传输速度反而掉到单卡的一半。后来改成mode4LACP并确认交换机端口配置了同样的链路聚合组速度才正常。多网卡绑定的核心经验是别追求所有报文轮询转发要让一个连接稳定走同一条路径否则TCP层的重传和乱序会吃掉所有带宽收益。4. 计算层实战MoE等开销负载均衡的代码实现4.1 MoE为什么天然容易负载不均MoEMixture of Experts架构把一个大模型拆成多个专家子网络每次推理只有一部分专家被激活。路由器给每个token打分选出Top-K个专家执行计算。这个设计的初衷是降低计算量但它默认了一个前提token在各个专家上的分布是均匀的。现实恰恰相反不同领域的token集中倾向完全不同如果某类token高频出现它们路由到的专家就是热点而其他专家空转最终效果是热点专家的计算占满其他卡GPU利用率只有个位数。这个问题在大规模训练里会被无限放大。专家并行时每个专家通常放在不同的GPU上不均衡意味着跨卡通信频率暴涨而且每一轮迭代的耗时取决于最忙的那个专家。更崩溃的是负载不均衡还会加速训练坍缩——热点专家学到的东西越来越多路由概率越来越高非热点专家梯度不足逐渐变成死神经元。这个正反馈一旦形成单靠调学习率救不回来。4.2 辅助负载均衡损失最经典的做法最简单也是论文里最常用的思路是加一个辅助损失惩罚路由器对专家的使用不均匀。拿大家最熟悉的Switch Transformer来说每个token被路由到专家前会计算一个softmax得分得分最高的专家被选中。辅助损失的计算方式是统计每个专家被选中的token比例再与均匀分布求偏差。代码写出来很直观import torch import torch.nn.functional as F def load_balance_loss(router_logits, num_experts): # router_logits: [batch * seq_len, num_experts] probs F.softmax(router_logits, dim-1) # 每个专家被选中的token总数 tokens_per_expert torch.zeros(num_experts, deviceprobs.device) _, selected_expert probs.max(dim-1) tokens_per_expert.scatter_add_( 0, selected_expert, torch.ones_like(selected_expert, dtypeprobs.dtype) ) total_tokens selected_expert.numel() fraction_per_expert tokens_per_expert / total_tokens # 辅助损失每个专家被选中的比例与均匀分布的偏差 loss num_experts * torch.sum(fraction_per_expert * fraction_per_expert) return loss这个loss的本质是让每个专家被选中的比例趋近于1/num_experts。系数num_experts是为了让loss值不随专家数增加而稀释。实际训练中辅助损失的权重系数alpha通常从0.01开始调我试过更大的值模型会变得“过于均衡”——路由完全均匀但专家专业化的优势也被磨掉了。这个度需要根据数据集和模型规模反复试没有一个通用最优值。4.3 无辅助损失的均衡思路辅助损失虽然有效但它本质上是在干扰路由器的自然学习。近两年无辅助损失auxiliary-loss-free的方法更受关注。核心思路是不动路由分数而是给每个专家设一个可学习的偏置项根据当前负载调整路由偏好。具体机制可以理解为负载高的专家偏置项变小让路由器的得分被压低负载低的专家偏置项变大相当于给了一个临时加分。这样路由器的原始学习方向不被破坏只是在不同训练阶段被偏置项修正。这个思路在DeepSeek-V3的实现里做得比较细。每个token的最终路由分数是router_score bias训练过程中统计每个专家接收的token量如果超过平均水平就增大该专家的bias惩罚低于就削弱惩罚。实现时还要在启动阶段强制要求每个专家至少接收一定数量的token避免训练前期就出现某个专家饿死。无辅助损失的调参点从“权重系数”变成“bias更新步长”和“惩罚强度上限”这两个参数也需要手动调。但相比辅助损失它更不容易出现“均衡假象”——辅助损失只在batch级别的统计上做均衡而偏置项的反馈是连续的对局部突发流量的适应性更好。4.4 专家并行下的通信均衡计算负载均衡之外MoE训练还要考虑通信负载均衡。路由结果是稀疏的token被分到不同专家的GPU上每个GPU需要把自己的token发给目标专家所在的GPU这就形成了all-to-all通信。通信量同样不均衡——有的GPU发送的token多有的少。我在大规模训练中曾经只做计算均衡忽略了通信结果每轮迭代的耗时没有明显下降最后才发现是通信热点卡住了步骤。通信均衡的实用手法是“batch切分多级路由”。把一个大batch切成多个小batch每个小batch的路由结果不完全相同错峰发送可以减少瞬时通信压力。另外在ESExpert Selection阶段尽量做局部均衡先看当前GPU上有没有足够空闲的专家槽位有就尽可能在本卡消化token减少跨卡发送。这种“就近优先”的策略不一定能保证全局最优但通信开销下降得最明显。5. 调度层均衡作业调度与动态负载感知5.1 作业调度器的均衡策略怎么选HPC集群的作业调度是另一个层面的负载均衡。SLURM是我最常用的调度器它的默认调度策略是FIFO先来先服务。但HPC的作业资源需求差异极大——有的作业要整节点独占有的只要几个核FIFO会导致前面排一个大作业后面一堆小作业全部卡住。所以生产集群里通常开backfill回填功能。回填的思路是在大作业排队等待时调度器检查当前空闲资源如果够跑后续的小作业且不会延误大作业的最早开始时间就先跑小作业。这个策略本质上就是在做时间维度的负载均衡把空闲资源全部填满。开回填很简单SLURM里设置SchedulerTypesched/backfill但回填的判断逻辑有点讲究。它要求所有作业都能估计运行时间如果你的作业不写--time参数默认用的可能是无穷大回填就永远不会触发。所以我一般会在集群规范里强制要求作业必须声明预计运行时间否则排队优先级降级。幼儿园上班时间没写老师就没法安排活动——一个道理。5.2 多维资源联合感知的均衡方案节点调度不能只看CPU高性能计算现在至少要看CPU、内存、GPU、网络带宽四个维度。SLURM支持GRESGeneric RESource来管理GPU可以做到GPU级别的分配。比如# 提交一个需要2张GPU的交互式作业 srun --gresgpu:2 --time01:00:00 --pty bash但如果只看GPU数量不看显存和GPU型号两台不同型号的节点会被当成等价资源结果作业跑到老型号的卡上直接OOM或者速度慢到不可接受。我的做法是在节点配置里显式声明每个节点的GPU型号和显存大小让调度器按节点分组调度而不是把全网GPU拉平成一个资源池。网络拓扑感知在通信密集作业里更关键。SLURM的拓扑插件可以按交换机层级感知节点位置把需要多节点通信的作业尽量安排在同一个交换机下。连续多次实测下来网络拓扑感知调度对分布式训练作业的提速能达到15%到30%尤其在跨节点GPU通信的场景效果立竿见影。5.3 动态负载感知的配额调整集群不是静态的有的项目组白天活跃、晚上闲置有的作业夜间跑数据备份。单纯靠调度器算资源分配很难跟上这种动态变化。我最近在实践中给集群加了一个“分组配额动态调节”的机制每个项目组有基础配额但可以借用其他组空闲的配额借用时长超过阈值后进行回收。这个机制的实现不复杂核心逻辑就是周期性地检查各组分时利用率利用率为0的组释放配额到公共池利用率超过90%的组可以额外申请公共池资源。看起来像是银行业务里“同业拆借”的概念——白天都在贷晚上资金有空闲就拆借出去收利息。这套机制上线后整个集群的GPU平均利用率从62%提高到81%没有再出现“有人排队有人闲置”的反差局面。6. 两类典型问题的排查实录6.1 LVS均衡失效的排查过程有一次前端登录节点突然卡顿我在LVS调度器上看IPVS统计发现所有SSH连接都落在了同一台RS上。先检查ipvsadm -L -n连接记录确实都挂在其中一个RS另一个RS的ActiveConn是0。第一步怀疑RS的VIP配置丢失检查ip addr show loVIP还在。第二步检查ARP抑制发现net.ipv4.conf.all.arp_ignore值被改成0了——应该是某次系统重启后sysctl配置没持久化生效。客户端ARP缓存里VIP对应的MAC地址变成了RS的MAC流量就不经过调度器了。这类问题最好的预防措施是把sysctl配置写进/etc/sysctl.d/下的独立文件并且在集群自动化部署脚本里加一个启动自检检查ip addr里的VIP和arp_ignore的值不匹配就直接发告警。人工排查这种问题虽然不复杂但在几百台节点的集群里定位到“是哪台RS抢了VIP”这件事本身就很费时间。6.2 MoE训练中辅助损失不降的坑MoE训练时对辅助损失的变化要格外敏感。正常情况下辅助损失应该缓慢下降如果发现辅助损失不降反升先别急着调学习率——大概率是某个专家进入了“死循环”状态路由器几乎不再给它分配token它收不到梯度bias还在持续增大表现为损失剧烈震荡。我排查过这个问题最后发现是训练初期学习率过高导致路由器参数突跳把一部分专家直接“锁死”了。解决办法有两个一是降低前几个epoch的router学习率让路由分布平滑建立二是在辅助损失里加入均值过滤对长时间处于过载或饥饿状态的专家进行显式干预强制重新分配batch。还有一个容易忽略的细节辅助损失统计的token数量要包含所有token而不是只看路由得分最大的那部分。有些框架为了加速计算只统计得分最高的token忽略了分数偏低的token结果损失值看起来很低真实均衡性并不好。我的经验是宁可每步多花一点点计算也要把全量token纳入统计。6.3 常见问题速查表现象可能原因排查顺序LVS下流量全部打向一台RSRS的ARP抑制失效检查sysctl配置、VIP绑定、抓包确认ARP回应多网卡绑定后传输速度反降LACP协商失败或mode不匹配检查交换机聚合组状态、bonding mode链路状态作业排队但节点显示空闲内存/GPU等维度资源未释放scontrol show node查看空闲资源明细MoE训练辅助损失不降专家死锁或路由学习率过高查专家token统计分布、重启训练前降低router lr分布式训练跨节点慢作业跨交换机通信检查拓扑感知调度配置、改用节点分组调度显存OOM但与单张卡负载无关专家不均衡导致热点卡显存爆满检查token per expert热力图、增加容量因子7. 最后聊几点实际操作的体会这几个层面的均衡策略我陆续做过很多轮最大的体会是不要追求“理论最优均衡”高性能计算场景的负载均衡本质是一个多目标折中问题。网络层做了严格均衡可能牺牲调度层的节点亲和性调度层做细粒度切分可能增加计算层的通信开销。每加一层优化都要评估它对上一层的影响。如果让我给一个落地优先级我会先保证调度层的多维资源感知这是集群利用率的地基再在网络层做好前端高可用最后再花精力啃计算层MoE的精细均衡——因为计算层的优化收益最大但难度也最高驾驭不了的时候先别大规模铺开。你可以在小规模集群上跑一个月记录辅助损失曲线和专家利用率热力图再决定要不要把方案推广到所有训练任务。做负载均衡这么多年我越来越认同一点好的均衡策略不是让所有资源占用率变成同一水平线而是让整个系统的短板在每一时刻都最小化。那根木桶最短的板才是你该花力气补的地方。
返回列表