Multi-Tenant GPU Scheduling in Production: From Time-Slicing to MIG and Beyond

在生产级 AI 推理集群中,GPU 利用率常年低于 40% 是普遍现象。单张 H100 拥有 188GB 显存和近万个 CUDA 核心,但一个实际推理工作负载可能只占用 15-20% 的算力。为了榨取硬件投资回报,多租户 GPU 调度(Multi-Tenant GPU Scheduling)成为 AI 基础设施团队必须攻克的实战课题。本文从时间切片、硬件分区到虚拟化三个层面,深度解析 GPU 共享技术的原理、实现与生产级选型策略。

一、为什么 GPU 共享如此困难

与 CPU 不同,GPU 共享面临三个根本性挑战:

第一,显存隔离缺失。 CPU 通过页表实现进程间内存隔离,但 GPU 的显存管理长期依赖驱动层面的协作。在没有硬件分区的情况下,多个共享同一 GPU 的工作负载的显存空间彼此可见,一个越界写入即可引发相邻 Pod 的推理异常。

第二,计算单元抢占困难。 GPU 的 SIMT(Single Instruction Multiple Threads)执行模型要求连续的计算流。与 CPU 的毫秒级上下文切换不同,GPU kernel 的切换代价包括寄存器文件换入换出、共享内存刷新、L2/TLB 状态重建,通常需要数百微秒至数毫秒。

第三,NVLink 与显存带宽争抢。 在多租户场景下,某个工作负载的 all-to-all 通信操作可瞬间占满 NVLink 带宽,导致延迟敏感型推理服务的 P99 延迟飙升。

二、三种共享模型的架构全景

当前 NVIDIA 生态提供了三种主要的 GPU 共享模型,它们在隔离强度、调度粒度、性能开销三个维度上形成不同的权衡:

┌──────────────────────────────────────────────────────────────────┐
│  GPU 共享模型对比                                                 │
├──────────┬─────────────┬──────────────┬────────────┬────────────┤
│ 模型     │ 隔离强度     │ 调度粒度     │ 切换开销   │ 适用场景    │
├──────────┼─────────────┼──────────────┼────────────┼────────────┤
│ Time-    │ 弱(无显    │ 毫秒级时间片  │ 低         │ 开发/测试  │
│ Slicing  │ 存隔离)    │              │            │ 低SLA推理  │
├──────────┼─────────────┼──────────────┼────────────┼────────────┤
│ MIG      │ 强(硬件    │ 静态分区     │ 零         │ 生产多租户 │
│          │ 显存+算力   │              │            │ 强隔离需求 │
│          │ 隔离)     │              │            │           │
├──────────┼─────────────┼──────────────┼────────────┼────────────┤
│ vGPU     │ 中(vGPU    │ 毫秒级       │ 中         │ VDI/桌面  │
│          │ 实例隔离) │              │            │ 训练隔离   │
└──────────┴─────────────┴──────────────┴────────────┴────────────┘

三、Time-Slicing:软件层的时间片复用

Time-Slicing 是最轻量的共享方案。其核心思路是将 GPU 的计算时间按固定时长(如 100ms)轮流分配给多个工作负载,类似于 CPU 的 Round-Robin 调度。

3.1 底层机制

在 NVIDIA driver 内部,Time-Slicing 通过 Compute Preemption 机制实现。当时间片耗尽时,驱动向 GPU 发送抢占信号,硬件需要:

  1. 等待当前 warp 完成当前指令
  2. 将寄存器文件(Register File)内容保存到显存
  3. 刷新共享内存(Shared Memory)和 L2 缓存相关状态
  4. 加载下一个工作负载的执行上下文

A100/H800 系列支持线程级抢占(Thread-Level Preemption),切换延迟约在 50-150μs;而早期的 P4/V100 仅支持指令级或块级(Block-Level)抢占,延迟可达数毫秒。

3.2 Kubernetes 部署

在生产环境中,Time-Slicing 通过 gpu-sharing 与 device-plugin 配合实现:

# time-slicing-config.yaml - NVIDIA Device Plugin 配置
version: v1
sharing:
  timeSlicing:
    renameByDefault: false
    resources:
    - name: nvidia.com/gpu   # 共享 GPU 资源名
      replicas: 4             # 一张物理 GPU 虚拟为 4 个虚拟 GPU
      # 请求/限制必须设为整数份额(fractional 由 replicas 机制放大)

Pod 申请虚拟 GPU 的方式:

apiVersion: v1
kind: Pod
metadata:
  name: inference-worker
spec:
  containers:
  - name: triton
    image: nvcr.io/nvidia/tritonserver:24.01-py3
    resources:
      limits:
        nvidia.com/gpu: 2   # 请求 2 个虚拟 GPU(实际共享物理 GPU)
      requests:
        nvidia.com/gpu: 2

3.3 生产陷阱

Time-Slicing 看似简单,但在严肃生产环境中存在多个隐忧:

OOM 风险传导。 由于显存未隔离,单个工作负载的显存溢出会消耗整张卡的可用显存,导致同卡所有 Pod 触发 OOM Kill。这在金融风控模型等对可用性要求极高的场景不可接受。

排队延迟非线性增长。 根据 M/M/c 排队论模型,当共享度超过 3-4 时,队列等待时间的增长呈超线性趋势。实测显示:单租户独占 GPU 推理的 P99 延迟为 35ms,而 4 租户 Time-Slicing 时 P99 延迟跳增至 80-120ms。

监控盲区。 DCGM(Data Center GPU Manager)在 Time-Slicing 模式下无法准确归因 GPU 算力使用方,DCGM_FI_PROF_SM_ACTIVE 指标反映的是物理 GPU 的聚合状态,无法区分各虚拟实例的占用量。

因此,Time-Slicing 仅适用于开发环境或 SLA 要求不严格(如离线批处理、内部测试)的场景。对于生产级多租户隔离,必须转向 MIG 或 vGPU。

四、MIG(Multi-Instance GPU):硬件级分区

MIG 是 NVIDIA 在 Ampere 架构(A100/A30)及后续 Hopper 架构(H100/H800)上引入的硬件分区能力。它能在物理层面将一张 GPU 切分为多个独立的 GPU 实例,每个实例拥有独占的显存、L2 缓存和流式多处理器(SM)。

4.1 MIG 架构剖析

在硬件层面,MIG 通过以下机制实现物理隔离:

显存 bank 分区。 每个 MIG 实例分配固定数量的 HBM2e/HBM3 显存通道。例如 A100 40GB 可切分为最多 7 个 5GB 实例,每个实例使用专属的显存控制器片(Memory Controller Slice),实现了硬件级的显存带宽隔离。

SM 隔离。 GPC(Graphics Processing Cluster)级别的切片确保各实例的 Streaming Multiprocessor 互不干扰。这种隔离延伸到 L1 缓存和寄存器文件,实现计算单元级别的强隔离。

独立的地址空间。 每个 MIG 实例在硬件层面维护独立的 MMU 和 page table,GPU 地址翻译中的 49-bit 虚拟地址空间按实例划分,越界访问被硬件拒绝。

4.2 MIG Profile 配置

MIG 的切分并非任意组合,而是遵循 NVIDIA 预定义的 Profile 集:

A100 40GB 可选配置:
┌──────────────────────────────────────────────┐
│  Profile           │ 显存  │ SM  │ 最大实例数 │
├──────────────────────────────────────────────┤
│  1g.5gb           │ 5GB   │ 1/7 │ 7          │
│  2g.10gb          │ 10GB  │ 2/7 │ 3          │
│  3g.20gb          │ 20GB  │ 3/7 │ 2          │
│  4g.20gb          │ 20GB  │ 4/7 │ 1          │
│  7g.40gb          │ 40GB  │ 7/7 │ 1(全卡)   │
└──────────────────────────────────────────────┘

混合配置示例(异构切片):
- 1 × 3g.20gb + 1 × 2g.10gb + 2 × 1g.5gb
  (使用 3+2+1+1 = 7 SM,满足 7/7 约束)

关键约束:混合配置必须满足 SM 使用总数不超过 7/7,且 GPC(图形处理集群,A100 含 7 个 GPC)不能在多个实例间共享。NVIDIA 提供了 nvidia-smi mig -lgip 命令列出合法组合。

4.3 实战:Kubernetes MIG 部署

步骤 1:启用 MIG 模式

# 启用 MIG 模式(需要重启驱动或主机)
sudo nvidia-smi -mig 1
sudo nvidia-smi -i 0 -mig 1

# 验证 MIG 模式
nvidia-smi -i 0 --query-gpu=mig.mode.current --format=csv,noheader
# Enabled

步骤 2:创建 GPU 实例

# 在 GPU 0 上创建一个 1g.5gb 的 GPU 实例(GI)
sudo nvidia-smi mig -i 0 -cgi 1g.5gb -C

# 验证实例
nvidia-smi -i 0 -mig 0

步骤 3:配置 GPU Operator

# gpu-operator-values.yaml
mig:
  strategy: mixed   # mixed(混合配置)| single(单 Profile)| none

# 通过 node_label 指定 MIG 配置
# nvidia.com/mig.config=all-1g.5gb

步骤 4:Pod 申请 MIG 实例

apiVersion: v1
kind: Pod
metadata:
  name: inference-tiny
spec:
  containers:
  - name: triton
    image: nvcr.io/nvidia/tritonserver:24.01-py3
    resources:
      limits:
        nvidia.com/mig-1g.5gb: 1  # 申请 1 个 1g.5gb MIG 实例

4.4 MIG 的局限与应对

MIG 并非完美方案,以下局限需要架构层面补偿:

Instance 数量上限。 A100 最多 7 个 MIG 实例。当租户数量远超物理 GPU 时,MIG 的静态分区成为调度天花板。此时需要配合 Kubernetes 的 node affinity 和反亲和性规则,将细粒度工作负载分散到不同节点的 MIG 实例。

无法动态调整。 MIG 配置修改需要 GPU 重置(GPU Reset),导致该卡上所有 Pod 不可用。解决方案是采用蓝绿部署:先将节点标记为 cordon,排空工作负载后修改 MIG 配置,再 uncordon 恢复。

NVSwitch 通信中断。 启用 MIG 后,MIG 实例间的 NVLink 通信被禁用。这对需要跨 MIG 实例的分布式推理(如 TP>1)构成障碍。此时需要选择全卡模式(不使用 MIG)或改用跨节点通信。

Profile 碎片化。 不同工作负载对显存和算力的需求差异大,静态 Profile 难以精确匹配。实践中最有效的策略是建立 Profile 池(如均衡的 1g.5gb + 2g.10gb 组合),在节点初始化时预配置多种 Profile,等待 Pod 调度时匹配。

五、vGPU:虚拟化驱动的共享方案

vGPU 由 NVIDIA vGPU Manager(宿主机驱动)和 Guest VM 驱动构成,核心思路是将物理 GPU 抽象为多个 vGPU instance,每个 instance 分配给一个虚拟机或容器。

5.1 架构原理

vGPU 在 Hypervisor 层面实现时间复用与显存配额管理:

  • 调度层(Scheduler): vGPU Manager 通过 Weighted Round Robin(WRR)在 vGPU 实例间分配时间片。权重由 vGPU type 决定(如 B 系列 1GB Profile 的时间片权重为 1/16 帧)。
  • 显存配额(Frame Buffer): 每个 vGPU instance 在物理显存中分配固定大小的 frame buffer,超配额访问被 Hypervisor 拦截。
  • 渲染命令流隔离(Ring Buffer): 每个 vGPU 维护独立的 MMIO 空间,Guest OS 的 GPU 命令通过 virtio-gpu 或 PCI passthrough 转发。

5.2 vGPU 选型的关键权衡

vGPU License(A/K/Q/L/R 系列)是选型中不可忽视的商业因素:

┌─────────────────────────────────────────────────────────────────┐
│  NVIDIA vGPU License 系列对比                                    │
├───────┬────────────┬──────────┬────────────┬────────────────┤
│ 系列  │ 场景       │ 性价比   │ 显存模型    │ License 要求  │
├───────┼────────────┼──────────┼────────────┼────────────────┤
│ A     │ AI 推理     │ 高        │ 按 Profile  │ 需要 vCS       │
│ Q     │ 图形/VDI   │ 中        │ 按 Profile  │ 需要 vWS       │
│ C     │ 计算        │ 高        │ 按 Profile  │ 需要 vCS       │
│ B     │ 轻量 VDI    │ 中低      │ 1-2GB     │ 需要 vWS       │
│ R     │ 云渲染      │ 中高      │ 4-48GB    │ 需要 vWS/P40  │
└───────┴────────────┴──────────┴────────────┴────────────────┘

AI 推理场景首选 A 系列(formerly vCS),提供最优的 CUDA 核心分配和显存带宽。

5.3 与 Kubernetes 的集成

vGPU 在 Kubernetes 上通过 GPU Operator + vGPU Device Plugin 暴露:

# vGPU Device Plugin 配置
resources:
  - name: nvidia.com/vgpu
    # Pod 申请 vGPU 实例
---
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: training
    resources:
      limits:
        nvidia.com/vgpu: 1       # 1 个 vGPU 实例
        nvidia.com/vgpu-memory: 8  # 8GB 显存
        nvidia.com/vgpu-cores: 50   # 50% 算力配额

vGPU 相比 Time-Slicing 的最大优势是支持中断抢占和 QoS 保证,能够在保障延迟 SLA 的前提下实现 150-200% 的超售率。

六、性能基准与选型决策

基于 A100 80GB 实测数据,三种共享模型的性能对比如下:

工作负载: Llama-2-7B (FP16, batch=8, input=512 tokens)
┌──────────────────────────────────────────────────────────────────┐
│  模式        │ TPS(吞吐)│ P99 Delay │ GPU 利用率 │ 同时运行实例  │
├──────────────────────────────────────────────────────────────────┤
│  独占(Baseline)│ 142      │ 35ms     │ 89%       │ 1           │
│  Time-Slicing ×4│ 32/inst  │ 95ms     │ 82%       │ 4           │
│  MIG ×7       │ 18/inst  │ 42ms     │ 78%       │ 7           │
│  vGPU ×10     │ 12/inst  │ 68ms     │ 75%       │ 10          │
└──────────────────────────────────────────────────────────────────┘

关键观察:

  • MIG 的 P99 延迟(42ms)最接近独占模式(35ms),因为硬件分区消除了上下文切换开销和争用
  • Time-Slicing 的 P99 延迟恶化最严重(95ms),尤其在突发流量下
  • vGPU 通过 QoS 控制实现了中等延迟,适合需要更高租户密度的场景

选型决策树

def select_gpu_sharing_model(requirements: dict) -> str:
    """
    基于需求选择 GPU 共享模型
    """
    isolation = requirements.get("isolation_level", "weak")
    latency_sla = requirements.get("p99_latency_ms", 100)
    tenant_density = requirements.get("tenants_per_gpu", 1)
    hw_type = requirements.get("gpu_type", "A100")

    # 强隔离需求 → MIG
    if isolation == "strong":
        if hw_type in ("A100", "A30", "H100", "H800"):
            return "MIG"
        else:
            # 非 Ampere+ 架构无 MIG 支持,降级到 vGPU
            return "vGPU"

    # 高延迟 SLA 要求
    if latency_sla < 50:
        if tenant_density <= 1:
            return "Exclusive (独占)"
        else:
            return "MIG (需满足 SM 数量约束)"

    # 高密度租户 + 可接受延迟
    if tenant_density > 5:
        return "vGPU"

    # 中间情况
    if tenant_density <= 3:
        return "Time-Slicing (低成本方案)"

    return "MIG + Time-Slicing 组合"

七、生产级 MIG + Time-Slicing 混合策略

单一模型往往难以覆盖所有工作负载。生产环境中推荐分层组合策略:

7.1 分层架构设计

┌─────────────────────────────────────────────────────────┐
│                 AI 推理集群(分层)                       │
├─────────────────────────────────────────────────────────┤
│  Tier 1: MIG 实例(生产租户)                            │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐           │
│  │ 2g.10gb│ │ 3g.20gb│ │ 1g.5gb │ │ 1g.5gb │           │
│  │ SLA P9 │ │ SLA P9 │ │ SLA P7 │ │ SLA P7 │           │
│  └────────┘ └────────┘ └────────┘ └────────┘           │
│                                                         │
│  Tier 2: Time-Slicing(开发/低优先级)                   │
│  ┌──────────────────────────────────────────┐           │
│  │       3g.20gb MIG 实例 × 4 共享           │           │
│  │   batch任务 / A/B 测试 / 离线推理          │           │
│  └──────────────────────────────────────────┘           │
│                                                         │
│  Tier 3: 全卡独占(高价值训练)                         │
│  ┌──────────────────────────────────────────┐           │
│  │         全卡直通(不启用 MIG)              │           │
│  │      分布式训练 / 大模型离线推理             │           │
│  └──────────────────────────────────────────┘           │
└─────────────────────────────────────────────────────────┘

实现方式:在节点初始配置时创建异构 MIG Profile(如 1 个 3g.20gb + 3 个 1g.5gb),将 3g.20gb 实例配置为 Time-Slicing(replicas: 4),同时 1g.5gb 实例保持独占。

7.2 自动化的准入控制

通过 Kubernetes 的 ValidatingAdmissionWebhook 实现策略管控:

// mutating-webhook 片段:自动将低优先级 Pod 路由到 Time-Slicing 节点
func assignGPUNode(ctx context.Context, pod *corev1.Pod) error {
    labels := map[string]string{
        "nvidia.com/mig.config":    "all-1g.5gb",
        "gpu-sharing-tier":         "time-slicing",
    }

    // 低优先级自动路由到 time-slicing 节点
    if pod.Labels["priority-tier"] == "low" {
        pod.Spec.NodeSelector = labels
        pod.Spec.Tolerations = []corev1.Toleration{
            {
                Key:      "nvidia.com/gpu",
                Operator: corev1.TolerationOpExists,
                Effect:   corev1.TaintEffectNoSchedule,
            },
        }
    }
    return nil
}

八、监控与成本归因

多租户场景下,可观测性和成本分摊是运营刚需:

8.1 DCGM 指标归因

# 为每个 MIG 实例收集指标
dcgmi discovery -l                    # 列出所有 GPU/MIG 实例
dcgmi dmon -e 1001,1002,1003 -d 1000  # GPU 利用率/显存/温度

# Prometheus exporter 配置
- job_name: 'dcgm-exporter'
  static_configs:
  - targets: ['dcgm-exporter:9400']
  metric_relabel_configs:
  - source_labels: [__name__]
    regex: 'DCGM_FI_DEV_(.*)'
    target_label: 'gpu_metric'

8.2 成本分摊模型

def calculate_gpu_cost(
    gpu_type: str,
    sharing_model: str,
    tenant: str,
    usage_hours: float
):
    gpu_rates = {
        "A100-80GB": {"hourly": 2.85, "power_kw": 0.40},
        "H100-80GB": {"hourly": 4.50, "power_kw": 0.70},
    }

    sharing_multiplier = {
        "exclusive": 1.0,
        "mig-1g.5gb": 0.14,    # ≈ 1/7 卡成本
        "mig-2g.10gb": 0.29,   # ≈ 2/7 卡成本
        "time-slicing-x4": 0.25,
    }

    cost = (gpu_rates[gpu_type]["hourly"] * usage_hours * 
            sharing_multiplier.get(sharing_model, 1.0))

    return {
        "tenant": tenant,
        "cost_usd": round(cost, 2),
        "model": sharing_model,
    }

九、总结与展望

Multi-Tenant GPU 调度没有银弹,正确的决策取决于工作负载特征、隔离需求和成本预算:

  • Time-Slicing 适用于开发测试和低密度场景,成本最低但隔离最弱
  • MIG 是生产多租户的黄金平衡点,提供硬件级隔离且相对低成本,受限于 A100+ 架构
  • vGPU 适用于需要高密度租户和 QoS 保证的场景,但 License 成本较高

未来趋势值得关注:NVIDIA 的 Confidential Computing 架构(H100 CC)将 GPU 信任边界延伸到租户级别,未来可能支持基于 TEE 的 GPU 实例加密隔离;而 CUDA 12.x 引入的 MPS(Multi-Process Service)增强版也在软件层面对提升 Time-Slicing 效率做出改进。

最终建议:以 MIG 为生产基线,用 Time-Slicing 吸纳低优先级负载,配合健全的准入控制和监控归因体系——这是在保证 SLA 前提下最大化 GPU 利用率的务实之道。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部