27届大模型面试准备(七十一):多租户 GPU 虚拟化与推理隔离工程------MIG、MPS 与噪声邻居治理
引言
上篇(六十九)讲 Prefill-Decode 分离,把单卡算力按阶段拆开调度;(七十)讲网关层如何把请求路由到不同实例。本篇把视角再抬一层:当一张 A100/H100 要同时服务多个业务方(多租户)时,怎么在显存、算力、延迟三个维度做隔离与复用,既把卡利用率榨干,又不让某个租户的突发流量把别人的 P99 拖垮。这是华为云、大模型推理平台岗的高频题,也是面试里最容易区分"只会调 API"和"真做过平台"的分水岭。
前文链接:A70 推理服务负载均衡与智能请求路由、A69 Prefill-Decode 分离式推理架构、A66 推理调度与弹性扩缩容工程。
┌──────────────────────────────────────────┐
│ 推理网关 / 多租户调度器 │
│ 租户标识 → 路由 → 配额 → 优先级队列 → 限流 │
└───────────────────┬──────────────────────┘
│
┌─────────────────────────────┼─────────────────────────────┐
│ 租户A(高优/SLA) │ 租户B(中优) │ 租户C(best-effort)
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌──────────────┐
│ MIG slice │ │ MIG slice │ │ 共享 MPS 时间片│
│ 1g.10gb │ │ 3g.40gb │ │ (无硬隔离) │
└───────────┘ └───────────┘ └──────────────┘
│ │ │
└─────────────────────────────┴─────────────────────────────┘
▼
┌────────────────────────────────────┐
│ NVIDIA A100 80GB 单卡 │
│ SM 阵列 / HBM / NVLink 物理资源 │
└────────────────────────────────────┘
表:四种隔离手段对比
| 手段 | 隔离粒度 | 算力隔离 | 显存隔离 | 切换开销 | 典型适用场景 |
|---|---|---|---|---|---|
| MIG | 硬件物理切片 | 强(独占 SM 子集) | 强(独占 HBM 分区) | 中(需重建 slice) | 稳定多租户、强 SLA 合同 |
| MPS | 进程级共享引擎 | 中(按 CUDA context 分时) | 弱(共享显存池) | 低 | 同信任域、提利用率 |
| 时间片调度 | 调度器级 | 弱(时分复用整卡) | 无 | 低 | best-effort 错峰复用 |
| 容器+Cgroup | 主机级 | 依赖驱动/MPS | 依赖驱动 | 低 | 物理机多实例混部 |
一、为什么推理平台必须做多租户隔离
云上推理的两个矛盾:(1) 单卡跑一个小模型太浪费,7B/13B 模型 decode 阶段显存占用可能只有 8GB,剩下 70GB 空转;(2) 把多个模型塞一张卡,某个租户来一波长上下文突发,就会把共享的 HBM 带宽和 SM 占满,别人的 TTFT 从 200ms 飙到 3s。不做隔离,利用率和公平性只能二选一。
面试常问:"你们线上一张卡跑几个模型?怎么保证互不干扰?"标准答案不是"用 Docker",而是分层:硬件切片(MIG)→ 驱动级共享(MPS)→ 调度器配额(K8s/Volcano)→ 应用级限流(网关)。
二、硬件级 MIG:把一张卡切成多张"小卡"
MIG(Multi-Instance GPU)在 A100/H100 上把 SM 阵列、L2 Cache、HBM 带宽、显存做物理分区。一个 7g.40gb 的 slice 就是一张"逻辑 GPU",有独立的显存和算力,一个 slice 的故障不影响其他 slice。
# 创建 1 个 1g.10gb + 1 个 3g.40gb 的组合(需先置 admin 模式)
sudo nvidia-smi -i 0 -mig 1
sudo nvidia-smi mig -i 0 -cgi 1g.10gb,3g.40gb -C
# 查看 slice
nvidia-smi mig -lgi
# 输出示例:
# GI ID CI ID Profile
# 9 0 1g.10gb
# 10 1 3g.40gb
# 应用侧用 CUDA_VISIBLE_DEVICES 绑定到具体 GI
CUDA_VISIBLE_DEVICES=9 python -m vllm.entrypoints.openai.api_server --model qwen2.5-7b
MIG 的代价:slice 之间不能共享 KV Cache,跨 slice 不能做张量并行;且创建/销毁 slice 需要短暂中断整卡。所以它适合"长期稳定、SLA 不同"的租户组合,不适合频繁变配。
三、驱动级 MPS:同信任域提利用率
MPS(Multi-Process Service)让多个进程共用同一个 CUDA context,kernel 在 SM 上并发调度,显存仍共享。它没有硬隔离,但能把"两张卡各跑 30% 利用率"的两个小模型合并到一张卡跑 60%,省一张卡的钱。
# 启动 MPS 服务端(按用户/按卡)
export CUDA_VISIBLE_DEVICES=0
nvidia-cuda-mps-control -d
# 之后该卡上的所有 CUDA 进程都走 MPS 共享引擎
# 限制单进程可用 SM 比例(需 MPS 配置 + 配合 cgroups)
echo "set_default_active_thread_percentage 50" | nvidia-cuda-mps-control
注意 MPS 下的故障传染:一个进程显存越界可能拖垮同 context 的其他进程。所以 MPS 只在"同团队、同信任等级"的内部租户间用,对外售卖必须用 MIG。
四、显存配额与上下文隔离
即使不用 MIG,也要防止某个租户申请超大 KV Cache 把整卡 OOM。做法是在调度器层给每个模型实例设显存预算,并在推理引擎里做硬性上限。
# 伪代码:按租户维度限制 KV Cache 预算
class TenantGPUScheduler:
def __init__(self, total_hbm_gb=80):
self.budget = {} # tenant -> 显存配额(GB)
self.used = {} # tenant -> 已用(GB)
def admit(self, tenant, need_gb):
if self.used.get(tenant, 0) + need_gb > self.budget.get(tenant, 0):
return False, "tenant_gpu_quota_exceeded"
if sum(self.used.values()) + need_gb > self.total_hbm_gb * 0.92:
return False, "node_hbm_pressure"
self.used[tenant] = self.used.get(tenant, 0) + need_gb
return True, "ok"
def release(self, tenant, free_gb):
self.used[tenant] = max(0, self.used.get(tenant, 0) - free_gb)
五、噪声邻居(Noisy Neighbor)治理
"噪声邻居"指同卡其他租户的突发把你的延迟拖垮。治理三板斧:
-
优先级抢占:高优租户到来时,低优租户的长 decode 被挂起或限速。
-
带宽隔离:MIG 天然隔离 HBM 带宽;非 MIG 场景用 MPS thread percentage + 限流近似。
-
延迟 SLO 监控:每个租户独立打 P99,越界自动触发扩容或迁移。
优先级队列 + 加权公平调度(WRR)
import heapq
class PriorityAdmitter:
def init(self):
self.queues = {0: [], 1: [], 2: []} # 0高 1中 2低def schedule(self): # 高优先发,中优按权重,低优有空才发 for level in (0, 1, 2): if self.queues[level]: return self.queues[level].pop(0) return None
六、调度器层:K8s Device Plugin 与公平性
在 K8s 上,MIG 通过 nvidia.com/mig-1g.10gb 这类扩展资源暴露,Volcano / 自研调度器按租户 namespace 做配额(ResourceQuota)和优先级(PriorityClass)。避免"一个租户 Pod 占满节点 MIG slice 导致别人 Pending"。
面试速答
问:MIG 和 MPS 有什么区别,什么时候用哪个?
答:MIG 是硬件物理切片,算力和显存都硬隔离,故障不传染,适合对外多租户强 SLA;MPS 是驱动级共享引擎,提利用率但不隔离,故障可能传染,适合内部同信任域复用。对外卖算力用 MIG,内部提效用 MPS。
问:一张卡跑多个模型,怎么防止 OOM 互相影响?
答:三层------硬件用 MIG 物理隔离显存;驱动/调度层给每实例设显存预算和 KV Cache 上限;节点层保留 8% HBM 余量防整卡 OOM。
问:噪声邻居怎么治?
答:优先级抢占 + 带宽/算力配额 + 每租户独立 P99 SLO 监控,越界自动限速、扩容或迁移。
高频追问清单
- MIG slice 之间能共享 KV Cache 吗?跨 slice 张量并行有什么限制?
- MPS 故障传染的根因是什么,生产上怎么规避?
- best-effort 租户和 SLA 租户混部,调度器怎么做加权公平?
- 显存预算按 tenant 还是按 model instance 计量更合理?长上下文突发怎么处理?
- K8s 上 MIG 资源如何暴露和做 namespace 配额?
- 节点 HBM 压力时,应该拒绝新请求还是抢占低优请求?怎么选?
- 租户级限流(QPS/并发)和 GPU 配额怎么联动?
- 单卡多模型场景下,NVLink 带宽争用怎么观测和隔离?