☰
GPU资源隔离实战:让训练与推理在共享集群中互不干扰
2026/10/11 18:50:01 网站建设 项目流程

资源隔离这个词,听起来像是运维该操心的事,但我在实际项目中体会是:它往往是AI工程化过程里最容易被低估、也最容易拖垮线上稳定性的一环。训练任务和推理服务共用一批GPU,训练一开跑,推理的P99延迟直接翻倍;反过来,推理流量一上来,训练任务被挤到反同步,整批实验报废。资源隔离要解决的问题,不是简单地把卡分两堆,而是要在共享算力、显存、带宽、CPU核和内存的前提下,同时保住训练任务的吞吐和在线推理的延迟。

这篇文章用我在某公司智能平台团队里做过的模拟项目X的经验来讲透这件事:训练和推理为什么会互相伤害、隔离有哪些层级、每层怎么做、以及那些文档里不会写的坑。适合正在跑大模型训练、又扛着线上推理服务,想把GPU利用率提上去但又不敢乱动资源分配的同学参考。

1. 资源隔离前,先看清训练和推理的“脾气”差异

1.1 训练和推理的资源画像

训练任务和推理服务在资源消耗特征上根本不是同一类负载。训练是典型的“长跑型”负载:一批数据喂进去,GPU算力长时间跑满,显存需求大且持续,batch size大、算子密集,多机多卡之间还有大量集合通信流量。它对延迟不敏感,哪怕某个step慢了几十毫秒,也就多等几秒,问题不大。

推理则是“短跑型”负载:请求突发性强,单个请求的算力消耗很小但频率极高,显存需求以模型权重和KV cache为主,整体利用率往往不高,但对延迟极度敏感。用户发来一句话,几百毫秒不出结果,体验就崩了。

这两类负载放在同一张卡上,冲突几乎是必然的。训练任务为了吃满算力会把SM占用拉到接近100%,推理的算子排队时间就长了;训练的大显存请求还会让推理的显存申请失败,直接OOM。更隐蔽的是带宽竞争,训练要做好多个GPU之间的梯度同步,网卡和NVLink流量常年很高,推理虽然数据量小,但关键路径上的任何一次抖动都会造成长尾延迟。

我见过一个典型的反例:某项目组为了省卡,把一个小规模LoRA微调任务和一个在线问答推理服务放在同一台8卡机器上。训练任务在凌晨两点自动触发,第二天早上一看监控,推理P99从平时的18ms飙到了93ms,线上反馈炸了。这不是偶然,只要训练任务把资源顶到极限,推理没有保底资源,必然出问题。

1.2 隔离的本质:给谁压,给谁保

理解了资源画像差异之后,隔离的本质就清楚了:不是把所有资源平均分,而是按照SLA目标做差异化分配。训练任务要的是“尽量跑快”,可以用完冗余资源,但不能影响核心链路;推理服务要的是“始终稳定”,即使牺牲一点峰值吞吐,也要保证P99在阈值内。

这个思路决定了隔离方案的整体倾向。训练侧可以接受资源被高优任务抢占后暂停或重启,所以适合放到可被驱逐的队列里;推理侧必须被保护,所以要有独立配额、独立节点池、独立显存分区,任何情况下都不能被训练任务挤占。

想清楚这一点,后面选型才不会跑偏。很多人一上来就想着“把两张卡分给训练、三张卡分给推理”,这是物理隔离的初级形态,能解决一部分问题,但资源碎片率很高。更合理的做法是先用硬件虚拟化把小颗粒资源隔离做掉,再用调度策略把不同SLA的负载分开,最后靠监控兜底。

2. 从整卡到虚拟化:GPU层面的隔离方案选型

2.1 物理隔离:最贵也最省心的方案

物理隔离就是给训练任务和推理服务使用不同的物理GPU,甚至不同的物理机。这是最直接的方案,隔离强度最高,故障域最小,排查问题也最简单。

但它的代价是资源碎片化。一张80GB显存的卡,推理服务可能只要20GB,剩下60GB用不上又不能让训练任务共享。如果集群规模小,这种方案会让整体利用率很难看。而且物理机级别的隔离还会带来调度灵活性的问题,训练任务要看机器拓扑,推理服务要看高可用,两边都舒服的机器并不多。

所以物理隔离适合核心链路推理服务,以及有合规要求、不允许任何资源争抢的场景。对一般项目来说,我更推荐把它作为兜底方案,而不是默认选项。

2.2 硬件虚拟化接管小颗粒度

硬件虚拟化是当前性价比最高的GPU隔离手段。典型方案如MIG(Multi-Instance GPU),可以把一块物理GPU切分成多个独立的GPU实例,每个实例有独立的显存、SM和带宽切片。不同实例之间的故障和性能隔离是硬件级的,一个实例里跑满算力,另一个实例的延迟也几乎不受影响。

我在模拟项目X里用MIG切分过一批推理用卡,把一张80GB的卡切成3个实例,每个实例约20GB显存,分别承载不同QPS的推理服务。实测下来,实例之间的隔离效果确实接近物理隔离,而且利用率比整卡分配高很多。需要注意,MIG切分后,数据并行训练里常用的NCCL通信在部分切分模式下兼容性并不好,所以训练任务我基本不会放到MIG实例上,它更适合纯推理场景。

如果用的加速卡不支持MIG这类硬件虚拟化,退而求其次可以用vGPU方案,靠驱动和虚拟化层做显存和算力限制。这类方案的隔离强度弱于硬件级,尤其在算力抢占方面,极限负载下还是会有互相影响,但比完全没有隔离强得多。

2.3 容器调度下的GPU分配细节

现在多数团队跑在容器平台上,GPU分配靠调度器统一管理。容器调度GPU时,最关键的是不要让训练任务的资源需求溢出到推理实例上。

实际操作里,我会给推理服务所在的节点池打上污点,训练任务默认无法调度上去;训练任务所在的节点池则允许推理服务在低峰期复用,但必须设置显存上限和算力上限,防止互相挤占。纯容器级需要给工作负载声明GPU数量,这一点大家都会写,但真正容易忽略的是:配套的device plugin是否支持MIG实例上报。如果不支持,你就算切好了MIG,调度器也不知道有多少个实例可用,还是会按整卡去调度,隔离就名存实亡。

我见过某项目组调了一天资源隔离策略,GPU利用率始终上不去,查到最后是设备插件版本太旧,根本不识别MIG的切片信息。这种基础组件问题最坑人,排查起来非常隐蔽。

3. 别只盯GPU:CPU、内存与网络的资源隔离

3.1 用 CGroup 把 CPU 份额钉死

大家做隔离时容易犯一个毛病:只盯GPU,把CPU、内存和网络当成次要角色。实际上,训练任务的数据加载、预处理和后处理都要吃CPU,推理任务的算子执行和框架调度也要吃CPU。两边如果同时抢CPU核,结果一样是两败俱伤。

控制在CPU层面,最底层的手段是CGroup。通过容器资源限制,可以精确设置某个工作负载使用的CPU核数、CPU份额和内存上限。推理服务我通常会写成整核预留,比如requests给8核,limits给8核,用cpuset方式绑定物理核,避免被调度器弹来弹去。训练任务则可以用cfs配额限制,允许它突发使用空闲核,但一旦推理负载上来,内核调度会优先保证推理任务的配额。

这里面有个容易被忽略的细节:CPU绑核和CPU份额是两套机制。份额控制的是权重,绑定控制的是位置。推理服务两种都要做,保证延迟稳定;训练任务可以只做权重控制,允许它吃点空闲资源,但别让它绑死整台机器的核。

3.2 NUMA 感知与内存带宽隔离

在单机多路服务器上,CPU和内存的访问是有远近之分的。一个CPU核访问本NUMA节点内的内存,速度很快;跨NUMA节点访问,延迟和带宽都会打折扣。推理服务如果被调度在离散的CPU核上,内存访问正好跨了NUMA节点,P99延迟会很稳定地变差,而且这个变差和负载无关,纯粹是物理拓扑问题。

处理办法是给推理服务做NUMA亲和性绑定:让它的CPU核、内存和GPU落在同一个NUMA节点内。这个优化在训练场景里同样重要,分布式训练的数据加载如果跨NUMA,吞吐会有明显下降。我在项目里用numactl配合容器启动参数做绑定,推理P50变化不大,但P99降了将近三成,效果非常明显。

内存带宽也需要关注。训练任务在做数据增强时内存拷贝量大,推理服务的KV cache读写同样吃带宽。这个层面没有太细粒度的控制手段,更多依赖调度层面的隔离,让两类负载尽量落在不同的物理机上,或者至少不同的NUMA节点上。

3.3 网络带宽的隔离与限速

分布式训练的网络流量是恐怖的。数据并行每步都要做梯度同步,千兆网卡被跑满很常见。而在线推理服务如果用同样的网络路径,训练流量一突增,推理请求的往返时延就会波动。

网络隔离有几个层次。最稳妥的是物理网络隔离,训练流量和推理流量走不同的网卡、不同的交换机,但多数团队没这个条件。实用做法是在虚拟网络上做隔离:把训练任务和推理服务划到不同的网络命名空间,再配合服务质量策略做带宽限速。

我习惯的配法是:训练任务的网卡带宽上限设为物理带宽的70%,保证它不会把网络彻底堵死;推理服务单独限一个最低保障带宽,不做上限限制或做得很宽。限速工具各家实现不一样,但思路一致:给抢占型任务加限制,给保活型任务留空间。

4. 运行时与应用层的隔离设计

4.1 进程级资源控制的落地做法

容器层面的隔离管得住资源配额,但管不了运行进程本身的一些行为。比如推理服务启动时,框架会做显存预分配,训练任务也会做类似操作。如果两者跑在同一张物理卡上,就需要在进程级别控制每个进程对GPU的可见性和利用率。

最基本的手段是环境变量控制。用CUDA_VISIBLE_DEVICES限定进程只能看到规划好的GPU编号,这个大家应该都在用。但要注意,如果用了MIG实例,环境变量里写的是实例编号而不是物理卡编号,配置错一个数字,进程可能直接起不来,或者不小心用到了别人的实例,这是非常危险的。

推理服务的启动参数里,我还会设置显存分配策略为按需增长。很多推理框架默认会预先占满整卡显存,在你做MIG切分或共享卡时,这会导致明明显存没用到多少,但其他任务已经无卡可用。改成按需增长之后,才能发挥共享的意义。

4.2 显存分配策略的差异处理

训练和推理在显存管理上的策略应该完全不同。训练任务为了吞吐,通常希望把显存用到极限,batch size能大则大,哪怕偶尔要跟别的任务抢一抢,也不在乎。所以训练侧的显存管理可以激进,不用设上限,或者设一个接近物理上限的值。

推理则相反,它要保证的是请求处理不被打断。如果显存设置得太紧,突发请求一来,KV cache增长导致OOM,整个服务直接崩掉,比延迟劣化严重得多。因此推理服务要预留足够的余量,例如给峰值估一个波动系数,再留20%的缓冲显存。

这个分配差异在容器配置上要体现出来。训练任务我一般不设置显存limits,只设置requests,目的是让调度器知道它要占多少卡,但给它一定的弹性;推理任务则必须同时设置requests和limits,而且limits不能等于显存上限,要低于上限留缓冲,避免触发OOM Kill。

4.3 优先级与抢占:让高优任务说话

在资源有限的前提下,光做静态分配还不够,必须有动态调整机制。训练任务和推理服务之间、训练任务彼此之间,应该存在优先级差异。比如在线推理服务的优先级最高,标注类离线任务次之,实验探索型训练任务最低。

容器的优先级抢占怎么做?在K8s里可以用PriorityClass给不同负载设定优先级,调度器在资源紧张时会优先保证高优先级任务,需要时驱逐低优先级任务。推理服务的PriorityClass必须是集群里最高的,低优先级训练任务被驱逐后会自动重排到其他空闲资源上。

这里有三个坑要提醒:一是训练任务被驱逐后要支持断点续训,否则驱逐一次等于白跑几小时,整个机制没有意义;二是驱逐要让调度器不要太频繁,否则会出现训练任务反复起停、永远跑不动的“活锁”状态,解决办法是给被驱逐任务设置重调度冷却时间;三是推理服务之间的优先级也要细分,核心接口和辅助接口不能平级,否则突发流量时容易互相抢资源。

5. 调度层与容量规划:放之集群维度的隔离

5.1 节点池与污点隔离

集群维度做隔离,最常用的手法是节点池加污点。节点池把物理机按用途分组,比如推理池、训练池、混部池。污点和容忍则控制哪些负载可以调度到哪些节点上。

我在模拟项目X里的节点池设计大致是这样:推理池的节点数量固定,只运行在线推理服务,节点上打上污点,只有推理服务声明了相应的容忍才能上来。训练池的节点数量有余量,主要跑离线训练任务,但允许低优先级的推理服务在低峰期进行弹性扩容。混部池则是动态资源池,按CPU和内存的余量动态调配。

这套设计的核心思想是:给核心链路留出确定的容量,把弹性需求放到共享池里。对于成本压力大的团队,混部池的比例可以设高一些;对于稳定性要求极高、预算又充足的团队,可以把推理池完全独立出来,连混部都不做。

很多团队的问题是只分池不做污点,只写亲和性不完备。结果是调度器看节点有资源就把任务塞进去,隔离策略形同虚设。污点加容忍这套机制一定能用上,不要省。

5.2 配额与公平性调度

光有节点池还不够,不同团队、不同项目之间的资源分配必须有配额约束。否则一个团队把集群资源全占了,其他任务全部排队,项目协作就没法谈。

命名空间级别的ResourceQuota是基础,它限定某个命名空间最多使用多少CPU、内存、GPU卡数。LimitRange则是给单容器设置的上下限,防止有人写超大requests把配额撑爆。这两个加到一起,能保证“大户”不会吃光一切。

公平性调度还需要考虑训练任务之间的资源竞争。多个训练任务同时要卡时,如果按先到先得,后来的长任务可能永远跑不上。合理做法是引入公平分享的调度策略,让不同项目组按权重分摊资源,同时允许空闲资源被临时借用,等借出方需要时再还回来。

这里有一个现实问题:训练任务的GPU申请往往是“整卡”粒度,削减一分都跑不了。所以混部池在共享时要做好提前量,不要借出太多卡,否则对方任务一开始就需要多张整卡,结果资源凑不齐,任务还是一直排队。

5.3 容量规划的几个实操数字

做容量规划时,工程上总要有个起点。我在项目里常用的估算逻辑是:先算推理服务的显存基线。单个推理实例的显存主要看模型权重和KV cache,权重大小基本固定,KV cache则跟并发深度和序列长度强相关。拿一个大模型做例子,假设权重占40GB,KV cache峰值约30GB,单实例显存就要预留70GB以上,这还没算框架自身的开销。

然后是算力需求。在线推理服务的实际算力占用波动很大,不能按峰值去规划,否则浪费严重。我会用一个经验值:按峰值QPS对应的算力需求的70%来预留,剩下30%靠队列缓冲和弹性扩缩容去扛。

训练任务的容量则是另一套算法,看的是总算力需求和训练时长目标。比如一个千卡规模的任务想在两天内跑完,就要保证千卡资源一周内基本可用。这个窗口期的资源保障率,决定了混部池可以借出多少资源。保障率设在90%以上时,借出比例一般不超过训练池总资源的三成,否则经常会出现资源凑不齐的情况。

6. 监控告警与避坑实录

6.1 必须盯住的资源指标

做资源隔离,前提是看得见资源在怎么用。只看显存占用和GPU利用率远远不够,这两个指标太粗了。

至少要把下面几类指标纳入监控范围:GPU维度要盯SM利用率、显存占用率、温度、功耗和算力受限状态;带宽维度要盯NVLink和PCIe流量、网卡出入带宽;应用维度要盯推理P50/P99延迟、QPS、训练任务吞吐和当前迭代耗时;系统维度要盯CPU load、内存带宽、NUMA节点命中情况。

工具的选型,我习惯用DCGM exporter采集GPU细粒度指标,配合Prometheus做告警。它可以采集每个GPU进程的算力利用情况,这样排查“哪个容器在抢算力”时,一张图就能定位到具体进程。另外,查看GPU受限状态很重要,如果某个推理实例频繁出现算力受限,说明资源配额不够或者有更强请求在争抢,这个告警能提前暴露问题。

6.2 常见故障速查表

我在项目里遇到的典型问题,基本都算有共性的坑,整理成速查表方便后面团队参考。

现象可能原因排查思路后续规避方案
推理P99飙升,显存不高CPU绑核失效,跨NUMA访问查看CPU亲和性配置与系统分配核号推理容器绑定固定物理核,禁止调度器迁移
训练一启动,推理延迟变差GPU算力被训练任务抢占用DCGM看SM利用率和受限状态训练任务降到混部池,推理池不共享
显存OOM,但占用率看起来不高某进程预分配显存或显存碎片化严重查看每个容器的显存预分配值推理服务开启按需分配,限制显存limits
训练被驱逐后反复重启调度抢占过于激进,无冷却时间看调度事件和任务重试次数设置重调度冷却时间,做断点续训
集群资源显示充足,任务调度不上污点与容忍配置不一致查看调度器过滤后的可用节点列表检查污点和容忍声明是否匹配
MIG集群利用率上不去设备插件无法上报MIG实例检查插件日志及节点资源上报升级支持MIG的插件版本,校准上报信息

排查的第一步永远是看监控曲线,不要上来就查日志。我先看曲线找对应的变化拐点,再对照调度事件和资源配额,命中原因的概率会大很多。

6.3 几个我踩过并且值得注意的坑

先说最典型的一个:为了降低GPU开销,我曾把推理服务调度到MIG实例上,训练任务放到同一张物理卡的另一个实例上。表面看隔离得挺好,结果训练任务做AllReduce时,NVLink带宽被占掉一大半,推理实例的跨实例通信延迟直接受影响。MIG隔离的是SM、显存和部分带宽,但卡内部的链路资源仍然是共享的。所以在带宽敏感场景下,训练和推理混在同一张物理卡上,哪怕分实例也有风险。

第二个坑是“自动弹性”在训练场景里基本是反模式。推理服务可以靠HPA做弹性伸缩,训练任务如果也跟着伸缩,一旦节点抖动,任务就会在节点间反复搬迁,进度经常回退。我的做法是训练任务用固定实例数量,只在容量规划层面做预留,不在运行时自动伸缩。

第三个坑是资源隔离做得太严格导致利用率过低。给推理服务预留30%的缓冲,给训练池留出保障率,这些都是合理的,但如果每个维度都预留,叠加起来浪费就很惊人。项目里我们要定期检视这些缓冲参数,根据几个月的运行数据逐步收紧,而不是一次配置就丢在那不管。

最后再分享一个实用小技巧:把资源隔离策略和发布流程绑定。每次推理服务版本变更、训练任务资源配置调整,都当作一次容量变更来做评审,配套的监控看板一起更新。我在项目上吃过亏,资源策略已经改了监控还没跟上,出了问题连现场数据都查不到。后来定了规则:策略变更必须先于发布生效,监控看板必须同步刷新,这个习惯帮我省了大量排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询