Kubernetes GPU 调度与 AI 工作负载运维

K8s GPU 调度全解:GPU Operator 部署、MIG 分片与 Time-slicing 共享策略、vLLM 推理服务部署、NCCL 排障与成本优化。

1. GPU 资源在 K8s 中的表示

与传统资源的差异

维度CPU / 内存GPU
调度资源名cpu, memorynvidia.com/gpu
细粒度分配requests: 250m(0.25核)只能整卡分配(1, 2, 4…)
超卖天然支持(limits > requests)不支持超卖(整卡独占)
OOM 行为OOMKillCUDA 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训练、高负载推理
MIG1/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
维度vLLMTGI
PagedAttention
量化支持AWQ/GPTQ/FP8GPTQ/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 工作负载运维经验吗?”

四段式回答:

  1. GPU 资源管理层(30秒)

    “我掌握 K8s 上 GPU 资源的全生命周期管理,包括 NVIDIA GPU Operator 部署、Device Plugin 工作原理、MIG 分片策略和 Time-slicing 时分复用。能根据业务场景(推理/训练/开发)选择最合适的 GPU 共享方案。”

  2. 推理服务部署(30秒)

    “我熟悉用 vLLM/TGI 在 K8s 上部署大模型推理服务,理解 PagedAttention 原理、tensor-parallel 多卡推理、以及基于延迟/并发数的 HPA 自动扩缩容。遇到过显存 OOM、NCCL 通信失败等 GPU 特有故障并处理过。”

  3. 监控与成本(30秒)

    “我搭建过 GPU 监控体系(DCGM + Prometheus + Grafana),配置了 GPU 温度/ECC/XID 等关键告警。在成本方面,用 Spot 实例跑训练任务、MIG 分片做多租户推理,将 GPU 利用率从 30% 提升到 70%+。”

  4. 故障经验(20秒)

    “处理过 GPU XID 48 错误导致节点不可用、CUDA OOM 导致的推理中断、NCCL 通信超时导致的多节点训练失败,形成了 GPU 故障诊断 SOP。”