资源隔离这个词听起来像是运维该操心的事但我在实际项目中体会是它往往是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隔离手段。典型方案如MIGMulti-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. 别只盯GPUCPU、内存与网络的资源隔离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则跟并发深度和序列长度强相关。拿一个大模型做例子假设权重占40GBKV 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%的缓冲给训练池留出保障率这些都是合理的但如果每个维度都预留叠加起来浪费就很惊人。项目里我们要定期检视这些缓冲参数根据几个月的运行数据逐步收紧而不是一次配置就丢在那不管。最后再分享一个实用小技巧把资源隔离策略和发布流程绑定。每次推理服务版本变更、训练任务资源配置调整都当作一次容量变更来做评审配套的监控看板一起更新。我在项目上吃过亏资源策略已经改了监控还没跟上出了问题连现场数据都查不到。后来定了规则策略变更必须先于发布生效监控看板必须同步刷新这个习惯帮我省了大量排查时间。