深色模式
GPU 利用率提升与共享
摘要:本文面向被"10 个服务各占 15% GPU 却要 10 张卡"困扰的平台团队。默认 Kubernetes 把 GPU 当作整数资源,一个 Pod 申请
nvidia.com/gpu: 1就独占整卡,轻量推理 / 嵌入 / ASR / TTS 模型常把利用率压到 0–10%。本文用 NVIDIA GPU Operator 的 MIG、Time-Slicing、MPS 三种共享机制打破 1:1 绑定,并给出度量与落地步骤。生产多租户优先 MIG,开发 / 低并发用 Time-Slicing,可信 CUDA 并发用 MPS。
适用版本与前提
- NVIDIA GPU Operator ≥ 1.11,NVIDIA Kubernetes Device Plugin ≥ 0.12.0(Time-Slicing API GA)。
- MIG 仅支持 Ampere 及以上(A100 / A30 / H100 / H200 / B200),其中 A100/H100/H200/B200 最多 7 个实例,A30 最多 4 个。
- Time-Slicing / MPS 可覆盖不支持 MIG 的旧卡(如 T4 / V100)。
核心概念:三种共享机制对比
| 机制 | 隔离性 | 最大分区 | 适用 |
|---|---|---|---|
| MIG | 硬件级显存 + 故障隔离 | A100/H100 7 个 | 生产多租户、需 SLA |
| Time-Slicing | 仅时间轮转,无隔离 | 任意 replicas | 开发、演示、低并发推理 |
| MPS | 虚拟地址隔离,无硬件错隔 | 48 进程(逻辑) | 可信同租户并发 |
Time-Slicing 没有内存与故障隔离
Time-Slicing 只是让多个 CUDA 进程轮流使用同一块卡,一个 Pod 的显存溢出(OOM)或非法访问可能拖垮同卡其他 Pod。生产多租户场景不要用它承担隔离责任,优先 MIG。请求多个 time-sliced GPU 不保证获得成比例算力,仅表示拿到一张被共享的卡。
生产实践一:Time-Slicing(最快上手)
通过 NVIDIA Device Plugin 的 ConfigMap 定义每张卡的共享副本数:
yaml
# time-slicing-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: time-slicing-config
namespace: gpu-operator
data:
a100-40gb: |
version: v1
sharing:
timeSlicing:
renameByDefault: true # 资源名变为 nvidia.com/gpu.shared
failRequestsGreaterThanOne: true # 禁止申请 >1 共享卡, 避免误解
resources:
- name: nvidia.com/gpu
replicas: 8 # 每张物理卡拆 8 个共享槽1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bash
kubectl create namespace gpu-operator 2>/dev/null || true
kubectl apply -f time-slicing-config.yaml
# 安装/重配置 GPU Operator 时挂载该 ConfigMap
helm upgrade nvdp nvdp/nvidia-device-plugin \
--namespace nvidia-device-plugin \
--set-file config.map.config=time-slicing-config.yaml1
2
3
4
5
6
2
3
4
5
6
Pod 申请一个共享槽(不再独占整卡):
yaml
# inference-light.yaml
spec:
containers:
- name: embed
resources:
limits:
nvidia.com/gpu.shared: 1 # 共享槽, 而非整卡1
2
3
4
5
6
7
2
3
4
5
6
7
生产实践二:MIG(生产推荐)
启用 MIG 后,device plugin 会把 MIG profile 暴露为独立资源名,如 nvidia.com/mig-1g.5gb:
bash
# 在节点上开启 MIG 模式(需该卡无运行负载)
sudo nvidia-smi -i 0 -mig 1
sudo nvidia-smi mig -i 0 -cgi 1g.5gb,1g.5gb,1g.5gb,1g.5gb -C # 创建4个 1g.5gb 实例1
2
3
2
3
yaml
# mig-pod.yaml —— 单租户拿到硬件隔离的小卡
spec:
containers:
- name: asr
resources:
limits:
nvidia.com/mig-1g.5gb: 1 # 1个SM切片 + 5GB显存, 故障隔离1
2
3
4
5
6
7
2
3
4
5
6
7
MIG profile 需提前规划
MIG 是静态硬件分区,profile(如 1g.5gb / 3g.20gb)必须提前规划;改 profile 通常要求该卡清空负载,属于运维事件而非运行时决策。切片过大浪费、过小挡住工作负载,先用真实显存峰值压测再定。
度量:用 DCGM-Exporter 量化利用率
任何优化都得有度量闭环,否则无法证明 ROI:
bash
# 部署 DCGM-Exporter 后, 查核心指标
kubectl get --raw /api/v1/nodes/<node>/proxy/metrics \
| grep -E "DCGM_FI_PROF_GPU_UTIL|DCGM_FI_DEV_MEM_COPY_UTIL"
# 本地快速看利用率/显存
nvidia-smi dmon -s u -d 1 # 算力利用率
nvidia-smi dmon -s m -d 1 # 显存利用率1
2
3
4
5
6
7
2
3
4
5
6
7
DCGM-Exporter 对 Time-Slicing 的限制
启用 Time-Slicing 时,DCGM-Exporter 无法把指标关联到具体容器(官方已知限制)。若需要 per-pod 度量,优先用 MIG(每个实例可独立度量),或在应用层用 vLLM / 框架自带 metrics 补足。
验证
bash
# 确认节点已暴露共享资源
kubectl get nodes -o json | jq '.items[].status.allocatable |
with_entries(select(.key|test("nvidia.com")))'
# 确认 Pod 落到共享槽而非整卡
kubectl describe pod <pod> | grep -A3 "nvidia.com"
# 期望 Limits: nvidia.com/gpu.shared: 11
2
3
4
5
6
7
2
3
4
5
6
7
回滚与清理
- Time-Slicing 调整 ConfigMap 后,Operator 不会自动热重载节点标签,需按官方"更新 Time-Slicing ConfigMap"流程滚动节点或重建 device plugin。
- 关闭 MIG 前必须排空该卡上所有 Pod:
kubectl drain <node> --ignore-daemonsets,再nvidia-smi mig -i 0 -dci && -dgi。 - 共享会放大"吵闹邻居"效应,回滚时优先保证高 SLA 服务回到独占卡。
故障排查
- 共享 Pod 调度不上去:检查
nvidia.com/gpu.shared资源是否真的被 GFD 打标签(renameByDefault 才生成.shared名)。 - MIG Pod Pending:profile 名拼写 / 该卡剩余 MIG 实例不足,用
nvidia-smi mig -lgi核对。 - 延迟抖动加剧:Time-Slicing 引入上下文切换开销,时延敏感服务改回独占或 MIG。
安全与合规
- 成本失控防护:共享提高密度,但一旦某个共享卡上的服务陷入无限循环推理,会拖垮同卡所有租户并持续烧钱。配合 LLMOps 成本视角 的 token 预算告警与限流做熔断。
- 多租户隔离:只有 MIG 提供硬件级故障隔离,监管 / 客户面服务应强制走 MIG 而非 Time-Slicing。
成本 / 性能权衡
- 合并轻量负载(ASR/TTS/嵌入)到单卡,可把集群密度提升数倍,等价于直接削减节点采购 / 实例账单。
- MIG 有约 5% 的硬件开销(切片边界资源不可跨用),但对 SLA 与隔离的收益远大于此。
- Time-Slicing 零硬件开销但引入延迟与隔离风险,仅适合可容忍抖动的负载。