GPU 虚拟化与资源隔离——一张卡多人用 / GPU Virtualization and Resource Isolation
📅 创建时间:2026-06-03 🏷️ 标签:#AI-Infra #集群侧 #虚拟化 #MIG #MPS #GPU隔离 📚 前置知识:[[01-gpu-cluster-hardware]](GPU 集群硬件架构)[[04-job-scheduling]](作业调度) 📚 相关知识:[[05-multi-tenant-management]](多作业与多租户)[[../training-infra/01-gpu-hardware]](GPU 硬件基础)
场景:8 张 A100 卡,但有 20 个团队在排队
┌─────────────────────────────────────────────────────────────┐
│ │
│ 集群现状:8 张 A100,当前跑着一个大作业(占用 4 卡) │
│ 空闲资源:4 张 A100 │
│ │
│ 新需求队列: │
│ 团队 A:跑推理服务,申请 1 张卡,长期运行(数周) │
│ 团队 B:验证实验,申请 1 张卡,跑 30 分钟 │
│ 团队 C:微调小模型,申请 2 张卡,跑 2 小时 │
│ 团队 D:数据预处理,申请 1 张卡,跑 1 小时 │
│ │
│ 4 张卡 vs 5 个需求(其中 3 个总共才要 4 卡): │
│ │
│ 方案 1:让大作业释放 8 张卡,重新分配(耗时 30 分钟) │
│ 方案 2:4 张卡分给 3 个小团队,大作业继续跑 │
│ 方案 3:用虚拟化技术把 1 张卡分成多个实例 │
│ │
│ 方案 3 就是 GPU 虚拟化——让多作业共享同一张物理卡。 │
│ │
└─────────────────────────────────────────────────────────────┘第1节:GPU 虚拟化全景
为什么需要 GPU 虚拟化
┌─────────────────────────────────────────────────────────────┐
│ GPU 虚拟化的动机 │
├─────────────────────────────────────────────────────────────┤
│ │
│ GPU 利用率的典型问题: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 作业类型 │ 平均利用率 │ 利用率低的原因 │ │
│ │ 推理服务 │ 10-30% │ 推理主要等 IO │ │
│ │ 训练(Batch大)│ 60-80% │ 有优化空间 │ │
│ │ 训练(Batch小)│ 20-40% │ GPU 经常等数据 │ │
│ │ 验证/测试 │ 5-20% │ 只需快速跑一遍 │ │
│ │ 数据预处理 │ 0-10% │ 纯 CPU 操作 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 推理服务是虚拟化的最大受益者: │
│ → 一个推理服务往往只用到 GPU 的 10-20% 算力 │
│ → 如果独占一张 80GB A100,浪费 60-70% │
│ → 把 1 张卡分成 4 个推理实例,利用率接近 100% │
│ │
│ 虚拟化的代价: │
│ → 隔离带来的性能损耗(通常 5-15%) │
│ → 显存不共享(每个实例独占分区) │
│ → 配置复杂度增加 │
│ │
└─────────────────────────────────────────────────────────────┘三种虚拟化方案对比
┌─────────────────────────────────────────────────────────────┐
│ GPU 虚拟化三大方案对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ │ 方案 │ 技术层面 │ 粒度 │ 适用场景 │
│ ├─────────────┼────────────┼────────────┼─────────────────┤
│ │ CUDA MPS │ 软件 │ 进程级 │ 小规模推理 │
│ │ │ (用户态) │ 共享算力 │ / 多作业测试 │
│ ├─────────────┼────────────┼────────────┼─────────────────┤
│ │ MIG │ 硬件 │ 7 实例/卡 │ 有硬件要求 │
│ │ │ (A100+) │ 固定分区 │ / 多租户生产 │
│ ├─────────────┼────────────┼────────────┼─────────────────┤
│ │ vGPU │ 软件+驱动 │ 任意粒度 │ 虚拟化环境 │
│ │ (Grid/NP) │ (厂商提供)│ 软件定义 │ / 远程渲染 │
│ │
│ 选择原则: │
│ → A100/H100 优先 MIG(硬件隔离,性能最好) │
│ → 老卡(V100)或需要灵活分区 → CUDA MPS │
│ → 虚拟化平台(VMware/KVM)→ vGPU │
│ │
└─────────────────────────────────────────────────────────────┘第2节:CUDA MPS(Multi-Process Service)
MPS 工作原理
┌─────────────────────────────────────────────────────────────┐
│ CUDA MPS 架构图 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统模式(无 MPS): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GPU ┌───┐ │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ │ SM │ │ │
│ │ │ProcessA│ │ProcessB│ │ProcessC│ │ x4 │ │ │
│ │ │ CUDA │ │ CUDA │ │ CUDA │ │ │ │ │
│ │ │ Context│ │ Context│ │ Context│ └───┘ │ │
│ │ └────────┘ └────────┘ └────────┘ │ │
│ │ 问题:每个进程独立 CUDA Context,上下文切换开销大 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ MPS 模式: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GPU ┌───┐ │ │
│ │ ┌──────────┐ │ SM │ │ │
│ │ │ MPS Server│←── 单一共享 CUDA Context │ x4 │ │ │
│ │ │ (共享进程)│ ┌────────┐ ┌────────┐ │ │ │ │
│ │ └────┬─────┘ │ProcessA│ │ProcessB│ └───┘ │ │
│ │ │ │(瘦客户)│ │(瘦客户)│ │ │
│ │ │ └────────┘ └────────┘ │ │
│ │ 单一 Context → 并发执行 → GPU 利用率更高 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ MPS 的限制: │
│ → 不支持显存隔离(所有进程共享完整 HBM) │
│ → 一个进程 OOM 会影响所有进程 │
│ → 不支持 UVA(Unified Virtual Addressing)共享 │
│ │
└─────────────────────────────────────────────────────────────┘MPS 配置实战
bash
# 1. 启用 MPS(每台 GPU 节点)
nvidia-cuda-mps-control -d
# 2. 设置 MPS 控制线程的 GPU 利用率上限(避免过度竞争)
echo "set_default_active_thread_percentage 50" | nvidia-cuda-mps-control
# 解释:限制 MPS 下所有进程的 GPU 占用上限为 50%
# 3. 针对特定进程设置上限
echo "set_active_thread_percentage -pid 12345 75" | nvidia-cuda-mps-control
# 4. 查看 MPS 状态
cat /proc/driver/nvidia-mps/control_dog_fid0/stats
# 5. 提交作业时指定 MPS
CUDA_VISIBLE_DEVICES=0 srun --gres=gpu:1 python inference.py
# 在代码中:os.environ["CUDA_MPS_ACTIVE_THREAD_PERCENTAGE"] = "25"
# 6. 关闭 MPS
killall nvidia-cuda-mps-controlpython
# Python 中使用 MPS
import os
# 启用 MPS
os.environ["CUDA_VISIBLE_DEVICES"] = "0" # 指定 GPU 0
# 设置该进程的 GPU 利用率上限(1-100)
os.environ["CUDA_MPS_ACTIVE_THREAD_PERCENTAGE"] = "50"
# 之后正常的 PyTorch 代码
import torch
model = torch.nn.Linear(10, 10).cuda()
# 这个进程最多使用 GPU 的 50% 算力MPS 的适用与不适用
┌─────────────────────────────────────────────────────────────┐
│ MPS 适用场景分析 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ 适合 MPS 的场景: │
│ → 多个轻量推理服务共享一张卡(利用率 10% × 5 = 50%) │
│ → 多进程训练(小模型,数据并行,每个进程 20% 算力) │
│ → 开发/测试环境:多个实验同时跑 │
│ → 资源预留:保留一张卡给紧急任务,同时给普通任务用 MPS │
│ │
│ ❌ 不适合 MPS 的场景: │
│ → 大模型训练(显存不够,需要完整 GPU) │
│ → 显存敏感作业(MPS 不隔离显存,一个 OOM 全挂) │
│ → 高性能推理服务(延迟敏感,MPS 调度开销不可接受) │
│ → 推理和训练混合(训练算力高,会抢推理的 GPU 时间) │
│ │
│ ⚠️ 使用注意: │
│ → 监控显存使用:nvidia-smi dmon -s u ← 显存总量 │
│ → 不像 MIG 有硬件级隔离,MPS 是纯软件隔离 │
│ → 建议结合 cgroup 限制每个进程的显存上限 │
│ │
└─────────────────────────────────────────────────────────────┘第3节:MIG(Multi-Instance GPU)
MIG 的硬件基础
┌─────────────────────────────────────────────────────────────┐
│ A100 MIG 硬件分区 │
├─────────────────────────────────────────────────────────────┤
│ │
│ A100 完整规格: │
│ → 108 个 SM 中的 108 个(100% 算力) │
│ → 80 GB HBM(100% 显存) │
│ │
│ MIG 支持的分区模式(7 种): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 模式 │ GPU Slice │ SM │ 显存 │ 名字后缀 │ │
│ │ ────────┼────────────┼──────┼────────┼──────────── │ │
│ │ 1g.10gb │ 1/7 │ 1/7 │ 10 GB │ .10gb │ │
│ │ 2g.20gb │ 2/7 │ 2/7 │ 20 GB │ .20gb │ │
│ │ 3g.40gb │ 3/7 │ 3/7 │ 40 GB │ .40gb │ │
│ │ 4g.40gb │ 4/7 │ 4/7 │ 40 GB │ .40gb │ │
│ │ 7g.80gb │ 7/7 │ 7/7 │ 80 GB │ .80gb │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 关键限制: │
│ → 最多同时创建 7 个 MIG 实例(因为 A100 有 7 个 GPC) │
│ → 分区必须是 GPU Slice 的整数倍 │
│ → 分区一旦创建,无法动态调整 │
│ │
│ H100 MIG 改进: │
│ → H100 有 8 个 GPC,支持更灵活的分区 │
│ → H100 MIG 支持动态分区(运行时可重新分区) │
│ │
└─────────────────────────────────────────────────────────────┘MIG 配置实战
bash
# 1. 确认 GPU 支持 MIG(A100 或 H100)
nvidia-smi --query-gpu=name,mig.mode.current --format=csv
# 2. 启用 MIG 模式(需要 root)
sudo nvidia-smi -mig 1
# 3. 查看可用 MIG 配置
nvidia-smi -L
# 输出示例:
# GPU 0: NVIDIA A100-SXM4-80GB
# MIG 1g.20gb Device 0: MIG 1g.20gb
# MIG 2g.40gb Device 1: MIG 2g.40gb
# MIG 3g.40gb Device 2: MIG 3g.40gb
# 4. 创建自定义 MIG 实例(以 A100 为例)
sudo nvidia-smi -i 0 -mig 1
sudo nvidia-smi mig -i 0 -cgi 19,19,19
# cgi 参数:19 = 3g.40gb 模式(3 slices)
# 创建 3 个 3g.40gb 实例(总占用 9 slices,剩余 4 slices)
# 5. 查看创建的 MIG 设备
nvidia-smi -L
# GPU 0: NVIDIA A100-SXM4-80GB
# MIG 3g.40gb Device 0: MIG 3g.40gb
# MIG 3g.40gb Device 1: MIG 3g.40gb
# MIG 3g.40gb Device 2: MIG 3g.40gb
# (剩余 4 slices 未使用)
# 6. 在 MIG 设备上运行作业
CUDA_VISIBLE_DEVICES=MIG-3g.40gb-0 python train.py
# 或
CUDA_VISIBLE_DEVICES=1 python inference.py # Device 1 = 第二个 MIG 实例
# 7. 删除 MIG 实例
sudo nvidia-smi mig -i 0 -dgi -gi 0 # 删除 Device 0Kubernetes 中使用 MIG
yaml
# 使用 NVIDIA GPU Operator 自动配置 MIG
# 1. 部署 GPU Operator(包含 MIG 策略配置)
apiVersion: v1
kind: ConfigMap
metadata:
name: nvidia-mig-parted-config
namespace: gpu-operator
data:
config.yaml: |
version: v1
shared-devices:
- device: all
mode: ["3g.20gb"] # 所有节点统一使用 3g.20gb 模式
mig-geometries:
0:
A100-SXM4-80GB:
3g.20gb:
- devices: [0, 1, 2, 3, 4, 5, 6]yaml
# 用户提交 MIG 作业
apiVersion: v1
kind: Pod
metadata:
name: inference-mig-pod
spec:
containers:
- name: inference
image: vllm/vllm:latest
resources:
limits:
nvidia.com/gpu: "3g.20gb" # 申请 3g.20gb MIG 实例
env:
- name: CUDA_VISIBLE_DEVICES
value: "0" # MIG 设备映射到 CUDA 设备 0MIG vs MPS 深度对比
┌─────────────────────────────────────────────────────────────┐
│ MIG vs MPS 深度对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ │ 维度 │ MIG │ MPS │
│ ├─────────────────┼─────────────────────────┼──────────────────┤
│ │ 隔离层级 │ 硬件(SM 级别) │ 软件(CUDA Context)│
│ │ 显存隔离 │ ✅ 硬隔离(固定分区) │ ❌ 共享(无隔离) │
│ │ 算力隔离 │ ✅ 硬隔离(SM 固定分配)│ ❌ 软件限制(不完美)│
│ │ 故障隔离 │ ✅ 一个 OOM 不影响其他 │ ❌ 一个 OOM 全挂 │
│ │ 性能干扰 │ ✅ 极低(硬件保证) │ ✅ 较低(但无保障)│
│ │ 动态调整 │ ❌ 静态(需重建) │ ✅ 动态(随时改) │
│ │ 支持硬件 │ A100 / H100 / H200 │ 所有 NVIDIA GPU │
│ │ 灵活性 │ 低(固定分区模式) │ 高(任意进程数) │
│ │ NCCL 兼容性 │ ✅ 全支持 │ ⚠️ 有限支持 │
│ │ 配置复杂度 │ 高(驱动层配置) │ 低(用户态) │
│ │ 适用场景 │ 生产多租户 │ 开发/推理/测试 │
│ │
│ 实践建议: │
│ → 生产多租户环境:MIG(隔离可靠) │
│ → 开发测试 + 灵活实验:MPS(配置简单) │
│ → 可以同时使用:MIG 承载推理服务,MPS 承载测试实验 │
│ │
└─────────────────────────────────────────────────────────────┘第4节:vGPU(Grid / NVIDIA Virtual GPU)
vGPU 的应用场景
┌─────────────────────────────────────────────────────────────┐
│ vGPU vs MIG 的定位差异 │
├─────────────────────────────────────────────────────────────┤
│ │
│ vGPU(Grid / Virtual GPU): │
│ → 适用:虚拟化平台(VMware ESXi、KVM、Hyper-V) │
│ → 场景:远程图形工作站、AI 推理服务的虚拟化部署 │
│ → 特点:驱动层虚拟化,粒度可自定义,软件定义 │
│ → 授权:需要 NVIDIA Virtual GPU License │
│ │
│ MIG: │
│ → 适用:裸金属服务器(Bare Metal) │
│ → 场景:HPC 集群、生产推理服务 │
│ → 特点:硬件级隔离,A100+ 原生支持 │
│ → 授权:无额外授权费用 │
│ │
│ 典型部署场景: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Bare Metal + 训练 → MIG(性能最优) │ │
│ │ Bare Metal + 推理 → MIG 或 MPS │ │
│ │ VM/Kubernetes + GPU → vGPU(MIG 不支持虚拟化) │ │
│ │ 远程图形/工作站 → vGPU(Quadro Virtual) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘vGPU 配置概要
bash
# vGPU Manager 安装(VMware/KVM 主机端)
# 1. 下载 NVIDIA vGPU Manager .vib 或 .deb 文件
sudo vmware-installer -u vmware-esx-vgpu-manager-16.x.x
# 2. 在 vCenter 中创建 vGPU 配置文件
# vSphere Client → Host → Configure → Graphics → vGPU Manager
# 3. vGPU 配置文件选择(A100 示例)
# 16GB : 1 vGPU, 2 vCPUs, 4GB FB
# 40GB : 2 vGPU, 4 vCPUs, 20GB FB
# 80GB : 4 vGPU, 8 vCPUs, 20GB FB
# 4. VM 中使用 vGPU(安装 vGPU Driver)
# VM 需要安装 NVIDIA vGPU Guest Driver(不是普通驱动)
# 5. KVM 下使用 vGPU(libvirt 配置)
# <hostdev mode='subsystem' type='mdev' model='nvidia-ggy'>
# <source>
# <address uuid='xxx'/>
# </source>
# </hostdev>第5节:虚拟化与调度系统的集成
调度器如何感知虚拟化
┌─────────────────────────────────────────────────────────────┐
│ GPU 虚拟化与调度的集成 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 问题:调度器如何知道哪些 GPU 是 MIG 实例? │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ nvidia-smi 输出(MIG 模式下): │ │
│ │ $ nvidia-smi -L │ │
│ │ GPU 0: NVIDIA A100-SXM4-80GB (UUID: xxx) │ │
│ │ MIG 1g.20gb Device 0: MIG 1g.20gb │ │
│ │ MIG 1g.20gb Device 1: MIG 1g.20gb │ │
│ │ MIG 2g.40gb Device 2: MIG 2g.40gb │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Kubernetes Device Plugin(识别 MIG): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ # GPU Operator 1.10+ 自动探测 MIG 设备 │ │
│ │ $ kubectl get nodes -o jsonpath='{.items[*].status.allocatable}'│ │
│ │ # 输出中包含 nvidia.com/gpu: 1g.20gb=3 等字段 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Slurm(识别 MIG): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ # gres.conf 中声明 MIG 设备 │ │
│ │ NodeName=node[1-4] File=/dev/nvidia[0-3] │ │
│ │ GresTypes=gpu,mig │ │
│ │ # 作业请求特定 MIG 类型 │ │
│ │ $ srun --gres=gpu:a100:1g.20gb:2 ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘升华:共享是效率与安全的博弈
┌─────────────────────────────────────────────────────────────┐
│ 虚拟化的工程哲学 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 隔离和效率是对立的目标: │
│ → 完全隔离(独占)= 最高性能,但最低利用率 │
│ → 完全共享(MPS)= 最高利用率,但性能互相干扰 │
│ → MIG 是两者之间的平衡点(硬件级隔离 + 合理分区) │
│ │
│ 2. 虚拟化方案的选择就是业务场景的选择: │
│ → 生产多租户:必须 MIG(故障隔离不可妥协) │
│ → 开发测试:MPS(灵活性和配置简单更重要) │
│ → 虚拟化基础设施:vGPU(平台决定) │
│ │
│ 3. 没有免费的午餐: │
│ → 任何虚拟化都有性能损耗(通常 5-15%) │
│ → 评估是否值得:5% 性能换 4 倍利用率是否合算 │
│ │
│ 一句话总结: │
│ GPU 虚拟化是把"一张卡"变成"多张虚拟卡"的技术, │
│ 选择哪种方案,本质上是在隔离强度、灵活性和性能之间做取舍。 │
│ │
└─────────────────────────────────────────────────────────────┘"AI 可查 vs 必须理解"清单
AI 可查:
✅ nvidia-smi mig 命令的具体参数
✅ vGPU Manager 的安装和配置步骤
✅ Kubernetes GPU Operator 的 MIG 配置 YAML
✅ 不同 MIG 配置文件(A100/H100)的详细参数
必须理解:
🔴 MPS、MIG、vGPU 三大方案的核心差异(硬件隔离 vs 软件隔离)
🔴 为什么推理服务是虚拟化的最大受益者
🔴 MIG 的硬件分区机制(A100 7 slices 的分区模式)
🔴 MIG vs MPS 的选型依据(生产 vs 开发)
🔴 虚拟化与调度系统的集成方式(调度器如何感知 MIG 设备)学习状态:🟡 开始学习