适用场景:AI 训练 / 推理 / 模型微调 / Jupyter Notebook / 多租户 GPU 共享
核心组件:NVIDIA GPU Operator | Device Plugin | Volcano | Kueue | DCGM Exporter
GPU 型号:A100 / H100 / H200 / A10 / L40S / RTX 4090 等
一、总体架构
K8s GPU 调度从底层到上层分为五层模型:工作负载层(PyTorchJob / TFJob / 推理 Deployment 等)→ 调度器层(kube-scheduler / Volcano / Kueue / KAI)→ GPU 共享策略层(MIG / Time-Slicing / MPS / vGPU)→ 设备管理层(GPU Operator / Device Plugin / GFD / Network Operator / Topology Manager)→ GPU 硬件层(NVLink / InfiniBand / NUMA 拓扑)。
核心原则:
- 先 Operator 再调度器:GPU Operator 是一切的前提,负责驱动、CUDA、设备插件的自动化部署
- 隔离性优先于利用率:生产环境用 MIG,开发环境用 Time-Slicing
- Gang 调度是分布式训练的刚需:防止部分 Pod 调度成功导致资源死锁
- 拓扑感知:NVLink / NUMA 对齐对多卡训练性能影响 5-10 倍
Kubernetes GPU 调度总体架构(五层模型)

图1 Kubernetes GPU 调度总体架构 —— 工作负载→调度器→共享策略→设备管理→GPU 硬件 五层模型
二、GPU 调度组件详解
2.1 NVIDIA GPU Operator
GPU Operator 是 K8s GPU 管理的基石,通过 DaemonSet 自动化部署完整 GPU 软件栈。
| 组件 | 功能 | 部署形态 |
|---|---|---|
| NVIDIA Driver | GPU 内核驱动 | DaemonSet(每 GPU 节点 1 个) |
| Container Toolkit | 容器运行时 GPU 支持 | DaemonSet |
| Device Plugin | 向 K8s 注册 GPU 资源 | DaemonSet |
| GPU Feature Discovery (GFD) | 节点标签(GPU 型号/CUDA/MIG) | DaemonSet |
| DCGM Exporter | GPU Prometheus 指标 | DaemonSet |
| MIG Manager | MIG 分区管理 | DaemonSet |
| Node Status Exporter | GPU 健康状态 | DaemonSet |
安装 GPU Operato
2.2 NVIDIA Device Plugin
Device Plugin 负责将 GPU 注册为 K8s 可调度资源(nvidia.com/gpu),并管理 GPU 共享配置。
Time-Slicing 配置(ConfigMap 方式)
# time-slicing-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: time-slicing-config
namespace: gpu-operator
data:
# 全局默认配置:每张 GPU 切 4 份
any: |-
version: v1
flags:
migStrategy: none
sharing:
timeSlicing:
renameByDefault: false
failRequestsGreaterThanOne: false
resources:
- name: nvidia.com/gpu
replicas: 4
# A100 专用配置:MIG + Time-Slicing 混合
a100-80gb: |-
version: v1
flags:
migStrategy: mixed
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 8
- name: nvidia.com/mig-1g.5gb
replicas: 2
- name: nvidia.com/mig-2g.10gb
replicas: 2
- name: nvidia.com/mig-3g.20gb
replicas: 3
- name: nvidia.com/mig-7g.40gb
replicas: 7
# T4 / L4 轻量级配置
tesla-t4: |-
version: v1
flags:
migStrategy: none
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 2
# 应用配置
kubectl apply -f time-slicing-config.yaml
# 更新 ClusterPolicy 使用此配置
kubectl patch clusterpolicies.nvidia.com/cluster-policy \
-n gpu-operator --type merge \
--patch '{"spec": {"devicePlugin": {"config": {"name": "time-slicing-config"}}}}'
# 为节点打标签,指定使用哪个配置
kubectl label nodes gpu-node-1 nvidia.com/device-plugin.config=a100-80gb
kubectl label nodes gpu-node-2 nvidia.com/device-plugin.config=tesla-t4
kubectl label nodes gpu-node-3 nvidia.com/device-plugin.config=any
# 验证:节点 GPU 资源应变为 replicas 倍
kubectl describe node gpu-node-1 | grep -A5 "nvidia.com/gpu"
# 期望:Capacity 中 nvidia.com/gpu 从 8 变为 64 (8卡 × 8 replicas)
MPS 配置(可选,高吞吐场景)
# mps-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mps-config
namespace: gpu-operator
data:
a100-80gb: |-
version: v1
sharing:
mps:
resources:
- name: nvidia.com/gpu
replicas: 5
2.3 MIG(Multi-Instance GPU)分区
MIG 是 A100 / H100 / H200 的硬件级 GPU 分区能力,每个分区有独立的计算核心、显存和 L2 缓存。
MIG 策略选择
| 策略 | 说明 | 适用场景 |
|---|---|---|
| single | 所有 GPU 使用同一 MIG 配置 | 同构推理集群 |
| mixed | 不同 GPU 使用不同 MIG 配置 | 混合负载集群 |
| none | 禁用 MIG | 训练集群 |
MIG 配置与启用
# mig-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mig-config
namespace: gpu-operator
data:
# 7×1g.5gb:最大并发,适合轻量推理
all-1g.5gb: |-
version: v1
mig-configs:
all-1g.5gb:
- devices: all
mig-enabled: true
mig-devices:
1g.5gb: 7
# 3×2g.10gb:平衡型,中等模型推理
all-2g.10gb: |-
version: v1
mig-configs:
all-2g.10gb:
- devices: all
mig-enabled: true
mig-devices:
2g.10gb: 3
# 2×3g.20gb:大型模型推理
all-3g.20gb: |-
version: v1
mig-configs:
all-3g.20gb:
- devices: all
mig-enabled: true
mig-devices:
3g.20gb: 2
# 混合配置:多种分区共存
mixed-config: |-
version: v1
mig-configs:
mixed-config:
- devices: [0]
mig-enabled: true
mig-devices:
1g.5gb: 2
2g.10gb: 1
3g.20gb: 1
- devices: [1]
mig-enabled: true
mig-devices:
7g.40gb: 1
# 应用 MIG 配置
kubectl apply -f mig-config.yaml
# 设置默认 MIG 策略
kubectl patch clusterpolicies.nvidia.com/cluster-policy \
-n gpu-operator --type merge \
--patch '{"spec": {"migManager": {"config": {"name": "mig-config", "default": "all-2g.10gb"}}}}'
# 为节点指定 MIG 配置
kubectl label nodes gpu-node-1 nvidia.com/mig.config=all-2g.10gb
kubectl label nodes gpu-node-2 nvidia.com/mig.config=mixed-config
# 验证 MIG 实例
nvidia-smi mig -lgi # 列出 MIG GPU 实例
kubectl describe node gpu-node-1 | grep -E "nvidia.com/mig"
请求 MIG 设备的 Pod 示例
apiVersion: v1
kind: Pod
metadata:
name: mig-inference
spec:
restartPolicy: Never
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
nodeSelector:
nvidia.com/mig.config: all-2g.10gb
containers:
- name: inference
image: nvcr.io/nvidia/tritonserver:24.01-py3
resources:
limits:
nvidia.com/mig-2g.10gb: 1 # 请求一个 2g.10gb MIG 实例
requests:
nvidia.com/mig-2g.10gb: 1
env:
- name: NVIDIA_VISIBLE_DEVICES
value: "0:0" # GPU 0 的 MIG 实例 0
2.4 GPU 共享策略对比
| 维度 | MIG | Time-Slicing | MPS | vGPU |
|---|---|---|---|---|
| 隔离级别 | 硬件级 | 无 | SM 级 | 硬件级 |
| 显存隔离 | 独立 | 共享(无限制) | 共享 | 独立 |
| 故障隔离 | 是(单分区故障不影响其他) | 否(一个 Pod 崩溃影响同 GPU 所有 Pod) | 否 | 是 |
| 性能影响 | 无干扰 | 上下文切换开销 | 协作型,吞吐高 | 轻微虚拟化开销 |
| GPU 要求 | A100 / A30 / H100 / H200 | 任意 NVIDIA GPU | Volta 架构以上 | 需商业 License |
| 最大分区数 | 7(A100) / 8(H100) | 无上限(建议 ≤10) | 无上限 | 取决于 License |
| 最佳场景 | 生产多租户推理 | 开发 / Notebook / 轻量推理 | 协作型小任务批量推理 | VM 虚拟化场景 |
| 不适用 | 多卡训练(NCCL 受限) | SLO 敏感推理 | 故障敏感场景 | K8s 原生 Pod |
最佳实践:MIG + Time-Slicing 混合——在 MIG 切片内再启用 Time-Slicing,实现硬件隔离 + 灵活超分。例如 A100 7×1g.5gb MIG 切片,每个切片再切 2 份 Time-Slicing,等效 14 个调度单元。

图2 GPU 共享策略对比与选型决策树 —— MIG / Time-Slicing / MPS / vGPU 四种策略对比和选型路径
三、批量调度器
3.1 Volcano(Gang 调度首选)
Volcano 是 CNCF 孵化项目,专为批量计算场景设计,支持 Gang 调度、队列管理和公平分享。
安装 Volcano
helm repo add volcano-sh https://volcano-sh.github.io/helm-charts
helm repo update
helm install volcano volcano-sh/volcano \
--namespace volcano-system \
--create-namespace \
--set basic.webhook_enable=true \
--set basic.metrics_enable=true
# 验证
kubectl get pods -n volcano-system
队列配置
# volcano-queue.yaml
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: training-queue
spec:
weight: 3 # 权重(3 表示占总资源 3/总权重)
reclaimable: true # 允许资源回收
capability:
nvidia.com/gpu: 8
cpu: "32"
memory: "128Gi"
---
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: inference-queue
spec:
weight: 1
reclaimable: true
capability:
nvidia.com/gpu: 4
cpu: "16"
memory: "64Gi"
Volcano Job 示例(Gang 调度)
# volcano-training-job.yaml
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: distributed-pytorch-training
spec:
minAvailable: 4 # Gang: 必须同时调度 4 个 Pod,否则全部等待
schedulerName: volcano
queue: training-queue
policies:
- event: PodEvicted
action: RestartJob
tasks:
- replicas: 1
name: master
template:
spec:
containers:
- image: nvcr.io/nvidia/pytorch:24.01-py3
name: pytorch-master
command:
- torchrun
- --nproc_per_node=1
- --nnodes=4
- --node_rank=$VC_TASK_INDEX
- train.py
resources:
limits:
nvidia.com/gpu: 1
cpu: "4"
memory: "16Gi"
restartPolicy: OnFailure
- replicas: 3
name: worker
template:
spec:
containers:
- image: nvcr.io/nvidia/pytorch:24.01-py3
name: pytorch-worker
command:
- torchrun
- --nproc_per_node=1
- --nnodes=4
- --node_rank=$VC_TASK_INDEX
- train.py
resources:
limits:
nvidia.com/gpu: 1
cpu: "4"
memory: "16Gi"
restartPolicy: OnFailure
plugins:
env: []
svc: []
ssh: []
kubectl apply -f volcano-queue.yaml
kubectl apply -f volcano-training-job.yaml
# 查看 Job 状态
kubectl get vcjob -o wide
kubectl describe vcjob distributed-pytorch-training
3.2 Kueue(配额管理)
Kueue 是 K8s 原生的 Job 排队层,管理配额和准入控制,不替换默认调度器。
# 安装 Kueue
kubectl apply -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.9.0/manifests.yaml
# 验证
kubectl get pods -n kueue-system
# kueue-resource-flavor.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
name: gpu-h100
spec:
nodeLabels:
nvidia.com/gpu.product: "NVIDIA-H100-80GB"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: gpu-cluster-queue
spec:
namespaceSelector: {}
queueingStrategy: BestEffortFIFO
resourceGroups:
- coveredResources: ["nvidia.com/gpu", "cpu", "memory"]
flavors:
- name: gpu-h100
resources:
- name: nvidia.com/gpu
nominalQuota: 8
- name: cpu
nominalQuota: "64"
- name: memory
nominalQuota: "256Gi"
- name: nvidia.com/gpu
borrowingLimit: 4 # 允许借调 4 个
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
name: team-ml-queue
namespace: ml-team
spec:
clusterQueue: gpu-cluster-queue
3.3 调度器选型对比
| 维度 | kube-scheduler | Volcano | Kueue | KAI Scheduler |
|---|---|---|---|---|
| Gang 调度 | 不支持 | 原生支持 | 不原生支持 | 支持 |
| 队列/配额 | ResourceQuota | Queue CRD | ClusterQueue/LocalQueue | 层级公平队列 |
| 公平分享 | 不支持 | DRF | 简单配额 | 层级 DRF |
| 抢占 | PriorityClass | 高级抢占 | 跨配额抢占 | 多级抢占 |
| 拓扑感知 | 有限 | 有限 | 无 | 原生支持 |
| 分数 GPU | 不支持 | 不支持 | 不支持 | 支持 |
| K8s 原生 | 是 | 替换调度器 | 叠加默认调度器 | 替换调度器 |
| 最佳场景 | 简单推理 | 分布式训练 | 多团队配额管理 | 大规模 AI 平台 |
推荐组合:Volcano(Gang 调度)+ Kueue(配额管理)+ GPU Operator(设备管理)是当前生产环境的标准组合。
四、拓扑感知调度
4.1 NUMA / NVLink 对齐
多卡训练中,GPU 之间的互连拓扑对性能影响巨大。NVLink 互联的 GPU 间 NCCL 带宽可达 300+ GB/s,而跨 NUMA 走 PCIe 仅 50-80 GB/s。
# 查看节点 GPU 拓扑
nvidia-smi topo -m
# 期望输出(8 卡 H100 NVLink):
# GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
# GPU0 X NV12 NV12 NV12 NV12 NV12 NV12 NV12
# GPU1 NV12 X NV12 NV12 NV12 NV12 NV12 NV12
# ...(NV12 = 12 NVLink 互联)
# SYS / PIX 表示跨 PCIe / 跨 NUMA
Topology Manager 配置
# kubelet 配置(/var/lib/kubelet/config.yaml)
topologyManagerPolicy: single-numa-node
# 可选策略:
# none — 不做拓扑对齐
# best-effort — 尽力对齐,不满足也不拒绝
# restricted — 必须对齐,不满足则调度失败
# single-numa-node — GPU+NIC+CPU 在同一 NUMA 节点
4.2 Network Operator(RDMA / GPUDirect)
# 安装 NVIDIA Network Operator
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm install network-operator nvidia/network-operator \
--namespace network-operator \
--create-namespace \
--set deploySRIOV=true \
--set rdma.enabled=true
# 验证 Pod 可访问 RDMA 设备
# Pod 需要请求 RDMA 资源
apiVersion: v1
kind: Pod
metadata:
name: rdma-test
spec:
containers:
- name: test
image: nvcr.io/nvidia/pytorch:24.01-py3
resources:
limits:
nvidia.com/gpu: 1
rdma/ib: 1 # 请求 RDMA 设备
securityContext:
capabilities:
add: ["IPC_LOCK"]
volumeMounts:
- name: ib-shared
mountPath: /dev/infiniband
volumes:
- name: ib-shared
hostPath:
path: /dev/infiniband
# 验证 NCCL 使用 RDMA(而非 TCP 回退)
# Pod 内执行 NCCL 测试
kubectl exec -it rdma-test -- python -c "
import torch.distributed as dist
print('Backend:', dist.get_backend())
"
# 查看 NCCL 日志确认使用 IB
export NCCL_DEBUG=INFO
# 日志中应出现 "NET/IB" 而非 "NET/Sock"
4.3 节点池划分与标签
# 按 GPU 型号和用途划分节点池
kubectl label nodes gpu-train-01 gpu-type=h100-full gpu-pool=training
kubectl label nodes gpu-train-02 gpu-type=h100-full gpu-pool=training
kubectl label nodes gpu-infer-01 gpu-type=a100-mig gpu-pool=inference
kubectl label nodes gpu-dev-01 gpu-type=t4-ts gpu-pool=development
# 污点设置(仅 GPU 工作负载调度到 GPU 节点)
kubectl taint nodes gpu-train-01 nvidia.com/gpu=true:NoSchedule
kubectl taint nodes gpu-infer-01 nvidia.com/gpu=true:NoSchedule
# 工作负载通过 toleration + nodeSelector 精确调度
五、监控体系
5.1 DCGM Exporter 核心指标
GPU Operator 自动部署 DCGM Exporter,在每台 GPU 节点的 9400 端口暴露 Prometheus 指标。
| 指标 | 类型 | 说明 | 关注重点 |
|---|---|---|---|
| DCGM_FI_DEV_GPU_UTIL | Gauge | GPU 核心利用率 | <30% 浪费,>95% 饱和 |
| DCGM_FI_DEV_MEM_COPY_UTIL | Gauge | 显存带宽利用率 | 与 GPU_UTIL 对比判断瓶颈 |
| DCGM_FI_DEV_FB_USED | Gauge | 已用显存 (MB) | 配合 FB_FREE 算水位 |
| DCGM_FI_DEV_FB_FREE | Gauge | 剩余显存 (MB) | |
| DCGM_FI_DEV_GPU_TEMP | Gauge | GPU 温度 (°C) | >85°C 告警,>90°C 严重 |
| DCGM_FI_DEV_POWER_USAGE | Gauge | GPU 功耗 (W) | 对比 TDP 评估能效 |
| DCGM_FI_PROF_PIPE_TENS_ACTIVE | Gauge | Tensor Core 活跃率 | 真正反映计算密度 |
| DCGM_FI_DEV_SM_CLOCK | Gauge | SM 时钟频率 | 降频 = 散热问题 |
| DCGM_FI_DEV_XID_ERRORS | Counter | Xid 错误计数 | >0 = 硬件故障 |
| DCGM_FI_DEV_ECC_SBE_VOL_TOTAL | Counter | ECC 单比特错误 | 累积增长 = 显存老化 |
| DCGM_FI_DEV_ECC_DBE_VOL_TOTAL | Counter | ECC 双比特错误 | >0 = 显存损坏 |
| DCGM_FI_DEV_REMOVED_REPS | Counter | GPU 被移除次数 | >0 = GPU 不可达 |
5.2 Prometheus 采集配置
# prometheus-config.yaml
scrape_configs:
# DCGM GPU 指标
- job_name: 'dcgm-exporter'
kubernetes_sd_configs:
- role: daemonset
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: dcgm-exporter
action: keep
metrics_path: /metrics
# Kubelet cAdvisor(容器级 GPU 使用)
- job_name: 'kubernetes-cadvisor'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- target_label: __address__
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor
# Volcano 调度器指标
- job_name: 'volcano-scheduler'
kubernetes_sd_configs:
- role: pod
namespaces:
names: ['volcano-system']
5.3 Prometheus 告警规则
# gpu-alerts.yaml
groups:
# ===== P0 紧急 =====
- name: gpu-critical
rules:
# GPU Xid 硬件故障
- alert: GPUXidError
expr: increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} on {{ $labels.node }} 发生 Xid 硬件错误"
description: "Xid 错误码见 NVIDIA 官方文档,常见:Xid=31 GPU 内存页错误 / Xid=43 GPU 停止响应 / Xid=48 双比特 ECC 错误"
# GPU 被系统移除
- alert: GPURemoved
expr: increase(DCGM_FI_DEV_REMOVED_REPS[5m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} 被从系统中移除"
# 双比特 ECC 错误(显存损坏)
- alert: GPUECCDoubleBit
expr: increase(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[1h]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} 双比特 ECC 错误,显存已损坏"
# GPU 温度过高
- alert: GPUTempCritical
expr: DCGM_FI_DEV_GPU_TEMP > 90
for: 2m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} 温度 {{ $value }}°C"
# GPU 不可达(DCGM Exporter 宕机)
- alert: DCGMExporterDown
expr: up{job="dcgm-exporter"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "DCGM Exporter 不可达: {{ $labels.instance }}"
# ===== P1 重要 =====
- name: gpu-warning
rules:
# GPU 显存水位过高
- alert: GPUMemoryHigh
expr: DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) * 100 > 90
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} 显存使用率 >90%"
# GPU 持续高利用率
- alert: GPUUtilSaturated
expr: avg_over_time(DCGM_FI_DEV_GPU_UTIL[10m]) > 95
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} 利用率持续 >95%,需扩容"
# GPU 利用率持续偏低(资源浪费)
- alert: GPUUtilLow
expr: avg_over_time(DCGM_FI_DEV_GPU_UTIL[30m]) < 20
for: 15m
labels:
severity: info
annotations:
summary: "GPU {{ $labels.gpu }} 利用率持续 <20%,可能资源过剩"
# 单比特 ECC 错误增长(显存老化预警)
- alert: GPUECCSingleBitGrowing
expr: rate(DCGM_FI_DEV_ECC_SBE_VOL_TOTAL[1h]) > 10
for: 10m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} 单比特 ECC 错误快速增长,显存老化中"
# GPU 降频(散热问题)
- alert: GPUClockThrottling
expr: DCGM_FI_DEV_SM_CLOCK < 1000
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} SM 时钟降至 {{ $value }}MHz,可能在降频保护"
# Volcano Job 调度失败
- alert: VolcanoJobPending
expr: volcano_job_pending > 5
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $value }} 个 Volcano Job 处于 Pending 状态"
5.4 Grafana PromQL 核心查询
# 1. GPU 利用率(按节点汇总)
avg by (node) (DCGM_FI_DEV_GPU_UTIL)
# 2. 显存使用率
DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) * 100
# 3. Tensor Core 活跃率(真正的计算密度)
DCGM_FI_PROF_PIPE_TENS_ACTIVE * 100
# 4. GPU 温度趋势
DCGM_FI_DEV_GPU_TEMP
# 5. 功耗效率(tokens/W 或推理/W)
DCGM_FI_DEV_POWER_USAGE
# 6. 容器级 GPU 使用(kubelet cAdvisor)
rate(container_accelerator_duty_cycle[1m]) * 100
# 7. Xid 错误事件
increase(DCGM_FI_DEV_XID_ERRORS[1h])
# 8. GPU 利用率分布(P50/P90/P99)
quantile_over_time(0.50, DCGM_FI_DEV_GPU_UTIL[1h])
quantile_over_time(0.90, DCGM_FI_DEV_GPU_UTIL[1h])
quantile_over_time(0.99, DCGM_FI_DEV_GPU_UTIL[1h])
# 9. 集群 GPU 总量 vs 已分配
sum(kube_node_status_capacity{resource="nvidia_com_gpu"})
sum(kube_pod_container_resource_limits{resource="nvidia_com_gpu"})
# 10. GPU 空闲率(可用于缩容评估)
1 - (count(DCGM_FI_DEV_GPU_UTIL < 30) / count(DCGM_FI_DEV_GPU_UTIL))

六、故障自愈方案
6.1 故障分级与自愈策略
| 级别 | 故障类型 | 检测方式 | 自愈动作 | 目标 MTTR |
|---|---|---|---|---|
| P0 | GPU Xid 硬件故障 | DCGM Xid 指标 | L2 节点隔离 + L1 Pod 重启 + 通知 | <5min |
| P0 | GPU 被移除 | DCGM RemovedReps | L2 节点隔离 + L4 节点替换 | <15min |
| P0 | 双比特 ECC 错误 | DCGM ECC_DBE | L2 节点隔离(永久隔离)+ 通知 | <5min |
| P1 | GPU 温度过高 | DCGM GPU_TEMP | 降频 + 告知 + 检查散热 | <10min |
| P1 | 显存不足 | DCGM FB_USED | Pod 重启 + 调整 gpu-memory-utilization | <3min |
| P2 | GPU 利用率低 | DCGM GPU_UTIL | 标记缩容候选 + 容量优化建议 | 人工评估 |
| P2 | Volcano Job Pending | Volcano 指标 | 资源回收 + 队列优先级调整 | <10min |
6.2 GPU 故障自愈 Controller
# gpu-failure-operator.yaml — 自动隔离故障 GPU 节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu-health-controller
namespace: gpu-operator
spec:
replicas: 1
selector:
matchLabels:
app: gpu-health-controller
template:
metadata:
labels:
app: gpu-health-controller
spec:
serviceAccountName: gpu-health-controller
containers:
- name: controller
image: registry.local/gpu-health-controller:latest
env:
- name: PROMETHEUS_URL
value: "http://prometheus:9090"
- name: XID_ALERT_EXPR
value: 'increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0'
- name: ECC_DBE_EXPR
value: 'increase(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[1h]) > 0'
- name: TEMP_THRESHOLD
value: "90"
- name: AUTO_CORDON
value: "true" # 自动 cordon 故障节点
- name: AUTO_DRAIN
value: "true" # 自动 drain
- name: DRAIN_GRACE_PERIOD
value: "300" # 5 分钟优雅退出
6.3 自动隔离脚本
#!/bin/bash
# gpu-auto-isolate.sh — GPU 故障自动隔离
# 被 Prometheus AlertManager webhook 调用
ALERT_NAME="$1"
NODE_NAME="$2"
GPU_ID="$3"
case "$ALERT_NAME" in
"GPUXidError"|"GPUECCDoubleBit"|"GPURemoved")
echo "[$(date)] P0 故障: $ALERT_NAME on $NODE_NAME GPU $GPU_ID"
# 1. Cordon 节点(阻止新 Pod 调度)
kubectl cordon "$NODE_NAME"
# 2. Drain 节点(驱逐现有 GPU Pod)
kubectl drain "$NODE_NAME" \
--grace-period=300 \
--timeout=600s \
--ignore-daemonsets \
--delete-emptydir-data \
--force
# 3. 添加故障标签
kubectl label nodes "$NODE_NAME" \
gpu-fault="$ALERT_NAME" \
gpu-fault-time="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
gpu-quarantine="true"
# 4. 污点标记(永久隔离,直到人工修复)
kubectl taint nodes "$NODE_NAME" \
gpu-fault=true:NoSchedule --overwrite
# 5. 通知(钉钉/企业微信 Webhook)
curl -s -X POST "$DINGTALK_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"msgtype\": \"markdown\",
\"markdown\": {
\"title\": \"GPU 故障自动隔离\",
\"text\": \"### GPU P0 故障\n\n**节点**: $NODE_NAME\n**GPU**: $GPU_ID\n**故障**: $ALERT_NAME\n**动作**: 已 Cordon + Drain + 隔离\n**时间**: $(date)\n\n请立即检查并更换故障 GPU。\n\"
}
}"
echo "[$(date)] 节点 $NODE_NAME 已隔离"
;;
"GPUTempCritical")
echo "[$(date)] P1 告警: GPU 温度过高 on $NODE_NAME GPU $GPU_ID"
# 温度过高不直接隔离,而是降频 + 告知
# 1. 设置 GPU 功率限制(降低频率减少发热)
ssh "$NODE_NAME" "nvidia-smi -i $GPU_ID -pl 250" # 限制到 250W
# 2. 标记为温度告警
kubectl label nodes "$NODE_NAME" \
gpu-temp-warning="true" --overwrite
echo "[$(date)] 已降低 GPU $GPU_ID 功率限制"
;;
"GPUMemoryHigh")
echo "[$(date)] P1 告警: 显存过高 on $NODE_NAME GPU $GPU_ID"
# 显存过高通常由 Pod 异常导致,重启 Pod 即可
# 获取占用此 GPU 的 Pod
POD_NAME=$(kubectl get pods --all-namespaces -o json | \
jq -r ".items[] | select(.spec.containers[].resources.limits.\"nvidia.com/gpu\" != null) | \
select(.status.phase==\"Running\") | .metadata.name" | head -1)
if [ -n "$POD_NAME" ]; then
echo "重启 Pod: $POD_NAME"
kubectl delete pod "$POD_NAME" --grace-period=30
fi
;;
esac
6.4 AlertManager Webhook 路由
# alertmanager-config.yaml
route:
receiver: default
group_by: ['alertname', 'node']
group_wait: 10s
group_interval: 30s
repeat_interval: 5m
routes:
# GPU P0 故障 → 立即触发自愈
- matchers: ['alertname=~"GPUXidError|GPUECCDoubleBit|GPURemoved"']
receiver: gpu-auto-isolate
group_wait: 0s
repeat_interval: 1h
# GPU P1 告警 → 告知 + 部分自愈
- matchers: ['alertname=~"GPUTempCritical|GPUMemoryHigh"']
receiver: gpu-warning-handler
group_wait: 30s
# 其他告警 → 钉钉通知
- matchers: ['severity=info']
receiver: dingtalk-info
receivers:
- name: gpu-auto-isolate
webhook_configs:
- url: 'http://gpu-health-controller:8080/webhook'
send_resolved: true
- name: gpu-warning-handler
webhook_configs:
- url: 'http://gpu-health-controller:8080/warning'
send_resolved: true
- name: dingtalk-info
webhook_configs:
- url: 'http://dingtalk-notifier:8080/alert'
- name: default
webhook_configs:
- url: 'http://dingtalk-notifier:8080/alert'
6.5 Pod 级自愈(Readiness / Liveness Probe)
# GPU 推理 Pod 健康探针配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: gpu-inference-service
spec:
replicas: 2
template:
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
# Readiness Probe:模型加载完成后才接收流量
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 300 # 模型加载需要 5-15 分钟
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
# Liveness Probe:服务卡死后自动重启
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 600 # 启动后 10 分钟才开始检查
periodSeconds: 60
timeoutSeconds: 10
failureThreshold: 5 # 连续 5 次失败 = ~5 分钟后重启
# Startup Probe:启动期间不杀 Pod
startupProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 30 # 允许 15 分钟启动
七、日常运维
7.1 每日检查清单
| 检查项 | 命令 | 正常标准 |
|---|---|---|
| GPU 节点状态 | kubectl get nodes -l nvidia.com/gpu.present=true | 全部 Ready |
| GPU Pod 状态 | kubectl get pods -A -l nvidia.com/gpu.present=true | 无 CrashLoop |
| DCGM Exporter | kubectl get ds -n gpu-operator -l app=dcgm-exporter | 全部 Running |
| Xid 错误 | curl prometheus:9090/api/v1/query?query=DCGM_FI_DEV_XID_ERRORS | 全部为 0 |
| GPU 温度 | PromQL: DCGM_FI_DEV_GPU_TEMP | <80°C |
| GPU 利用率 | PromQL: avg(DCGM_FI_DEV_GPU_UTIL) | 40-85% |
| Volcano 队列 | kubectl get vcqueue | 无积压 |
| 节点污点 | kubectl get nodes -o json | jq '.items[].spec.taints' | 无意外污点 |
7.2 每周检查清单
| 检查项 | 命令/操作 | 说明 |
|---|---|---|
| GPU 驱动版本一致性 | kubectl get nodes -o custom-columns=NAME:.metadata.name,DRIVER:.metadata.labels['nvidia\.com/gpu\.driver'] | 确认无版本漂移 |
| GPU 互连带宽 | 节点上执行 all_reduce_perf | 对比基线 |
| MIG 配置一致性 | nvidia-smi mig -lgi(每节点) | 与 ConfigMap 一致 |
| 告警审查 | AlertManager 历史 | 分析误报/漏报 |
| 容量趋势 | GPU 利用率周度趋势 | 评估扩容/缩容 |
| 日志归档 | logrotate /etc/logrotate.d/kube-gpu | 压缩归档 |
| 污点清理 | 检查 gpu-quarantine 标签的节点 | 修复后清除 |
7.3 常用运维命令速查
# ===== GPU 节点管理 =====
kubectl get nodes -l nvidia.com/gpu.present=true # 列出所有 GPU 节点
kubectl describe node <node> | grep -A10 "Allocated" # 查看 GPU 分配
kubectl cordon <node> # 暂停调度
kubectl uncordon <node> # 恢复调度
kubectl drain <node> --ignore-daemonsets --force # 驱逐 Pod
kubectl taint nodes <node> gpu-fault=true:NoSchedule # 添加隔离污点
# ===== GPU 状态检查 =====
nvidia-smi # GPU 概览
nvidia-smi -l 1 # 每秒刷新
nvidia-smi topo -m # 互连拓扑
nvidia-smi mig -lgi # MIG 实例
nvidia-smi -q -d MEMORY # 显存详情
nvidia-smi -q -d ECC # ECC 错误
nvidia-smi -q -d POWER # 功耗
dcgmi dmon -e 1001,1002,1003,1004,1005 # DCGM 实时
# ===== Volcano 管理 =====
kubectl get vcjob # 查看 Job
kubectl get vcqueue # 查看队列
kubectl describe vcjob <job-name> # Job 详情
kubectl get podgroups # PodGroup
# ===== MIG 管理 =====
nvidia-smi mig -lgi # 列出 GPU 实例
nvidia-smi mig -lci # 列出计算实例
nvidia-smi mig -cgi 1g.5gb,1g.5gb -C # 创建 MIG 实例
nvidia-smi mig -dgi # 销毁 GPU 实例
# ===== GPU Operator 管理 =====
kubectl get clusterpolicies -n gpu-operator # 查看集群策略
kubectl get ds -n gpu-operator # 查看 DaemonSet
kubectl logs -n gpu-operator ds/nvidia-device-plugin # 设备插件日志
kubectl logs -n gpu-operator ds/nvidia-driver-daemonset # 驱动日志
# ===== 清理隔离节点 =====
# 1. 确认 GPU 已修复
nvidia-smi # 在节点上确认 GPU 正常
# 2. 清除故障标签和污点
kubectl label nodes <node> gpu-fault- gpu-fault-time- gpu-quarantine-
kubectl taint nodes <node> gpu-fault:NoSchedule-
# 3. 恢复调度
kubectl uncordon <node>
# 4. 验证 Pod 恢复
kubectl get pods -o wide --field-selector spec.nodeName=<node>
7.4 性能优化要点
| 优化项 | 方法 | 预期收益 |
|---|---|---|
| MIG + Time-Slicing 混合 | MIG 切片内再 Time-Slicing | GPU 利用率 +40-60% |
| Gang 调度 | Volcano minAvailable | 消除资源死锁,集群利用率 +20% |
| 拓扑感知 | Topology Manager single-numa-node | NCCL 带宽 +3-5x |
| RDMA/GPUDirect | Network Operator + RDMA 设备插件 | NCCL 带宽 +10x vs TCP |
| 节点池隔离 | 按型号和用途分池 | 减少调度冲突,可预测性提升 |
| 配额管理 | Kueue + ResourceQuota | 防止团队抢占,公平分享 |
| Binpack 策略 | Volcano binpack 调度 | 减少碎片化,提升装箱率 |
| 弹性伸缩 | Karpenter + Cluster Autoscaler | 按需扩缩容,成本 -30% |
| 优先级抢占 | PriorityClass + Volcano | 高优训练抢占低优推理 |
| Spot/竞价实例 | Spot 节点池 + 容错 | 成本 -60-70% |
7.5 注意事项
| # | 注意点 | 说明 | 风险等级 |
|---|---|---|---|
| 1 | Time-Slicing 无隔离 | 共享 GPU 的 Pod 可能互相影响,一个 OOM 会影响所有 | 高 |
| 2 | MIG 不支持 NCCL | MIG 实例间不能直接 NVLink 通信,分布式训练受限 | 中 |
| 3 | 驱动版本兼容 | K8s 升级后 CUDA 版本可能不匹配,需 pin 驱动版本 | 高 |
| 4 | 首次加载慢 | 模型权重加载 5-15 分钟,K8s 探针需设大 initialDelaySeconds | 中 |
| 5 | MIG 策略变更需重启 | 修改 MIG 配置会销毁所有 GPU 实例,需 drain 节点 | 高 |
| 6 | --shm-size 不足 | NCCL 多卡通信需要共享内存,Docker 默认 64MB 太小 | 中 |
| 7 | DRA 迁移 | Dynamic Resource Allocation 是未来方向(K8s 1.34+),但当前 Device Plugin 仍稳定 | 低 |
| 8 | GFD 标签格式 | 不同 GPU Operator 版本的标签可能不同,升级前验证 | 中 |
| 9 | GPU 驱动冲突 | 宿主机已安装驱动 + GPU Operator 也会安装,可能冲突 | 高 |
| 10 | Volcano 版本兼容 | Volcano 替换默认调度器,K8s 版本升级需验证兼容性 | 高 |
| 11 | 资源碎片化 | 大量小 Pod 占满节点,大 Job 无法调度 | 中 |
| 12 | Spot 中断 | Spot 节点被回收时训练 Job 会失败,需 Checkpoint 机制 | 中 |
八、附录
8.1 节点池规划参考
| 节点池 | GPU 型号 | MIG/TS | 污点 | 用途 | 配额 |
|---|---|---|---|---|---|
| gpu-train | H100 80GB | 无 | nvidia.com/gpu=true:NoSchedule | 分布式训练 | 8 GPU/节点 |
| gpu-infer-prod | A100 80GB | MIG 2g.10gb | nvidia.com/gpu=true:NoSchedule | 生产推理 | 7 MIG/卡 |
| gpu-infer-dev | A10 24GB | Time-Slicing x4 | nvidia.com/gpu=true:PreferNoSchedule | 开发推理 | 4 副本/卡 |
| gpu-notebook | T4 16GB | Time-Slicing x2 | notebook=true:NoSchedule | Jupyter | 2 副本/卡 |
| gpu-spot | L4 24GB | 无 | spot=true:NoSchedule | 批处理/测试 | 按需 |
8.2 参考资源
文档说明:本文档基于 2025-2026 年 K8s GPU 生态最新实践编写,覆盖从 GPU Operator 安装到故障自愈的全流程。GPU 调度在 2026 年已进入生产成熟期,核心在于根据工作负载选择合适的共享策略和调度器组合。

370

被折叠的 条评论
为什么被折叠?



