🎮 第24课:GPU调度

📌 课程阶段:实战项目(4/5)|预计时间:60分钟|难度:⭐⭐⭐⭐☆

一、K8s GPU调度概述

AI/ML工作负载需要GPU加速。K8s通过设备插件(Device Plugin)机制支持GPU调度,NVIDIA提供官方的GPU Device Plugin。

┌─────── K8s GPU调度架构 ────────┐
│                                   │
│  ┌───── kube-scheduler ──────┐   │
│  │  Pod请求: nvidia.com/gpu:1│   │
│  │  → 调度到有GPU的节点       │   │
│  └──────────┬────────────────┘   │
│             │                     │
│  ┌──────────▼────────────────┐   │
│  │   GPU Node                │   │
│  │  ┌──────────────────┐    │   │
│  │  │ nvidia-device-   │    │   │
│  │  │ plugin DaemonSet │    │   │
│  │  │                  │    │   │
│  │  │ 1.发现GPU设备     │    │   │
│  │  │ 2.注册到kubelet  │    │   │
│  │  │ 3.分配GPU给Pod   │    │   │
│  │  └──────────────────┘    │   │
│  │                           │   │
│  │  NVIDIA Driver + CUDA    │   │
│  │  ┌──────┐ ┌──────┐      │   │
│  │  │GPU 0 │ │GPU 1 │      │   │
│  │  └──────┘ └──────┘      │   │
│  └───────────────────────────┘   │
│                                   │
│  GPU资源类型:                    │
│  nvidia.com/gpu        = 整卡    │
│  nvidia.com/gpu.shared  = MIG   │
│  nvidia.com/mig-1g.5gb  = MIG切 │
│                                   │
│  关键限制:                       │
│  • GPU资源不可超分(1卡=1请求)  │
│  • 默认不支持分时共享             │
│  • 需要NVIDIA驱动+Container      │
│    Toolkit预装                    │
└───────────────────────────────────┘

二、安装NVIDIA GPU支持

2.1 节点准备

# 安装NVIDIA驱动
sudo apt-get update
sudo apt-get install -y nvidia-driver-535

# 验证驱动
nvidia-smi
# +-----------------------------------------------------------------------------+
# | NVIDIA-SMI 535.x     Driver Version: 535.x     CUDA Version: 12.x         |
# | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
# | 0    A100-SXM...  On           | 00000000:00:04.0 Off |                    0 |
# +-----------------------------------------------------------------------------+

# 安装NVIDIA Container Toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -sL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=containerd
sudo systemctl restart containerd

# ✅ 验证通过
sudo nvidia-ctk --version

2.2 安装GPU Device Plugin

# 安装NVIDIA Device Plugin
kubectl create namespace nvidia-gpu-operator
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm install gpu-operator nvidia/gpu-operator \
  --namespace nvidia-gpu-operator \
  --set driver.enabled=false \
  --set toolkit.enabled=false

# 或简单安装(节点已有驱动和toolkit)
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/deployments/static/nvidia-device-plugin.yaml

# ✅ 验证通过 - 节点报告GPU资源
kubectl get nodes -o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu
# NAME          GPU
# k8s-gpu-1     2
# k8s-gpu-2     4

kubectl describe node k8s-gpu-1 | grep -A5 Allocatable
# Allocatable:
#   cpu:                16
#   memory:             65536Mi
#   nvidia.com/gpu:     2

三、GPU Pod调度

3.1 请求整卡GPU

# gpu-pod.yaml - 请求1块GPU
apiVersion: v1
kind: Pod
metadata:
  name: gpu-test
spec:
  containers:
  - name: cuda
    image: nvidia/cuda:12.4.0-base-ubuntu22.04
    command: ['nvidia-smi']
    resources:
      limits:
        nvidia.com/gpu: 1          # 请求1块GPU
      requests:
        nvidia.com/gpu: 1

# ✅ 验证通过
kubectl apply -f gpu-pod.yaml
kubectl logs gpu-test
# +-----------------------------------------------------------------------------+
# | NVIDIA-SMI 535.x     Driver Version: 535.x     CUDA Version: 12.x         |
# | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
# | 0    A100-SXM...  On           | 00000000:00:04.0 Off |                    0 |
# +-----------------------------------------------------------------------------+

3.2 多GPU训练

# multi-gpu-training.yaml
apiVersion: v1
kind: Pod
metadata:
  name: multi-gpu-training
spec:
  containers:
  - name: pytorch
    image: pytorch/pytorch:2.2.0-cuda12.4-cudnn9-devel
    command: ['python', '-c']
    args:
    - |
      import torch
      print(f"CUDA available: {torch.cuda.is_available()}")
      print(f"GPU count: {torch.cuda.device_count()}")
      for i in range(torch.cuda.device_count()):
          print(f"GPU {i}: {torch.cuda.get_device_name(i)}")
      
      # 简单矩阵运算
      x = torch.randn(10000, 10000).cuda()
      y = torch.randn(10000, 10000).cuda()
      z = torch.mm(x, y)
      print(f"Matrix multiplication result shape: {z.shape}")
    resources:
      limits:
        nvidia.com/gpu: 2          # 请求2块GPU
      requests:
        nvidia.com/gpu: 2
        cpu: "4"
        memory: "16Gi"

# ✅ 验证通过
kubectl logs multi-gpu-training
# CUDA available: True
# GPU count: 2
# GPU 0: NVIDIA A100-SXM4-80GB
# GPU 1: NVIDIA A100-SXM4-80GB
# Matrix multiplication result shape: torch.Size([10000, 10000])

3.3 GPU节点调度约束

# 给GPU节点打标签
kubectl label nodes k8s-gpu-1 hardware-type=NVIDIA-A100
kubectl label nodes k8s-gpu-2 hardware-type=NVIDIA-V100

# GPU优先调度(使用nodeAffinity)
apiVersion: v1
kind: Pod
metadata:
  name: a100-training
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: hardware-type
            operator: In
            values: [NVIDIA-A100]
  containers:
  - name: training
    image: pytorch/pytorch:2.2.0-cuda12.4-cudnn9-devel
    resources:
      limits:
        nvidia.com/gpu: 1

四、MIG(Multi-Instance GPU)

# A100支持MIG,将1块GPU切分为多个实例
# 配置MIG策略
kubectl label nodes k8s-gpu-1 nvidia.com/mig.config=all-1g.5gb

# 请求MIG实例
apiVersion: v1
kind: Pod
metadata:
  name: mig-pod
spec:
  containers:
  - name: inference
    image: nvidia/cuda:12.4.0-base-ubuntu22.04
    command: ['nvidia-smi']
    resources:
      limits:
        nvidia.com/mig-1g.5gb: 1   # 请求1个MIG实例(1G显存+5GB)

# MIG配置选项(A100 80GB):
# all-1g.5gb   → 7个1g.5gb实例
# all-2g.10gb  → 3个2g.10gb实例 + 1个1g.5gb实例
# all-3g.20gb  → 2个3g.20gb实例
# all-4g.20gb  → 1个4g.20gb实例

五、GPU时间片共享

# NVIDIA GPU Sharing(vGPU / MPS)
# 配置时间片共享
apiVersion: v1
kind: ConfigMap
metadata:
  name: gpu-sharing-config
data:
  sharing.yaml: |
    version: v1
    sharing:
      timeSlicing:
        resources:
        - name: nvidia.com/gpu
          replicas: 4              # 1块GPU虚拟为4个

# Device Plugin使用共享配置
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/deployments/static/nvidia-device-plugin.yml

# Pod请求共享GPU
apiVersion: v1
kind: Pod
metadata:
  name: shared-gpu-pod
spec:
  containers:
  - name: app
    image: nvidia/cuda:12.4.0-base-ubuntu22.04
    resources:
      limits:
        nvidia.com/gpu: 1          # 使用1/4的GPU

六、AI/ML工作负载最佳实践

# Kubeflow Pipeline - ML训练流水线
# 1. 使用PodTemplate定义GPU训练任务
apiVersion: kubeflow.org/v2beta1
kind: MPIJob
metadata:
  name: distributed-training
spec:
  slotsPerWorker: 1
  runPolicy:
    cleanPodPolicy: Running
  mpiReplicaSpecs:
    Launcher:
      replicas: 1
      template:
        spec:
          containers:
          - image: ml-training:v1
            name: launcher
    Worker:
      replicas: 4                 # 4个Worker各1块GPU
      template:
        spec:
          containers:
          - image: ml-training:v1
            name: worker
            resources:
              limits:
                nvidia.com/gpu: 1

# 2. 训练数据使用PVC共享
# 3. 模型输出存储到对象存储
# 4. 使用Argo Workflows编排训练流程

七、故障排查

# GPU不可见
nvidia-smi
# 如果失败 → 检查驱动安装

# Pod无法调度到GPU节点
kubectl describe pod <name> | grep -A5 Events
# 0/x nodes are available: Insufficient nvidia.com/gpu

# Device Plugin未注册
kubectl get pods -n nvidia-gpu-operator
kubectl logs -n nvidia-gpu-operator nvidia-device-plugin-xxx

# 容器内nvidia-smi失败
kubectl exec <pod> -- nvidia-smi
# 检查Container Toolkit配置
# cat /etc/containerd/config.toml | grep nvidia

八、练习

  1. 安装NVIDIA GPU Device Plugin,验证节点GPU资源
  2. 创建GPU Pod,运行CUDA测试程序
  3. 配置GPU节点标签与nodeAffinity精确调度
  4. 实现MIG切分,让多个推理Pod共享同一GPU
  5. 设计ML训练Pipeline:数据准备→训练→评估→模型导出

🏆 第24课成就解锁

下一课预告:第25课毕业项目——构建生产级K8s平台!

📌 补充知识

24-gpu调度补充要点:K8s生产实践扩展

🔹 资源配额(ResourceQuota):限制命名空间总资源
  apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: compute-quota
    namespace: production
  spec:
    hard:
      requests.cpu: "20"
      requests.memory: 40Gi
      limits.cpu: "40"
      limits.memory: 80Gi
      pods: "50"
      services: "10"

🔹 LimitRange:设置默认资源限制
  apiVersion: v1
  kind: LimitRange
  metadata:
    name: default-limits
  spec:
    limits:
    - type: Container
      default:
        cpu: "200m"
        memory: 256Mi
      defaultRequest:
        cpu: "100m"
        memory: 128Mi
      max:
        cpu: "2"
        memory: 4Gi

🔹 Pod优先级与抢占
  apiVersion: scheduling.k8s.io/v1
  kind: PriorityClass
  metadata:
    name: high-priority
  value: 1000000
  globalDefault: false
  ---
  spec:
    preemptionPolicy: PreemptLowerPriority

🔹 优雅处理Pod中断
  • PDB保证最小可用副本
  • preStop钩子处理连接排空
  • terminationGracePeriodSeconds充足
  • 应用必须处理SIGTERM信号

🔹 生产环境Checklist
  ✅ 设置resources requests/limits
  ✅ 配置liveness/readiness探针
  ✅ 使用PDB保护关键服务
  ✅ 实现优雅关闭(SIGTERM)
  ✅ 配置HPA自动伸缩
  ✅ 使用NetworkPolicy隔离
  ✅ 开启RBAC最小权限
  ✅ 日志结构化输出
  ✅ 指标暴露/metrics端点
  ✅ 配置PVC数据备份