ARTICLE DETAIL

资讯详情

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

大规模异构 GPU 资源碎片治理:基于拓扑亲和与 Binpack 算法的调度优化

大规模异构 GPU 资源碎片治理:基于拓扑亲和与 Binpack 算法的调度优化 大规模异构 GPU 资源碎片治理基于拓扑亲和与 Binpack 算法的调度优化在管理上百节点、包含 A100、H800、L40 及国产异构加速卡的混合算力集群时最令人头疼的问题往往不是算力绝对总量的不足而是“显存碎片化”与“拓扑不匹配”。经常出现的情况是集群总空闲显存高达上千 GB但当一个需要 8 卡 NVLink 全互联的 70B 大模型任务提交上来时却没有任何一台物理机能够满足连续的 8 卡拓扑需求。原因在于之前的零散小任务随意分布在各个节点上把原本连续的 NUMA / NVLink 节点打得支离破碎。为了解决异构算力碎片化问题我们自研并落地了基于 Kube-Scheduler 框架的拓扑感知装箱Binpack调度插件。graph TD subgraph 待调度任务队列 TaskA[70B分布式推理 申请8卡互联] TaskB[LoRA微调 申请2卡] TaskC[Embedding 单卡切片] end Scheduler[拓扑感知调度器] TaskA -- Scheduler TaskB -- Scheduler TaskC -- Scheduler subgraph 节点拓扑与打分决策 Scheduler -- Filter[拓扑过滤器: 校验NVLink/PCIe Switch连通性] Filter -- Score[Binpack 打分器: 优先填充已有碎片节点] Score -- BestFit[选定最佳节点: 保留完整整机给大任务] end BestFit -- Node1[节点 1: 已满载] BestFit -- Node2[节点 2: 填充轻量任务] BestFit -- Node3[节点 3: 预留完整 8 卡拓扑]1. 物理拓扑感知从单卡数量到通信带宽矩阵在传统的 Kubernetes 默认调度器中GPU 只是一个无差别的标量资源如nvidia.com/gpu: 2。调度器只要看到节点剩余 GPU 数量大于等于 2就会把 Pod 调度过去。但在真实硬件中卡 0 和卡 1 可能通过高速 NVLink 直连带宽达到 600GB/s而卡 0 和卡 7 可能跨越了 CPU NUMA 节点通信只能走高延迟的 PCIe 总线带宽骤降至 32GB/s。如果让分布式张量并行Tensor Parallel跨越 NUMA 节点运行模型推理延迟会直接增加 3 倍以上。我们在调度插件中维护了节点硬件的物理拓扑掩码Topology Maskpackage scheduler import ( context fmt v1 k8s.io/api/core/v1 k8s.io/kubernetes/pkg/scheduler/framework ) type GPUTopologyPlugin struct { handle framework.Handle } const PluginName GPUTopologyBinpack func (p *GPUTopologyPlugin) Name() string { return PluginName } // Filter 阶段不仅检查卡数更校验是否存在满足通信带宽的连续卡组合 func (p *GPUTopologyPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status { reqGPUs : getRequestedGPUs(pod) if reqGPUs 1 { return framework.NewStatus(framework.Success) } node : nodeInfo.Node() topologyData, exists : node.Annotations[topology.gpu.internal/matrix] if !exists { return framework.NewStatus(framework.Unschedulable, 节点缺少 GPU 拓扑元数据) } if !hasContiguousTopology(topologyData, reqGPUs) { return framework.NewStatus(framework.Unschedulable, fmt.Sprintf(节点无满足带宽的 %d 卡连续拓扑, reqGPUs)) } return framework.NewStatus(framework.Success) } // Score 阶段采用 Binpack 策略倾向于将碎任务挤入同一个 NUMA 节点尽量空出整机 func (p *GPUTopologyPlugin) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) { nodeInfo, err : p.handle.SnapshotSharedLister().NodeInfos().Get(nodeName) if err ! nil { return 0, framework.NewStatus(framework.Error, err.Error()) } usedGPUs, totalGPUs : getNodeGPUUsage(nodeInfo) if totalGPUs 0 { return 0, framework.NewStatus(framework.Success) } // 计算装箱分数已有使用率越高打分越高加速填满单机 utilization : float64(usedGPUs) / float64(totalGPUs) score : int64(utilization * 100) return score, framework.NewStatus(framework.Success) }2. 碎片主动整理与低优先级任务抢占静态调度只能保证任务入场时的合理性但随着不同任务生命周期的异步结束集群不可避免地会再次产生碎片。为了动态消除碎片平台引入了周期性碎片整理与智能重排控制器apiVersion: config.k8s.io/v1beta3 kind: KubeSchedulerConfiguration leaderElection: leaderElect: true profiles: - schedulerName: gpu-topology-scheduler plugins: filter: enabled: - name: GPUTopologyBinpack score: enabled: - name: GPUTopologyBinpack weight: 100 reserve: enabled: - name: GPUTopologyBinpack控制器每隔 15 分钟扫描一次全集群。当发现某台 8 卡机器上只运行了一个单卡低优先级批处理任务而其他节点有能力容纳该任务时控制器会自动向该任务发送安全迁移信号优雅终止并在另一节点重建从而将整台 8 卡机器释放出来为后续的高优先级大模型任务保留黄金算力槽位。3. 生产调优成效与避坑总结通过拓扑感知与装箱调度的结合我们在生产环境中取得了显著的收益集群整卡可用率提升8 卡连通任务的调度成功率从 61% 提升至 94%消除了“有空闲卡却调不动大任务”的窘境。算力装箱率显著提高在混合运行微调、轻量推理与批量计算的集群中整体算力碎片率下降了近 40%。避免过早调度锁定在 Score 阶段必须同时结合 CPU、内存与 RDMA 网络的分配情况防止因 GPU 绑定成功但宿主机网络带宽打满造成的性能次生灾害。
返回列表