ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI基础设施解密:从GPU集群调度到分布式训练与故障排查

AI基础设施解密:从GPU集群调度到分布式训练与故障排查

我在一线做AI基础设施(AI-Infra)这个方向,已经有一段时间。这个岗位通常不直接产出模型效果,但算法团队的每一次实验、每一次上线,都踩在我们搭好的地基上。简单说,AI-Infra负责把GPU算力、存储、网络、数据流和推理服务这些底层能力组织起来,让大模型能训得起来、跑得稳、上得线。这套东西在业内的叫法很多:有人叫AI平台,有人叫MLOps,有人叫算力基础设施,但核心诉求都一样——让算法同学把精力放在模型上,而不是跟环境、资源、故障作斗争。

第一章我会先把这个方向的全貌讲清楚,包括它到底管什么事、日常难点在哪里、会碰到的第一堵墙是什么,以及作为新人应该从哪里入手。这一篇适合刚接触分布式训练、想转AI基础设施方向的同学,也适合已经在做平台或运维、想对标一下自己团队实践的人。内容不绕弯,全是自己踩过坑之后的总结。

1. 先搞明白:AI-Infra不是装显卡,而是造发射台

1.1 算法团队眼里的AI-Infra

在我的经验里,算法同学对AI-Infra的期待非常朴素:提交一个训练任务,它能跑;跑崩了,能恢复;要多少卡,能拿到。看起来简单,做到很难。因为这里牵扯到的不只是一台机器,而是几十上百台机器组成的集群,每一台都可能因为显卡、驱动、网络、磁盘的问题变成不稳定因素。

我记得刚转岗到AI-Infra时,一位算法同事跟我说了一句让我印象很深的话:“我不关心集群怎么调度、存储怎么优化,我只要我把代码传上去,点一下运行,能跑就行。”这句话听上去对平台组要求很低,但细想其实是最高的要求——意思是所有复杂性都由你兜底,他希望面对的是一个黑盒,里面必须是稳定可靠的。我后来才明白,做AI-Infra的成就感不是来自你用了多炫的技术,而是来自算法团队真的可以做到“无感”。

这里还要澄清一件事:AI-Infra绝不是传统运维的换皮。传统运维关注的是服务的可用性、容量、变更流程;AI-Infra虽然有很多运维动作,但核心是解决分布式计算带来的新问题。比如多卡通信、梯度同步、任务再调度、显存管理、GPU故障检测,这些在传统后端里根本没有对应物。所以别带着“装环境、看监控”的心态入行,那样很快会觉得没意思,也会错过真正的挑战。

1.2 拆开来看:AI-Infra实际负责的五块阵地

我习惯把AI-Infra的工作按五块拆解:算力管理、数据管线、训练支撑、推理服务、可观测性。这五块各自独立,又互相咬合。

  • 算力管理:包含GPU集群的节点管理、任务调度、配额与优先级、队列管理、资源容量规划。核心目标是把GPU利用率拉上去,同时保证每个任务能拿到预期的资源。
  • 数据管线:细到数据集的上传、格式转换、清洗,大到分布式存储选型、缓存设计、数据预取。很多团队把数据管线划给数据组,但在实际协作里离不开Infra的深度介入。
  • 训练支撑:分布式训练框架的选型与封装、通信库调优、断点续训、故障自动恢复。这一块直接决定大模型训练任务能不能长期稳定跑完。
  • 推理服务:模型部署、服务化封装、动态批处理、显存规划、弹性扩缩容、版本灰度。这是模型上线后的战场,跟训练完全不同。
  • 观测与效率:GPU监控、任务耗时分析、成本分析、告警策略。没有可观测性,前四块做得再好也像盲人开车。

这五块不是平均用力。在小团队里,一个人可能全包;在大团队里,每个方向专职一人甚至一个组。第一章我会重点讲1、2、3这三块,因为它们是最基础、新人最容易先接触到的;第5块会穿插在故障排查里讲,第4块放到后面的章节单独展开。

2. 分布式训练:从单机到千卡的第一道坎

2.1 第一次在集群上提交任务:本地能跑,上去就崩

说说我自己的名场面。第一次在GPU集群上提交训练任务,是在转岗第二周。我在本地用单卡调试好的脚本,打包进容器,提交到集群,结果四个小时都没跑起来。登录到计算节点一看,日志停在了数据加载阶段——DataLoader的worker进程全被杀了。排查了很久,发现是容器里的共享内存默认只有64MB,而我的DataLoader开了8个worker,每个worker预取一批数据,直接把共享内存打爆了。加了一行配置把这个容量调大,任务才跑起来。

这种问题在当时的我看来很神奇:本地完全不会遇到的场景,到集群上就变成了拦路虎。后来陆续又踩了镜像缺库、工作目录没权限、进程间通信用的端口被防火墙挡了之类的坑。这些都是环境层面的,适应期熬过去就能解决。真正让我头皮发麻的是第一次上8卡。单卡跑得好好的,8卡一启动,训练到第30步直接卡住,几分钟后整批任务被判定超时挂掉,日志里全是通信报错,还有一个叫timeout的词反复出现。

那个时候我整个人是懵的。后来才逐步理解,多卡训练要解决的不只是“把代码跑在更多卡上”,而是一整套通信和同步机制。这也是AI-Infra和普通分布式后端最大的差异点:你不光要管任务怎么调度,还要管任务内部的通信效率、同步策略和故障恢复。

2.2 通信和同步这两个词,是所有难点的源头

为了把这一章讲透,我必须把数据并行这个最基础的分布式训练方式讲明白,因为它决定了你后面理解一切问题的框架。

数据并行的基本思路是:每个GPU上放一份完整模型,训练数据切成多份分给各GPU,大家各自算各自的batch,最后把梯度合并,再按合并后的梯度更新所有副本。这样等效于把batch size乘以GPU数量。但是“合并梯度”这步不是免费的:每个GPU算出自己的梯度后,要通过网络把所有梯度的平均值算出来,这个操作在分布式计算里叫allreduce。

如果你把梯度想成一份清单,allreduce就是让所有人把自己清单上的数字求和后,再把结果广播给大家。这个操作在多机多卡场景下非常贵。假设模型有70亿参数,以常见精度表示,一次allreduce要传几十GB数据。网络稍慢,通信时间就可能比计算时间还长。所以工程上要做两件事:一是让通信和计算重叠,在反向传播还没结束时就开始传梯度;二是选对通信拓扑,比如在单机内走更快的交换链路,在多机间走专用网络,同时避免频繁跨交换机通信。

这里有个新手容易误解的点:不是GPU数量越多越快。8卡加速比可能只有7倍,64卡可能只有40倍,因为通信开销、同步等待、慢节点都在拖后腿。到了一定规模,瓶颈不再是单卡算力,而是通信链路和同步策略。这也是为什么AI-Infra工程师要花大量精力研究网络拓扑、队列调度、通信库参数——目标都是减少通信开销,让加速比更接近线性。

3. 调度、存储与数据:日常操心最多的三件事

3.1 任务调度:排队、配额、优先级,还要防饿死

集群资源是共享的。一个小团队可能十几个人共用几十张卡,需求经常超过供给。调度的第一个问题就是排队:谁的任务先跑?这背后是配额和优先级的设定。比如A组有40卡配额,但此刻只用10卡;B组配额只有20卡,但一下子要30卡。调度器不能简单“谁来了谁排队”,还得兼顾配额利用率。

另一个训练特有的问题是gang scheduling。普通分布式服务(比如一堆Web容器)可以一个个启动,先启动的先对外服务。训练任务不行,因为NCCL初始化时需要所有rank都在线,只起来5个,另外3个卡在初始化,这5个也会一直等,等于白白占着资源。所以调度器要有“整组调度”的能力:要么8个worker一起启动,要么就不启动,避免资源死等现象。如果你是自己搭调度,这个小细节很容易漏,漏了之后集群利用率会莫名变低。

优先级抢占也是躲不开的话题。一个需要长期跑的调参任务,可能被高优先级的新实验抢占。这个时候任务要能被安全打断,靠的是checkpoint。所以我们一般会要求所有训练脚本必须支持定期保存状态,并且让调度器在抢占前感知checkpoint的位置,任务恢复后能接着跑。

提示:checkpoint不能只存在本地,要同步到持久存储。否则节点被回收,权重就没了。我见过有人训练了十几天,一问权重没同步,直接从零开始,整个人当场崩溃。

放置策略要做的功课更多。比如8卡任务,优先放在同一台8卡机器上,放不下才考虑两台4卡,再不行才跨机。因为跨机通信要走网络,比卡间通信慢得多。还有机器健康状态,调度时不能把任务分到有坏卡、降频或网络异常的节点上。这需要监控系统持续上报节点健康信息,调度器做过滤。没有这一步,坏节点会把任务拖死。

3.2 数据读得太慢是原罪:存储和加载的优化思路

存储问题在训练场景里被低估得很严重。拿CV场景举例:一个数据集几万张图片,每张只有几十KB到几百KB,加起来也就几十GB,听着不大,但真要是小文件随机读,能慢到让你怀疑人生。因为每次读取一个文件,都要付出一次文件系统元数据查询和网络往返的代价,吞吐根本跑不满网络带宽。

工程上的解法通常是多级缓存:把数据集转换成大文件格式,减少文件数量;或者建本地SSD、内存缓存,把热数据放在计算节点里;再配合对象存储或并行文件系统作为持久层。这样训练进程读的是本地缓存,远端只负责同步。另一个思路是把数据提前预热到所有计算节点的本地磁盘,用空间换时间,虽然占用存储,但能让训练环节彻底摆脱网络IO的抖动。

数据加载还要考虑CPU预处理。很多同学只盯着GPU利用率,完全忽略了CPU侧也在处理数据解码、随机增强、打乱等操作。如果DataLoader的worker数量设得太少,GPU会频繁等数据,利用率曲线就像锯齿一样上上下下。我习惯先把GPU利用率拉满,再看CPU有没有冲高,如果两者都高,就想办法减少预处理开销或者增大worker数量,而不是无脑加GPU。

checkpoint也是存储环节的老话题。大模型权重动辄几十GB,如果同步写,保存一次整个训练都要停。主流做法是异步保存到本地,再异步上传到持久存储,避免训练主进程被阻塞。还有更进一步的思路,比如把checkpoint分片保存到多节点,或者用内存快照做shadow复制,但只要能把训练停顿控制在几秒内,对小规模团队就已经够用了。不要为了追求炫技方案,把系统搞得太复杂。

4. 故障排查实录:训练跑崩之后的那些现场

4.1 NCCL超时和GPU掉卡:集群中最常见的两种“心脏病”

训练到一半,日志里出现大片报错,包含timeout或“got fatal error”这样的关键词,最后整个任务死掉。这是我在一线遇到最多的故障,没有之一。遇到这种事,新人最喜欢去调通信库参数,比如把超时时间调大。说实话,我早期也干过,结果只是拖延了问题暴露的时间,并没有治本。

治本要按顺序排查。第一件事是看硬件。登录到出问题的节点,查看系统内核日志,看有没有GPU相关的错误记录。如果能看到形如“xid”的错误码,比如79、43、63这类,基本可以断定是显卡硬件异常或者驱动问题。xid 79表示GPU已经无法正常工作,可能过热、供电异常或显存故障。这种情况下建议直接下线节点、更换显卡,不要试图在软件里解决。

第二件事是看网络。NCCL超时的一个常见诱因是高速网络链路质量下降。此时通信库在网络上等待,迟迟收不到数据,最终超时。可以在节点间跑一轮带宽测试,看链路是否正常。如果端口错误计数在上涨,说明物理链路有问题,该换线换线,该换网卡换网卡。

第三件事才是看通信和调度配置。比如任务是否被调度到了跨交换机的不利拓扑,或者通信库是否用错了网络接口。我的实际经验是:大面积NCCL超时,九成是硬件或链路先出了问题,通信库只是背锅侠。所以先把硬件和网络排干净,再去怀疑软件,否则方向全反。

4.2 慢节点和显存泄漏:排查过程比结果更有料

慢节点的现象很隐蔽:任务没有崩,但训练越来越慢。你观察每步耗时,会发现有个节点的步时间明显高于平均值,其他节点一直在等它。这就像团队合作里有个进度特别慢的队友,整个项目的节奏被他拉下来了,但他没有缺席,你还不好意思说他掉线。

排查慢节点,最有效的工具是训练日志里的步时间统计。每个rank都打印自己每步的耗时,端到端汇总后看分布,哪个rank时间异常,就顺藤摸瓜。常见的根本原因包括:CPU降频、散热不良导致功率被限制、GPU卡被邻居任务挤占、磁盘IO卡顿。慢节点如果不处理,影响的不是一个任务,而是占用资源的整整一个批次任务群。所以我会把步时间方差做成告警,超过阈值就自动重启任务或剔除节点。

另一个常见问题是显存泄漏。推理服务跑几天后,显存占用慢慢涨,最后服务被系统杀掉。先说一个容易误判的点:我们用的深度学习框架通常有显存分配器缓存策略,它不会把释放的显存立刻退回给驱动。所以你看到显存占用居高不下,不一定是真泄漏,可能是空闲缓存。要看趋势:如果长时间运行后占用稳定在一个值附近,大概率是缓存;如果持续线性增长直到OOM,才是真泄漏。排查时先让进程跑一小段时间,画出显存曲线,再结合是否每步都有新增缓存对象来判断。

真泄漏通常出现在自定义的数据处理或者推理逻辑里:有人把每个请求产生的中间结果保存在内存列表里,有人每次请求都创建一个永不释放的上下文,有人把数据集的缓存对象越堆越大。还有的框架在动态shape场景下会出现显存碎片化,导致新的大块显存申请失败。对碎片化问题,临时解法是定期重启进程,正经解法是在推理引擎里启用显存整理或使用分页机制。

5. 推理服务:模型炼成之后的另一个战场

5.1 训练求吞吐,推理求延迟:两种完全不同的工程逻辑

训练阶段,你要的是单位时间能处理多少个样本,所以batch size可以很大,即使单个样本慢一点也没关系。推理阶段,用户端到端的等待时间非常关键,你既要响应快,又要在QPS上来的时候不排队太久。这两个目标其实是冲突的:batch越大,吞吐越高,但第一个请求的等待时间也会增加。

工程上解决这个冲突的常见办法是动态批处理:不是等batch凑齐再统一算,而是允许新请求随时加入正在运行的batch中,插队计算,已经产生的结果直接返回给对应请求。这样既保持高吞吐,又把单请求延迟压到可接受范围。这个思路在训练里不适用,但在推理里是标配。

另外推理还有token生成的特点。大模型生成是逐个token来的,前一个token的结果决定下一个token。所以引擎会分为预填和生成两个阶段。预填阶段要一次性处理整个输入序列,吃算力;生成阶段逐token产出结果,吃带宽和缓存。工程上要针对两个阶段分开调优:比如预填用大的batch,生成阶段则优先保证缓存命中率。如果你用一套参数调两个阶段,往往顾此失彼。

5.2 推理部署的核心细节:显存规划、动态批处理和扩缩容

模型部署最基础的规划是显存预算。一个7B模型用常见精度加载权重,大约需要十几个GB。如果跑在线推理,还要给KV缓存预留空间,因为每生成一个token,历史token的KV状态都要保存在显存里。并发越高,KV缓存占的显存越大。如果不预留,并发一上来直接OOM。我习惯把显存划分成三块:权重区、KV缓存区、计算临时区,权重区固定,缓存区按并发动态伸缩,临时区留足算法编译和中间结果的余量。

有个粗略的估算公式可以分享:模型权重占用的显存约等于参数量乘以每个参数的字节数。KV缓存则按层数、注意力头数、序列长度和并发数相乘来估算。实际部署时我会在这个估算结果上再预留20%到30%的余量,避免动态shape波动导致OOM。很多上线事故都是因为把显存算得太满,稍微来点长文本流量就崩了。

冷启动也是个容易被忽视的问题。模型加载需要时间,如果QPS突然翻倍,扩出来的副本还没加载完,流量已经涌进来了。我的做法是准备一个预热副本池,平时保持几个副本处于待命状态,流量高峰直接切给它们。同时把模型权重放到高速共享存储或目标节点的本地缓存中,减少加载时间。版本灰度也不可少,新版本模型先放少量副本接流量,指标没问题再全量,出了事能快速回滚。

注意:推理服务的GPU利用率和延迟要分开看。利用率高不一定是好事,但P99延迟变差用户立刻会骂。监控上要同时看GPU利用率、请求成功率、P50、P99延迟和每卡并发数。调优的目标不是把GPU榨到100%,而是在保证SLA的前提下尽量提高吞吐。这是很多刚接触推理优化的同学经常搞反的地方。

6. 新人入坑指南:第一章最值得记住的几点

6.1 别急着调模型,先把监控指标和排查工具吃透

很多想转AI-Infra的人,第一时间去学深度学习框架、学分布式通信,这方向没错,但顺序可以优化。我建议先学会观察,再学会动手。观察的意思是,你要能看懂一套标准的GPU监控面板:利用率、显存、温度、功耗、网络吞吐、磁盘IO。训练脚本里主动输出step time、loss、内存占用。遇到任何问题,先去监控里找证据,而不是凭感觉猜。

我对新人的要求通常是:能自己用命令行在GPU节点上查显卡状态,能看懂里面每一列的含义;能拉出进程的GPU使用率和显存占用;能在训练日志里定位到第一个报错的rank。这些基本功看着简单,但关键时刻能省几个小时。我自己带过的同学里,凡是先把这些打牢的,后面上手分布式训练都很快。

学习路径上,建议按单卡到多卡再到集群的顺序走。先在本机用单卡把一个模型训练跑通,搞清楚训练循环和显存占用;然后尝试用数据并行跑双卡,观察通信耗时;最后才接触容器化、调度和集群管理。一下子从集群开始学,很容易被各种环境问题淹没,反而不利于建立直觉。尤其是NCCL报错这类问题,如果你连单卡什么样都没概念,就更难判断是不是通信导致的。

6.2 我的个人体会:AI-Infra最值钱的能力是判断力

做AI-Infra一段时间之后,我最大的感受是,这个岗位最稀缺的能力不是会配环境、会写调度器、会调通信参数,而是判断力:遇到一个性能问题,你能否快速判断瓶颈在哪一端——计算、存储、网络还是内存?

举个小例子:训练吞吐上不去,新人第一反应是换更强的GPU,但老手会先看一眼GPU利用率。如果利用率只有30%,说明计算根本没吃饱,换再贵的卡也没用,问题可能在数据加载、锁、通信等待;如果利用率已经95%以上,那才轮到考虑升级硬件或做算子优化。先去观察,再提假设,然后用一个最小实验去验证,最后把解法沉淀成工具或配置。这套方法论听起来朴素,但确实是AI-Infra看起来像玄学、实际可工程化的核心原因。

还有一个体会:做AI-Infra要有很强的服务意识,但又不能完全被动响应。主动性来自对系统数据的敏感,比如你看到某类任务平均排队时间在涨,就应该提前去排查是不是配额分配出了问题;你看到某个节点的GPU温度一直在高位,就该早点把它从资源池里摘除,不要等它掉卡再抢修。把故障消灭在发生之前,是这份工作和传统运维最不一样的地方。

第一章就写到这里。我必须承认,这个方向的内容量远比一篇能装下的要多——调度算法、存储选型、通信调优、推理引擎,每一块拆开都足够写好几章。我在写这第一篇的时候,特意把节奏控制在了“入门框架加核心难点”的层面,目的就是让第一次接触AI-Infra的人有一张清晰的地图,而不是一头扎进细节里出不来。

最后分享一点个人经验:不管你现在是算法工程想往底层走,还是后端工程想切到AI领域,AI-Infra都值得当作一个长期方向来投入,因为随着模型规模增长,这部分的复杂度只会更高,需求只会更旺。但入行之前请做好心理准备——这是个需要耐心跟意外打交道的方向,可每解决一个系统性问题的成就感,也确实不是普通业务开发能给到的。下一篇我打算拆一下调度器内部设计和GPU分配策略,顺带讲讲怎么搭建一套能救命的训练监控体系。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进