Kubernetes GPU 调度与 AI 工作负载运维
K8s GPU 调度全解:GPU Operator 部署、MIG 分片与 Time-slicing 共享策略、vLLM 推理服务部署、NCCL 排障与成本优化。
1. GPU 资源在 K8s 中的表示
与传统资源的差异
| 维度 | CPU / 内存 | GPU |
|---|---|---|
| 调度资源名 | cpu, memory | nvidia.com/gpu |
| 细粒度分配 | requests: 250m(0.25核) | 只能整卡分配(1, 2, 4…) |
| 超卖 | 天然支持(limits > requests) | 不支持超卖(整卡独占) |
| OOM 行为 | OOMKill | CUDA OOM → 进程崩溃 |
| 监控指标 | usage/requests/limits | 额外需 GPU 利用率、显存、温度 |
基础 GPU Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: gpu-inference
spec:
containers:
- name: vllm-server
image: vllm/vllm-openai:latest
resources:
limits:
nvidia.com/gpu: 1 # 申请 1 张 GPU
env:
- name: CUDA_VISIBLE_DEVICES
value: "0" # 指定使用 GPU 0
GPU 请求的三种写法
# 方式1:只设 limits(等同于 requests,Guaranteed QoS)
resources:
limits:
nvidia.com/gpu: 2
# 方式2:同时设 requests 和 limits(必须相等,GPU 不支持超卖)
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
# 方式3:MIG 设备(见第4节)
resources:
limits:
nvidia.com/mig-1g.5gb: 1 # 申请 1 个 1g.5gb MIG 实例
2. NVIDIA GPU Operator 部署
组件架构
NVIDIA GPU Operator
├── nvidia-driver-daemonset # 自动安装/管理 GPU 驱动
├── nvidia-container-toolkit # 容器运行时 GPU 支持
├── nvidia-device-plugin # 向 K8s 暴露 GPU 资源
├── nvidia-dcgm-exporter # GPU 监控指标导出
├── gpu-feature-discovery # 节点 GPU 标签自动发现
└── nvidia-mig-manager # MIG 分区管理(A100/H100)
Helm 安装
# 添加 Helm repo
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
# 安装 GPU Operator(推荐配置)
helm install gpu-operator nvidia/gpu-operator \
--namespace gpu-operator \
--create-namespace \
--set driver.enabled=true \
--set toolkit.enabled=true \
--set devicePlugin.enabled=true \
--set dcgmExporter.enabled=true \
--set migManager.enabled=true \
--set node-feature-discovery.enabled=true
验证
# 检查 GPU Operator 组件
kubectl get pods -n gpu-operator
# 检查节点 GPU 资源
kubectl describe node <gpu-node> | grep nvidia
# 预期输出:
# nvidia.com/gpu: 8
# nvidia.com/gpu: 8
# nvidia.com/mig-1g.5gb: 7 (如果启用了 MIG)
# 验证 device plugin 工作
kubectl logs -n gpu-operator -l app=nvidia-device-plugin
# 运行 GPU 测试 Pod
kubectl run gpu-test --rm -it --restart=Never \
--image=nvidia/cuda:12.4.0-runtime-ubuntu22.04 \
--limits nvidia.com/gpu=1 \
-- nvidia-smi
3. GPU 节点调度策略
3.1 节点标签自动发现
GPU Operator 会自动给 GPU 节点打标签:
# 查看自动标签
kubectl get nodes -o json | jq '.items[].metadata.labels' | grep nvidia
# 常见自动标签:
# nvidia.com/gpu.product: NVIDIA-A100-SXM4-40GB
# nvidia.com/gpu.count: 8
# nvidia.com/gpu.memory: 40960 (MB)
# nvidia.com/gpu.family: ampere
# nvidia.com/cuda.driver.major: 535
3.2 GPU 节点隔离(Taint + Toleration)
# 给 GPU 节点打污点,防止普通 Pod 调度上来
kubectl taint nodes gpu-node-1 nvidia.com/gpu=true:NoSchedule
kubectl taint nodes gpu-node-2 nvidia.com/gpu=true:NoSchedule
AI 工作负载加 Toleration:
spec:
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
3.3 NodeAffinity 按 GPU 型号调度
# 调度到 A100 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values:
- NVIDIA-A100-SXM4-40GB
- NVIDIA-A100-SXM4-80GB
# 或者调度到任何有 GPU 的节点
- matchExpressions:
- key: nvidia.com/gpu.present
operator: In
values: ["true"]
3.4 GPU 拓扑感知调度
# 保证多 GPU Pod 分配到同一 NUMA 节点(NVLink 互联)
spec:
containers:
- name: training
resources:
limits:
nvidia.com/gpu: 4
# GPU Operator 会自动处理拓扑感知
# 但需确认集群安装了 NVIDIA K8s Topology Scheduler
4. GPU 共享策略
四种共享方案对比
| 方案 | 粒度 | 隔离性 | 显存隔离 | 算力隔离 | 适用场景 |
|---|---|---|---|---|---|
| 整卡独占 | 1 GPU | 强 | ✅ | ✅ | 训练、高负载推理 |
| MIG | 1/7 GPU | 强 | ✅ 硬隔离 | ✅ 硬隔离 | 多租户推理(仅 A100/A30/H100) |
| Time-slicing | 时间片 | 弱 | ❌ 共享 | ❌ 共享 | 开发测试、低负载推理 |
| MPS | 进程级 | 弱 | ❌ 共享 | ❌ 共享 | 同用户多进程(安全可控环境) |
4.1 MIG(Multi-Instance GPU)— 推荐
仅适用于 A100、A30、H100。将一张物理 GPU 切分为多个独立实例:
# 在 GPU 节点上启用 MIG
nvidia-smi mig -cgi 9,9,9,9,9,9,9 -C # A100-40GB: 切成 7 个 1g.5gb
# 或
nvidia-smi mig -cgi 19,19,19 -C # A100-80GB: 切成 3 个 2g.20gb
# 查看 MIG 实例
nvidia-smi mig -lgi
K8s 中使用 MIG 实例:
# 申请 1 个 MIG 1g.5gb 实例(而不是整卡)
resources:
limits:
nvidia.com/mig-1g.5gb: 1
# 申请 1 个 MIG 2g.20gb 实例
resources:
limits:
nvidia.com/mig-2g.20gb: 1
4.2 Time-slicing(GPU 时分复用)
适用于开发环境,让多个 Pod 共享一张 GPU:
# device-plugin-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: time-slicing-config
namespace: gpu-operator
data:
any: |-
version: v1
flags:
migStrategy: none
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4 # 1 张物理 GPU → 4 个逻辑 GPU
# 应用配置
kubectl apply -f time-slicing-config.yaml
# 修改 GPU Operator 使用 time-slicing
kubectl patch clusterpolicy/cluster-policy -n gpu-operator \
--type merge -p '{"spec":{"devicePlugin":{"config":{"name":"time-slicing-config"}}}}'
4.3 场景选择决策树
生产推理多租户 → A100/H100 → MIG
↓ 非 MIG 卡
生产推理高负载 → 整卡独占
↓
开发/测试低负载 → Time-slicing
↓
训练任务 → 整卡独占 + NCCL/NVLink
5. GPU 监控与可观测性
5.1 DCGM Exporter 指标
# 查看 DCGM 暴露的指标
curl -s http://<gpu-node>:9400/metrics | grep DCGM
# 核心 GPU 指标
DCGM_FI_DEV_GPU_UTIL # GPU 利用率(%)
DCGM_FI_DEV_MEM_COPY_UTIL # 显存带宽利用率(%)
DCGM_FI_DEV_FB_USED # 已用显存(MB)
DCGM_FI_DEV_FB_FREE # 可用显存(MB)
DCGM_FI_DEV_GPU_TEMP # GPU 温度(℃)
DCGM_FI_DEV_POWER_USAGE # 功耗(W)
DCGM_FI_DEV_XID_ERRORS # XID 错误计数(硬件故障信号)
DCGM_FI_DEV_ECC_FAILURE_COUNT # ECC 错误计数
5.2 Prometheus 告警规则
# GPU 运维必配告警
groups:
- name: gpu_alerts
rules:
# GPU 不可用
- alert: GPUUnavailable
expr: absent(DCGM_FI_DEV_GPU_UTIL) == 1
for: 2m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.node }} GPU {{ $labels.gpu }} 指标消失"
# GPU 温度过高(A100 降频阈值 85°C)
- alert: GPUHighTemperature
expr: DCGM_FI_DEV_GPU_TEMP > 85
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} 温度 {{ $value }}°C"
# GPU ECC 错误(硬件故障前兆)
- alert: GPUECCErrors
expr: rate(DCGM_FI_DEV_ECC_FAILURE_COUNT[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} 检测到 ECC 错误"
# GPU XID 错误
- alert: GPUXIDError
expr: increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} XID 错误: {{ $value }}"
# 显存使用率 > 90%
- alert: GPUHighMemoryUsage
expr: (DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} 显存使用率 {{ $value | humanizePercentage }}"
# GPU 利用率过低(可能是 Pod 挂了但 GPU 还占着)
- alert: GPUIdleHighMemory
expr: (DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)) > 0.5
and DCGM_FI_DEV_GPU_UTIL < 10
for: 30m
labels:
severity: info
annotations:
summary: "GPU {{ $labels.gpu }} 显存占用高但利用率低(僵尸 Pod?)"
5.3 GPU 排障命令速查
# === 节点层面 ===
nvidia-smi # GPU 概览:利用率、显存、进程
nvidia-smi topo -m # GPU 拓扑(NVLink 互联拓扑)
nvidia-smi dmon -s pucvmet -c 10 # 实时监控(功耗/利用率/时钟/显存/温度)
nvidia-smi -q -d HEALTH # GPU 健康状态
# === 容器层面 ===
# 查看哪个 Pod 在使用 GPU 0
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.containers[].resources.limits."nvidia.com/gpu" != null) | {ns: .metadata.namespace, name: .metadata.name, node: .spec.nodeName}'
# 查看节点 GPU 分配情况
kubectl describe node <gpu-node> | grep -A5 "Allocated resources"
# nvidia.com/gpu 4 (50%) 4 (50%) 4 4
# === DCGM 诊断 ===
# GPU 错误注入诊断工具
dcgmi diag -r 1 # Level 1 诊断(快速)
dcgmi diag -r 3 # Level 3 诊断(深度,含显存测试)
# 查看 XID 错误详细
nvidia-smi -q -d XID
6. AI 推理服务部署
6.1 vLLM — 生产推理首选
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-qwen
spec:
replicas: 2
selector:
matchLabels:
app: vllm-qwen
template:
metadata:
labels:
app: vllm-qwen
spec:
nodeSelector:
nvidia.com/gpu.product: NVIDIA-A100-SXM4-40GB # 指定 GPU 型号
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
containers:
- name: vllm
image: vllm/vllm-openai:v0.6.3
args:
- --model
- "Qwen/Qwen2.5-7B-Instruct"
- --tensor-parallel-size
- "1" # 单卡推理
- --max-model-len
- "8192"
- --gpu-memory-utilization
- "0.90" # 显存利用率上限
- --max-num-seqs
- "256" # 最大并发请求数
ports:
- containerPort: 8000
name: http
env:
- name: CUDA_VISIBLE_DEVICES
value: "0" # 只使用 GPU 0
resources:
limits:
nvidia.com/gpu: 1
memory: 40Gi # GPU 节点通常内存也大
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 60
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 120
periodSeconds: 30
apiVersion: v1
kind: Service
metadata:
name: vllm-qwen
spec:
selector:
app: vllm-qwen
ports:
- port: 8000
targetPort: 8000
6.2 vLLM 关键参数速查
| 参数 | 说明 | 建议值 |
|---|---|---|
--tensor-parallel-size | 张量并行(模型切分到多GPU) | GPU数量(需要 NVLink) |
--gpu-memory-utilization | 显存利用率上限 | 0.85-0.90 |
--max-num-seqs | 最大并发序列 | GPU显存 / 每请求估算显存 |
--max-model-len | 最大上下文长度 | 根据业务需求 |
--enforce-eager | 禁用 CUDA Graph | 调试用,生产关闭 |
--dtype | 推理精度 | auto(默认bf16/fp16) |
6.3 推理服务 HPA 自动扩缩容
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-qwen-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-qwen
minReplicas: 1
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: vllm_request_latency_p99 # 自定义指标(需 Prometheus Adapter)
target:
type: AverageValue
averageValue: "2" # P99 < 2s
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 300
6.4 TGI(Text Generation Inference)对比
# HuggingFace TGI 的差异点
containers:
- name: tgi
image: ghcr.io/huggingface/text-generation-inference:2.3
args:
- --model-id
- "Qwen/Qwen2.5-7B-Instruct"
- --num-shard # TGI 用 --num-shard(等同于 vLLM tensor-parallel-size)
- "1"
- --max-total-tokens
- "8192"
- --max-input-length
- "4095"
- --max-batch-prefill-tokens
- "8192"
env:
- name: CUDA_VISIBLE_DEVICES
value: "0"
- name: HUGGING_FACE_HUB_TOKEN # 下载 gated 模型需要
valueFrom:
secretKeyRef:
name: hf-token
key: token
| 维度 | vLLM | TGI |
|---|---|---|
| PagedAttention | ✅ | ✅ |
| 量化支持 | AWQ/GPTQ/FP8 | GPTQ/bitsandbytes |
| 并发性能 | 高 | 中 |
| 生态 | 社区驱动 | HuggingFace官方 |
| 推荐 | 生产首选 | HuggingFace 生态深度用户 |
7. AI 训练任务
7.1 PyTorchJob(Kubeflow Training Operator)
apiVersion: "kubeflow.org/v1"
kind: PyTorchJob
metadata:
name: llama-finetune
spec:
runPolicy:
cleanPodPolicy: All
ttlSecondsAfterFinished: 3600
pytorchReplicaSpecs:
Master:
replicas: 1
restartPolicy: OnFailure
template:
spec:
nodeSelector:
nvidia.com/gpu.product: NVIDIA-A100-SXM4-80GB
containers:
- name: pytorch
image: pytorch/pytorch:2.4.0-cuda12.4-cudnn9-devel
command:
- torchrun
- --nnodes=1
- --nproc_per_node=8
- train.py
resources:
limits:
nvidia.com/gpu: 8
memory: 512Gi
volumeMounts:
- name: dataset
mountPath: /data
- name: checkpoints
mountPath: /checkpoints
env:
- name: NCCL_DEBUG
value: "INFO"
- name: NCCL_SOCKET_IFNAME
value: "eth0"
volumes:
- name: dataset
persistentVolumeClaim:
claimName: training-dataset-pvc
- name: checkpoints
persistentVolumeClaim:
claimName: checkpoints-pvc
Worker:
replicas: 1
restartPolicy: OnFailure
template:
# ... 同上配置(多节点训练)
7.2 训练任务 NCCL 排障
# NCCL 常见问题排查
export NCCL_DEBUG=INFO # 打印详细日志
export NCCL_DEBUG_SUBSYS=ALL # 所有子系统
# 常见错误:
# NCCL WARN Net plugin not found - 缺 libnccl-net.so
# NCCL WARN Call to posixMemalign failed - 共享内存不足
# ncclSystemError - GPU 间通信失败
# 排查:检查 NCCL 拓扑
nvidia-smi topo -m
# 确认 GPU 间 NVLink 是否正常连接
# 训练 Pod 内测试 GPU 间通信带宽
# 在 Pod 内运行
git clone https://github.com/NVIDIA/nccl-tests.git
cd nccl-tests && make MPI=0
./build/all_reduce_perf -b 8 -e 2G -f 2 -g 8
8. GPU 存储方案
8.1 模型存储策略
模型文件大小参考:
- Qwen2.5-7B: ~15GB (bf16)
- Qwen2.5-72B: ~140GB (bf16)
- Llama-3-70B: ~140GB
- Llama-3-405B: ~800GB
存储策略:
┌──────────────────────────────────┐
│ 热数据(正在推理的模型) │
│ └─ 本地 NVMe SSD / tmpfs │ ← 最快加载
├──────────────────────────────────┤
│ 温数据(近期可能用的模型) │
│ └─ Ceph RBD / Longhorn PVC │ ← 分钟级加载
├──────────────────────────────────┤
│ 冷数据(历史模型/模型仓库) │
│ └─ S3/MinIO + 模型缓存层 │ ← 首次下载慢
└──────────────────────────────────┘
8.2 模型下载 initContainer
# 提前下载模型到共享 PVC
initContainers:
- name: model-downloader
image: alpine:3.20
command:
- sh
- -c
- |
apk add --no-cache curl aws-cli
# 从 S3 或 HuggingFace 下载模型
if [ ! -f /models/config.json ]; then
aws s3 sync s3://models/qwen2.5-7b/ /models/
fi
volumeMounts:
- name: models
mountPath: /models
8.3 训练数据管道
# 训练数据建议:高性能 PVC + 大容量
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: training-dataset
spec:
storageClassName: local-nvme # 本地 NVMe(高性能)
accessModes:
- ReadWriteMany # 多节点训练需 RWX
resources:
requests:
storage: 2Ti
9. GPU 成本优化
9.1 Spot/可抢占实例策略
# 推理服务:On-Demand(不可中断)
spec:
nodeSelector:
node.kubernetes.io/instance-type: on-demand
# 训练/批处理:Spot(可中断,大幅降成本)
spec:
nodeSelector:
node.kubernetes.io/instance-type: spot
tolerations:
- key: "node.kubernetes.io/spot"
operator: "Exists"
effect: "NoSchedule"
terminationGracePeriodSeconds: 300 # 5分钟优雅退出
9.2 GPU 利用率优化自查
# 1. 检查僵尸 GPU Pod(显存占用但利用率极低)
kubectl top pods -A | grep -v NAME
# 2. 统计各节点 GPU 分配率
kubectl describe nodes | grep -A5 "Allocated resources" | grep nvidia
# 3. 发现未设置 limits 的 GPU Pod(可能浪费 GPU)
kubectl get pods -A -o json | jq '[.items[] | select(.spec.containers[].resources.limits."nvidia.com/gpu" == "") | {ns: .metadata.namespace, name: .metadata.name}]'
9.3 成本优化清单
| 策略 | 预期节省 | 实施难度 |
|---|---|---|
| Spot 实例 + 检查点恢复 | 50-70% | 中 |
| MIG 分片推理 | 30-50% | 低(需 A100/H100) |
| Time-slicing 开发环境 | 40-60% | 低 |
| 模型量化(bf16→int4/8) | 30-50% | 低 |
| 显存利用率调优 | 10-20% | 低 |
| 闲置 GPU 自动回收 | 10-30% | 中 |
10. 健康阈值速查
| 指标 | 正常范围 | 告警阈值 | 处理动作 |
|---|---|---|---|
| GPU 利用率 | 60-95% | < 10% 持续 30min → 僵尸检查 | 检查对应 Pod 是否挂死 |
| GPU 温度 | 40-75°C | > 85°C → 降频风险 | 检查机房空调、GPU 风扇 |
| 显存占用 | 50-85% | > 90% → OOM 风险 | 扩容或减少 batch/concurrent |
| ECC 错误 | 0 | > 0 → 硬件故障 | 隔离节点,申请更换 |
| XID 错误 | 0 | > 0 → GPU 异常 | 查看 nvidia-smi -q -d XID |
| NVLink 带宽 | > 300 GB/s | < 200 GB/s | 检查 NVLink 连接 |
| Power 功耗 | 200-400W (A100) | 异常波动 | 检查电源模块 |
11. 面试表达框架
当被问到”你有 GPU/K8s AI 工作负载运维经验吗?”
四段式回答:
-
GPU 资源管理层(30秒)
“我掌握 K8s 上 GPU 资源的全生命周期管理,包括 NVIDIA GPU Operator 部署、Device Plugin 工作原理、MIG 分片策略和 Time-slicing 时分复用。能根据业务场景(推理/训练/开发)选择最合适的 GPU 共享方案。”
-
推理服务部署(30秒)
“我熟悉用 vLLM/TGI 在 K8s 上部署大模型推理服务,理解 PagedAttention 原理、tensor-parallel 多卡推理、以及基于延迟/并发数的 HPA 自动扩缩容。遇到过显存 OOM、NCCL 通信失败等 GPU 特有故障并处理过。”
-
监控与成本(30秒)
“我搭建过 GPU 监控体系(DCGM + Prometheus + Grafana),配置了 GPU 温度/ECC/XID 等关键告警。在成本方面,用 Spot 实例跑训练任务、MIG 分片做多租户推理,将 GPU 利用率从 30% 提升到 70%+。”
-
故障经验(20秒)
“处理过 GPU XID 48 错误导致节点不可用、CUDA OOM 导致的推理中断、NCCL 通信超时导致的多节点训练失败,形成了 GPU 故障诊断 SOP。”