做AI平台的人早晚会碰上一个尴尬局面:训练任务把几块显卡吃得干干净净,推理服务的延迟却在同一台机器上飙到不可接受;或者反过来,线上推理还算平稳,一启动离线评测任务,关键服务直接被打挂。“隔离”两个字听起来简单,但真去落地,既要分算力又要分显存,还要管存储带宽和网络通道,麻烦得很。这篇文章就从“AI 模型训练与推理的资源隔离”说起,聊一聊我在这类场景里验证过的做法、踩过的坑,以及一些值得提前想清楚的规划原则。
我会把视角放在一个真实可操作的层面上:先在单机上解决进程和容器的边界,再在集群里靠标签、配额和优先级把两类负载分开,最后落到GPU显存和算力怎么切、存储和网络怎么管。无论你是平台运维、算法工程师,还是偶尔要接管集群的团队负责人,这篇文章都能提供一个判断框架,让你知道该在什么场景下用什么隔离方案。
1. 训练和推理的资源画像:两种工作负载根本不在一个频道上
1.1 训练要“占坑”,推理要“快进快出”
先想清楚一个事实:模型训练和在线推理对资源的口味几乎完全相反。训练任务一旦启动,往往以小时甚至天为单位持续占用GPU、内存和存储带宽,它不在乎单次请求花多少时间,只在乎单位时间内能灌进去多少数据、能算出多少梯度。推理任务恰恰相反,每个请求都是短小细碎的,可能只占几十毫秒的GPU时间,但它对延迟极其敏感,尤其是线上服务,P99一涨,产品侧立刻就能感觉到卡顿。
这种差异决定了我们做资源隔离时不能简单地“把显卡分成两半,你一半我一半”。因为训练任务的资源占用是波动的,batch大小、模型并行策略、数据加载快慢都会让显存和算力需求不断变化;推理任务则相对稳定,它更怕的是别人突然冲进来抢走几百毫秒的算力,导致一次超时。
我见过不少团队一开始图省事,把训练和推理放到同一批机器上,理由是“反正GPU那么贵,不用白不用”。前几周确实风平浪静,因为训练任务还没到峰值阶段。可一旦开始跑大规模预测或模型调参,显存和算力同时被拉满,推理服务的响应时间就会像过山车一样起伏。这时候才意识到,有些资源争抢是可以在设计阶段就避免的,没必要靠事后救火。
1.2 显存、算力、带宽三方面都不同
画一张简单的对比表,能更直观看出两类负载的差异:
| 维度 | 离线训练 | 在线推理 |
|---|---|---|
| 单次任务时长 | 小时到天 | 毫秒到秒 |
| GPU利用率 | 往往接近满载 | 往往只有个位数到十几 |
| 显存变化 | 随batch和动态shape大幅波动 | 相对稳定,峰值较可预测 |
| 延迟敏感度 | 低,更看重吞吐 | 高,P99是生死线 |
| 网络需求 | 大量梯度同步、数据拉取 | 小包高频,跨节点请求多 |
| 故障容忍度 | 可断点续跑,容忍重试 | 单次失败直接影响用户 |
为什么训练任务会把GPU利用率打满?因为它本质上是在“榨取”算力来减少训练时间,多等一分钟都是成本。推理服务则不一样,大部分模型在推理阶段根本用不满GPU,很多时间花在数据预处理、网络传输和序列化上,计算只占一小段。这就造成一种很典型的场景:推理任务显存占了几个GB,算力却只在请求进来那一瞬间被使用,其他时间都是空的。如果这时训练任务想要“顺便”用这块卡,就会导致推理请求进来时,GPU的运算单元正在为训练服务,推理计算只能排在后面,延迟自然失控。
1.3 隔离前先做负载画像
很多人跳过这一步,直接开始配容器限流或调度规则,结果方案总是隔靴搔痒。我的建议是:动手隔离之前,先给当前集群做一次至少七天的负载画像。
怎么做?把每个容器或进程的GPU利用率、显存占用、CPU使用率、磁盘读写、网络吞吐都按分钟级别记录下来,同时记录业务侧的延迟指标。重点不是看平均值,而是看峰值重叠的概率。比如训练任务每天凌晨跑定时评估,推理服务正好也在凌晨有大流量波动,两者峰值一旦重叠,就是隔离方案必须覆盖的边界条件。
负载画像还能回答一个关键问题:到底需不需要物理隔离。如果训练和推理的任务类型都偏轻量,峰值重叠很少,逻辑隔离可能就够了;如果发现训练任务几乎整天占满GPU,那就别再纠结什么优雅共享方案了,直接考虑节点池分离,成本虽然高,但稳定性收益是确定的。没有数据支撑的隔离规划,最后多半要靠运气。
2. 单机内部的资源隔离:先守住进程和容器边界
2.1 进程级隔离只能解决浅层问题
单机层面最朴素的做法,是通过进程管理工具把CPU亲和性、内存上限、GPU设备编号绑定到特定任务上。例如给训练进程绑定2到7号CPU核,给推理进程绑定0到1号核,再用内存限制防止训练任务把系统内存吃到触发OOM。这套方法应付一两台机器、一两个模型的时候是够用的,手工操作也不复杂。
但进程级隔离有几个明显缺陷。一是GPU显存和算力无法通过Linux原生机制真正分割,你只能用设备可见性变量让不同进程看到不同显卡,如果多个进程被分配到同一块卡,它们的算力争抢是控制不住的。二是维护成本太高,每加一个任务都要人工确认绑定关系,一旦有人配置错,排查起来非常痛苦。三是操作系统层面的缓存、页表、中断处理仍然是共享的,训练任务大量读写文件时,系统缓存抖动也会拖累推理进程。
所以我的建议是:进程级隔离只适合做临时止血,不适合作为长期方案。真正要稳定运行,至少得把任务放进独立的容器或虚拟机里。
2.2 容器边界的组合限制怎么写
容器方案的好处是可以在创建任务时一次性声明资源上限。下面是一个典型的组合限制声明,我用通用字段来表示,不管底层用的是哪种容器运行时,思路是一致的:
resources: cpu: "8" memory: "32Gi" gpu: "1" gpuMemory: "40Gi" storageRead: "200MB/s" storageWrite: "100MB/s"关键细节是:CPU和内存的限制相对标准,真正容易忽略的是GPU。如果容器平台不支持显存字段,那就需要在GPU分配层面额外加一道校验,否则可能出现一个只申请了4GB显存的任务,实际把整张卡的显存都占了。早期我们遇到过类似情况:某个数据预处理容器不小心跑到了GPU卡上,模型服务瞬间OOM,整条链路雪崩。
配置容器边界还要注意“限制不等于隔离”。CPU限额能防止任务占用超过设定核数,但多个容器依然共享同一批物理核时,调度器的上下文切换、缓存争抢仍然存在。要想做到比较彻底的隔离,单机层面还需要在系统配置上动手脚,比如把不同任务划分到不同的NUMA节点,让训练任务的CPU访问内存路径不会干扰推理任务。这个操作并不复杂,但需要在上线前确认硬件拓扑,临时改很难。
2.3 容器隔离也盖不住的两个盲区
即便容器配置写得再完善,有两个盲区仍然存在。
第一个是共享GPU导致的算力争抢。两个容器即便各自限制显存,只要它们被调度到同一张卡上,GPU的计算单元就仍然是共享的。训练任务可以轻松把流处理器占满,推理任务的计算请求只能排队,这个排队时间会直接变成推理延迟。显存限制从来不等于算力隔离,这是最容易误解的一点。
第二个是存储和网络带宽的不可控。容器默认只限制CPU内存,对磁盘IO和网络吞吐往往没有硬性限制。一个训练任务疯狂写检查点,可以把整块磁盘的IO能力拖垮;一个训练集群在做梯度同步时,也能把网卡带宽打满,让推理服务跨节点通信出现大量重传。要解决这两块,必须把存储和网络纳入隔离方案,后面我会单独展开。
3. 集群调度的隔离策略:节点池、标签与配额
3.1 机器打标签:物理池是最大前提
单机隔离做得再好,也架不住调度器把任务随机分配到不该去的地方。所以集群层面第一件事,就是把机器按用途打上标签,分成训练池、推理池、评测池、开发调试池。调度器只允许对应标签的任务在对应池内运行,这是所有隔离方案里最粗暴也最有效的一层。
有人会问:难道不能直接用一个池子,靠容器配额来保证不越界吗?答案是:如果你胆量够大,可以试,但代价往往很惨痛。因为即便任务数量不算多,一旦某个训练框架的分布式同步环节写得不够克制,它会把集群所有网络节点的带宽都打满,推理服务的跨节点调用全部受阻。物理池的好处是,即使训练池把资源用尽了,推理池的机器依然干干净净,不会受到波及。
节点打标签的操作本身不难,难的是后续治理。新节点加入如果没有自动化初始化流程,很容易漏打标签,然后被默认调度器当成公共池分配任务。我见过不止一次,运维同学开开心心加了台新机器,结果当晚就被训练任务跑满,第二天推理服务延迟告警。建议把标签校验放在节点准入阶段,做不到准入校验,也要写清楚入网前检查清单。
3.2 配额和命名空间:防住“撑死胆大的”
给机器分好池子之后,还要给每个业务团队设置资源配额。没有配额约束,最直接的后果就是一个团队把池子占满,其他团队排队等到天荒地老。很多时候大家把配额字段一写就觉得完事了,但实际上配额至少要考虑三层:CPU和内存、GPU卡数、总显存容量。
GPU显存配额尤其容易被忽略。某些任务申请1张GPU卡,但模型很大,实际的显存需求可能超过一块卡的一半。如果只看卡数不看显存,两个大模型任务被调度到同一张卡后就会争抢显存,甚至触发OOM。我在实操中会把每个业务团队的显存配额单独列一项,由调度器在分配时校验。校验不了的场景,就在镜像里内置一个显存预检步骤,启动时先探测显卡剩余显存,不够就自动退出并返回排队原因。
配额还会引出一个公平性问题:离线训练任务可以排队等待,在线推理服务却不能等。因此推理团队通常需要独占高优先级配额,训练团队则使用可抢占配额。这个在分配上要提前约定,不能全池共享一个默认值。
3.3 优先级和抢占:在线推理必须被保护
即便做了节点池和配额,依然可能出现某个训练任务通过预留资源的方式把推理池挤占的情况。所以隔离策略里还必须包含优先级机制:在线推理服务在整个集群里拥有最高的优先级,训练和评测任务可以被VIP调度器挂起、抢占、重新排队,但推理服务永远不能被训练任务挤掉。
这里有个容易被忽略的点:被抢占的训练任务需要能够恢复进度,否则强行抢占只会导致训练计算浪费。训练任务的监控检查点要足够频繁,至少每十分钟保存一次模型状态。否则被抢占一次,重启就要从头再来,团队之间的仇恨值会迅速飙升。
优先级机制的具体实现,可以在调度器里为不同服务类型定义不同的优先级阈值,再配合抢占策略。例如推理服务的优先级写10,训练任务写5,当训练任务和推理服务同时申请同一池内资源时,调度器优先满足推理服务。如果是配额已满且训练任务长时间运行,调度器可以将其挂起,等推理服务完成资源释放后再恢复。
4. GPU算力与显存隔离:从逻辑分片到硬件级切片怎么做
4.1 第一档:按显存上限隔离的局限
很多深度学习框架提供了限制显存使用的参数,例如设置显存占用比例,或者通过特定环境变量让进程认为自己只看到一部分显存。这种做法的价值在于防止任务越界占满整卡,让同一块卡上能同时跑多个小模型。
但它的局限非常明显:显存和算力是两个维度。一个训练任务就算只分到20GB显存,它的计算指令仍然可以塞满整个GPU的计算单元,让旁边同样卡在显存限制里的推理任务排队等算力。所以如果你只依赖显存限制做隔离,那只能在“不同任务互不干扰”的幻想里睡上一段时间,等到峰值流量一来就会被现实叫醒。
我的建议是:显存限制只作为一道兜底线,防止OOM级灾难,不要把它当作真正的算力隔离方案。
4.2 第二档:时间片共享的代价
GPU时间片隔离是另一条常见路线。它的思路是当一个任务用不到全部算力时,把GPU算力按时间片分给多个任务轮流使用。这样每个任务都能分到一定的计算资源,且不会让GPU空闲。
代价也很明确:任务切换会产生开销。对于训练任务来说,时间片切换可能让有效计算时间下降;对于推理任务来说,时间片带来的抖动非常致命。推理请求通常在几十毫秒内完成,如果轮到它时正好被切出去,等待下一个时间片回来,延迟可能直接翻倍甚至更高。所以我的经验是:时间片适合用在低优先级的评测任务、统计算法调试任务,不适合给在线推理服务使用。
另一种变体是按算力比例进行软隔离,比如让某个任务最多只能使用30%的计算单元。这种方式比纯时间片平滑一些,但仍然受限于任务的调度粒度。如果你希望推理服务的P99长期稳定,还是别依赖这一档。
4.3 第三档:硬件多实例分片的硬边界
如果硬件支持,优先考虑硬件级多实例分片方案。它把一张物理GPU切割成多个独立实例,每个实例拥有独立的显存、独立的内存控制器、独立的计算单元,实例之间在硬件层面天然隔离,互不抢占。这套方案比任何软件层的逻辑分片都可靠,因为一个实例里的任务跑得再疯,也不会影响另一个实例里任务的延迟。
硬件分片的坑在于切分粒度是固定的,无法根据每个任务的实际需求动态调整。比如一张卡显存共80GB,硬件方案通常支持切成7个实例或8个实例,但每个实例的显存大小是固定的。如果推理模型需要30GB显存,而切分方案里没有恰好对应的实例大小,你就只能用一块更大的实例,浪费掉一部分显存。
另一个坑是:分片方案必须在显卡初始化时配置好,后续运行时不能随意修改。修改配置往往需要重启GPU,如果集群里正有训练任务在跑,这会引发大范围重启。所以硬件分片方案更适合长期稳定的推理服务,不适合频繁变化的实验环境。
4.4 三种方式的选型对照
这里给出我自己的选型建议,方便你直接抄作业:
| 隔离方式 | 隔离强度 | 额外开销 | 适合场景 |
|---|---|---|---|
| 显存限制 | 弱,只能防止OOM | 低 | 小模型混部、临时兜底 |
| 时间片共享 | 中,但延迟抖动明显 | 中 | 离线评测、批量调参 |
| 硬件多实例分片 | 强,稳定可靠 | 低但灵活性差 | 在线推理、关键生产链路 |
我在实际项目中更倾向这样的组合:半张物理卡给推理服务预留,确保延迟可控;剩下能力切给训练任务,但不能让训练任务和推理服务混用同一片硬件实例。如果业务量不大,就用一块卡专门跑推理,另一块卡专门跑训练,物理隔离永远是最省心的。
5. 存储与网络的隐性隔离:吞吐、带宽和缓存都是资源
5.1 磁盘IO队列不能混用
GPU隔离解决的是计算和显存问题,但AI任务从来不只是算GPU。训练任务要拉取数据集、写检查点、缓存中间特征;推理服务要加载模型、写访问日志、读配置文件。这两类IO模式放在同一个存储系统里,就会互相拖累。
训练任务通常会产生大量顺序写,比如每跑完一轮就写一次模型检查点。这个写操作如果和推理日志的随机写落到同一块磁盘,磁盘调度器会频繁切换寻道方向,整体IO效率急剧下降。更严重的是,推理服务的日志丢失和模型加载超时会直接影响线上可用性。
我的建议是至少把训练和推理的存储卷从物理上分开。训练挂载高性能大容量盘,推理挂载低延迟SSD,两者之间不要共享同一个文件系统挂载点。如果条件允许,还可以给不同目录设置不同的IO优先级,让推理服务的磁盘请求优先获得处理。这一步很多人会忽略,等到磁盘IO被打满才发现问题,那时候再迁移数据就很痛了。
5.2 缓存目录和临时文件必须划分边界
AI训练和推理都有缓存机制。训练会把预处理后的数据缓存到本地临时目录,推理则会把模型文件缓存起来以便快速加载。如果这两类缓存目录没有被严格划分,就会发生一件特别戏剧化的事:清理磁盘的定时任务把推理服务的模型缓存当成垃圾清掉了,然后所有推理服务在第二天流量高峰时冷启动加载模型,延迟集体飙升。
这听起来像是低级错误,但我确实遇到过不止一次。根本原因就是大家只关注了计算GPIO隔离,忘了文件系统层面的边界。一次性规划好训练缓存目录、推理模型缓存目录和临时文件目录,并在清理脚本里明确白名单,能省掉很多半夜被叫醒的机会。
5.3 网络流控:训练同步别挤占推理链路
分布式训练对网络的消耗远超很多人想象。几十张卡同步梯度的时候,数据量可以达到每秒几十GB,如果和推理服务的跨节点请求共享同一张网卡和同一条网络链路,推理服务的延迟会直线上升。
要处理这个问题,第一步是区分网络平面。训练和推理的流量最好走不同的网段或不同的物理链路,做不到物理分离也要在虚拟网络层面做隔离。第二步是对网络流量做带宽限制,例如给训练任务的同步流量设置上限,让它不能把网卡带宽全部占满。设置阈值时要注意:训练任务的网络性能下降,最直接的影响是GPU利用率也会下降,因为数据喂不过来,所以阈值不能设得太低,要在吞吐和隔离效果之间找一个平衡点。
网络隔离还有一个细节容易被忽略:管理面和数据面的流量要分开。如果集群管理工具使用的主机通信链路和训练数据链路互相争抢,GPU任务会在调度时出现莫名其妙的超时。建议把管理网络和训练网络独立出来,至少保证管理流量有独立的优先通道。
6. 隔离效果的验证与监控:把争抢变成看得见的数字
6.1 关键指标表:按进程看还是按容器看
做隔离规划时,很多人只盯着GPU利用率这一个指标,这是远远不够的。GPU利用率高,到底是被哪个容器拉高的?显存占用高,是什么时候涨上去的?响应延迟变大,是算力不足还是IO拖慢?要回答这些问题,监控粒度必须细化到进程或容器,而不是只盯着节点级图表。
我通常会关注以下几类指标:
| 类别 | 关键指标 | 为什么重要 |
|---|---|---|
| GPU | 利用率、显存占用、温度、降频状态 | 判断是否算力打满或显存不足 |
| CPU | 使用率、软中断、上下文切换 | 判断CPU是否成为瓶颈 |
| 存储 | IO等待时间、读写带宽、队列深度 | 判断磁盘争抢是否严重 |
| 网络 | 吞吐、丢包率、重传率 | 判断是否因网络拥塞影响延迟 |
| 业务 | P50、P99、错误率 | 最终衡量隔离是否生效 |
重要的一点是:不要只看平均值,要看峰值曲线和重叠状态。训练任务可能平均GPU利用率只有60%,但每10分钟会有一个同步阶段把利用率拉到95%。如果在监控图上只看平均,你会误以为还有40%算力可用,然后把推理服务调度上去,结果每次同步阶段推理延迟就飙一下。
6.2 上线前的隔离压测流程
任何隔离方案落地后,都要用压测来验证,不能凭感觉说“看起来没事”。我常用的流程很简单:
第一步,先给推理服务打一段稳定的基准流量,记录它的P99延迟和错误率,作为对照基线。第二步,在隔离环境下启动一个模拟训练任务,让它以接近真实的负载运行。第三步,观察推理服务的延迟曲线,如果P99仍然在业务可接受范围内,则说明隔离方案有效;如果P99明显恶化,就需要逐步排查是算力、显存、存储还是网络出了问题。
压测时有一个容易遗漏的点:模拟训练任务要包含数据加载和检查点写入。只跑纯GPU计算是测不出存储争抢的。我在压测中会把训练任务的数据加载频率调高,让它的IO模式接近真实场景。这样压出来的结果才可能原样映射到线上。
6.3 一次混跑事故的排查复盘
最后分享一个排查实例,帮助你把前面所有隔离原则串起来。某团队有两类服务:一个离线训练任务,一个在线推理服务。上线初期业务量小,混在同一批机器上风平浪静。后来训练数据量变大,某天下午推理延迟突然冲到三秒以上。
排查链路是这样的:先看GPU利用率的节点级图表,发现整台机器GPU利用率并不高,只有50%左右,于是很疑惑。再按容器维度拆开看,发现训练容器和推理容器被调度到了同一块GPU上,训练容器在大部分时间只占少量算力,但每当它的数据加载阶段结束,GPU利用率会瞬间冲到90%以上,推理请求正好在这个窗口被堵住。
再往深挖,发现训练容器的GPU设备编号没有限制,调度器也没有强制绑定,导致它可以在整台机器的任何一张卡上运行。最终修复方案很简单:把训练任务固定到专用GPU上,并将推理服务所在的池子设成不允许训练任务调度进去。整个过程最大的教训是:如果从一开始就按节点池分离,并严格校验GPU设备编号,这次故障完全可以避免。
我自己做隔离规划时,习惯先回答三个问题:业务峰值会不会重叠,推理服务对延迟到底多敏感,隔离付出的成本是否可以接受。能接受混跑,就用逻辑隔离加配额;不能接受,就物理分离加节点池。合理的隔离不是消灭资源争抢,而是把争抢控制在不会影响关键业务的范围内。