学习库/ AI Infra 转型/ 04 · 04 K8s GPU 资源接入层
🧠 AI Infra 转型 · 第 5 / 17 篇

04 K8s GPU 资源接入层

5930 字· 阅读约 14 分钟· 2026-08-23 更新

04 K8s GPU 资源接入层

从这里起,你熟悉的 K8s 登场了。这一章解决一个核心问题:K8s 原生不懂 GPU,怎么让集群「看见」并管理成千上万张卡? 这是你云原生经验的直接延伸——学完你会发现,本质就是「给 kubelet 加一种新的可分配资源」。

💡 给你的话:这一章你应该学得最快。把 device-plugin 类比成你熟悉的 CSI/CNI 那种扩展机制就对了。但本章会深入到 gRPC 接口、Linux 内核机制、容器挂载细节——不要跳过,这些是你和算法方定位「Pod 为什么拿不到 GPU」时的底气。

📖 本章导读与前置依赖

  • 前置(你已在 01 学过):GPU 是异构硬件(01)、CUDA 是 NVIDIA 的软件生态(01/02)、HBM 显存(01)。
  • 本章解决什么:K8s 原生只认 CPU/内存,不认识 GPU——怎么用 Device Plugin 机制把 GPU 注册成扩展资源(nvidia.com/gpu),再用 GPU Operator 一键管驱动+插件+监控;容器里为什么能用 GPU(NVIDIA Container Toolkit + Linux 内核机制);多厂商(N/AMD/国产)异构集群怎么接入。
  • 学习目标:能把 GPU 接入 K8s 并验证 Pod 拿到卡;能说清「kubelet 怎么发现 GPU → gRPC 注册 → 调度器分配 → 容器挂载设备文件」全链路;理解异构集群统一编排的四层抽象。
  • 怎么读:4.1-4.2 懂问题和 Device Plugin 原理,4.3-4.4 懂 Operator 全家桶和实战,4.5 深入 gRPC 接口和 Linux 内核机制(核心新增),4.6 看多厂商,4.7 看 GFD 标签,4.8 看异构统一编排,4.9 看资源演进引出 05

4.1 问题:K8s 原生不懂 GPU

K8s 原生只认识 CPU、内存、ephemeral-storage(临时存储) 等「通用资源」。GPU 是异构硬件,K8s 不知道它是什么、有多少、怎么隔离。

名词解释

  • 异构硬件(Heterogeneous Hardware):和通用 CPU 不同的专用硬件(GPU/TPU/FPGA)。K8s 原生为通用 CPU 设计,所以要扩展。
  • ephemeral-storage:K8s 里节点本地临时存储资源。

解决方案:K8s 提供了一个标准扩展机制——Device Plugin(设备插件)——让第三方把 GPU 注册成「扩展资源」(如 nvidia.com/gpu)。你的 Pod 写 nvidia.com/gpu: 1 就能申请 1 张卡。

💡 类比(K8s 人秒懂):Device Plugin 就像你熟悉的 CSI(容器存储接口) 之于存储、CNI(容器网络接口) 之于网络——一个标准化的扩展点,让 K8s 能管「非原生」的资源。CSI 管存储卷、CNI 管网络、Device Plugin 管「设备」。

这正是你之前对话里问的「是不是写个 operator 去检测有几张卡」——答案更标准:用 Device Plugin 机制 + GPU Operator 全家桶,而不是自己造轮子。


4.2 Device Plugin 机制(核心原理)

GPU 物理卡
   │
NVIDIA device-plugin(DaemonSet,每节点一个)
   │  gRPC 向 kubelet 上报
   ▼
kubelet 把 GPU 注册为节点扩展资源 nvidia.com/gpu: N
   │
kube-scheduler 看到资源,按 Pod 请求调度
   ▼
Pod 被分配到有卡的节点,容器里出现 /dev/nvidia*
  • Device Plugin 是 K8s 的标准扩展点(kubelet 侧的 gRPC 插件)。
    • 名词解释gRPC 是 Google 的高性能远程调用框架(基于 HTTP/2 + Protobuf)。Device Plugin 通过 gRPC 跟 kubelet 通信。DaemonSet 是 K8s 里「每个节点跑一个副本」的工作负载类型——device-plugin 必须每节点一个,所以用 DaemonSet。
  • 上报后,GPU 数量进 Node.status.allocatable(节点可分配资源),调度器据此分配。
  • NVIDIA device-plugin 仓库:https://github.com/NVIDIA/k8s-device-plugin

重点:Device Plugin 只负责「发现 + 注册资源」,不负责「智能调度」。分配谁、怎么分配,是 kube-scheduler(及扩展)的事——这正好引出 06/07 的调度层。

💡 类比:device-plugin 像是「仓库管理员登记库存」,kube-scheduler 才是「调度员决定货发哪个仓」。职责分离,和你熟悉的所有 K8s 扩展一样。


4.3 NVIDIA GPU Operator 全家桶

手动部署 device-plugin + 装驱动 + 装容器运行时太繁琐。GPU OperatorClusterPolicy 这个 CRD 一键管理整套组件:

名词解释

  • Operator:K8s 里「用控制器自动化管理某类应用」的模式,你很熟。GPU Operator 就是一个管理「GPU 相关全套组件」的 Operator。
  • CRD(Custom Resource Definition,自定义资源定义):K8s 里扩展 API 的方式。ClusterPolicy 是 GPU Operator 定义的 CRD,描述「集群 GPU 该怎么配置」。

GPU Operator 全家桶

组件 作用
NVIDIA Driver 节点 GPU 驱动(让操作系统能用 GPU)
NVIDIA Container Toolkit 容器内用 GPU 的运行时(把 GPU 设备映射进容器)
Kubernetes Device Plugin 上面讲的资源注册(注意:Operator 包含 device-plugin)
GPU Feature Discovery (GFD) 自动给节点打 GPU 标签(型号/算力/拓扑)
DCGM-Exporter GPU 指标采集(给 Prometheus,见 11
Node Feature Discovery (NFD) 节点硬件特征发现

纠错回顾(来自转岗指南):元宝说「Operator 和 device-plugin 是并列关系」——。GPU Operator 内含 device-plugin,是包含关系。面试别答错。

官方文档:https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/


4.4 实战:部署 GPU Operator(Helm)

# 添加 repo
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# 安装(用 ClusterPolicy 管理全套组件)
helm install gpu-operator nvidia/gpu-operator \
  -n gpu-operator --create-namespace

# 观察 Pod(每个 GPU 节点会有个 device-plugin 容器)
kubectl get pods -n gpu-operator

验证 GPU 成为资源:

kubectl describe node <gpu-node> | grep -A3 "Allocatable"
# 应看到 nvidia.com/gpu:  8

# 提交一个用 1 张卡的 Pod
cat <<EOF | kubectl apply -f -
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 }
  restartPolicy: Never
EOF

kubectl logs gpu-test   # 应打印出 GPU 信息

无真实 GPU 时,可用社区 fake-device-plugin 或 CPU 模拟节点验证流程(文档注明即可),重点是把「资源注册→调度→Pod 拿到卡」的链路跑通。


4.5 深入:Device Plugin gRPC 接口与 Linux 内核机制(零基础必读)

上面讲了「Device Plugin 注册资源」,但零基础一定会追问:kubelet 怎么发现 GPU?gRPC 调了哪些接口?容器里怎么出现 /dev/nvidia*?这用到了 Linux 内核的什么机制? 这一节从头讲透。

4.5.1 kubelet 怎么发现 GPU 物理卡

kubelet 本身不直接检测 GPU 硬件——它只认 device-plugin 上报的资源。真正检测 GPU 的是 NVIDIA device-plugin 这个 DaemonSet Pod:

  • 它启动后调用 NVIDIA 驱动的管理接口(nvidia-smi / NVML 库)扫描节点上的 GPU。
  • 发现几张卡、每张卡的 UUID、显存大小等信息。
  • 然后通过 gRPC 把这些信息上报给 kubelet。

💡 类比:kubelet 像「物业前台」,不自己数楼里有几台电梯;device-plugin 像「电梯巡检员」,数完到前台登记。前台只信登记的数字。

4.5.2 Device Plugin 的 gRPC 接口(kubelet 调 device-plugin)

Device Plugin 实现 K8s 定义的 Device Plugin APIpluginregistration + DevicePlugin 两组 gRPC 服务)。核心接口:

gRPC 接口 谁调谁 作用
Register device-plugin → kubelet 插件启动时注册自己,声明资源名(如 nvidia.com/gpu
ListAndWatch kubelet → device-plugin 持续监听设备列表和健康状态(流式 gRPC)
Allocate kubelet → device-plugin Pod 被调度到本节点时,问「这个容器要挂载哪些设备文件/环境变量」
GetDevicePreference(可选) kubelet → device-plugin 返回设备偏好(如拓扑亲和)
PreStartContainer(可选) kubelet → device-plugin 容器启动前的准备工作

工作流程(一次 Pod 调度到拿到 GPU 的完整链路):

① device-plugin 启动 → 调 NVML 扫描 GPU → 调 Register 向 kubelet 注册
② kubelet 持续调 ListAndWatch → device-plugin 上报「8 张卡,UUID 分别是...」
③ kubelet 把 nvidia.com/gpu: 8 写入 Node.status.allocatable
④ kube-scheduler 看到 allocatable 里有 GPU → Pod 被调度到这个节点
⑤ kubelet 收到 Pod → 调 Allocate(请求 1 张卡)
⑥ device-plugin 返回:「把 /dev/nvidia0 + 驱动库 + 环境变量挂进容器」
⑦ kubelet 把这些信息传给容器运行时(containerd/CRI-O)
⑧ 容器运行时用 NVIDIA Container Toolkit 执行挂载 → 容器启动
⑨ 容器内 nvidia-smi 能看到 1 张卡(其余 7 张不可见)

关键认知:device-plugin 只返回「该挂哪些设备文件」,真正执行挂载的是容器运行时 + NVIDIA Container Toolkit。device-plugin 是「出主意的」,Container Toolkit 是「干活的」。

4.5.3 容器里为什么能用 GPU(NVIDIA Container Toolkit + Linux 内核机制)

这是零基础最懵的点:容器是个隔离环境,为什么里面的程序能用到宿主机的 GPU?

答案在一个关键组件:NVIDIA Container Toolkit(原名 nvidia-docker2)。

Device Plugin 工作流程

它做了什么:它是一个容器运行时钩子(runtime hook)。当 kubelet/容器运行时(containerd/CRI-O)发现 Pod 申请了 nvidia.com/gpu,Toolkit 会把宿主机的以下三类东西按需挂载进容器

  1. GPU 设备文件/dev/nvidia0/dev/nvidiactl 等——这是 Linux 的设备文件机制/dev/ 下的特殊文件,访问它们就是访问硬件)。
  2. NVIDIA 驱动内核模块的库:如 /usr/lib/x86_64-linux-gnu/libnvidia-*——用户态程序通过这些库调驱动。
  3. CUDA 用户态库:如 libcudart.so——AI 框架(PyTorch)通过它调 GPU。

用到的 Linux 内核机制

  • device cgroup:Linux cgroup 的设备子系统控制「容器能看到哪些 /dev/ 设备」。Toolkit 让容器只看到分配给它的那张卡(如 /dev/nvidia0),其余卡在容器里不可见——这是设备级隔离
  • bind mount:把宿主机的设备文件和库「绑定挂载」到容器的对应路径,容器看到的是「自己有这些文件」,实际读的是宿主机。
  • namespace:容器的 mount namespace 让这些挂载只对该容器可见。

💡 类比:容器像一间密封机房,默认看不到外面的设备;Toolkit 像「在机房墙上开个口子,把 GPU 的线接进来」。申请了 nvidia.com/gpu:1 就是告诉它「开一个口子接一张卡」,device cgroup 确保「只接这一张,其余 7 张的线不接进来」。

面试要点:能讲清「device-plugin 返回设备列表 → Container Toolkit 用 cgroup device + bind mount 把设备文件和驱动库挂进容器 → 容器内程序通过 CUDA 库调 GPU」这条链,就显得有深度。

4.5.4 没有 Toolkit 会怎样

没有它,容器里 nvidia-smi 会报「没找到 GPU」;PyTorch torch.cuda.is_available() 返回 False。GPU Operator 自动装 Toolkit,所以你装了 Operator 就不用手动配。

4.5.5 CDI 标准(未来方向)

K8s 社区正在推进 CDI(Container Device Interface)——目标是让「设备声明」与厂商解耦,未来 device-plugin 走 CDI 标准化接口,不再各厂商各自为政。目前 CDI 处于推进阶段,NVIDIA Container Toolkit 已支持 CDI 输出,但生态完全切换还需时间。


4.6 多厂商接入(异构集群,呼应 02

如果集群里有 NVIDIA + AMD 卡(中国场景常还有昇腾):

  • NVIDIA GPU Operator(注册 nvidia.com/gpu
  • AMD ROCm device-plugin / AMD GPU Operator(注册 amd.com/gpu
  • 国产卡(昇腾):装华为对应的 device-plugin(注册如 huawei.com/ascend-910b
# AMD GPU Operator(推荐,一键管理驱动 + device-plugin + 监控)
helm repo add amd-gpu-operator https://helm.rocm.github.io/amd-gpu-operator
helm install amd-gpu-operator amd-gpu-operator/amd-gpu-operator -n kube-system

# 方式二:单独部署 ROCm device-plugin(DaemonSet 形式)
# 注意:manifest 路径随仓库迭代可能变动,部署前先到官方仓库核实最新地址:
# https://github.com/ROCm/k8s-device-plugin
kubectl apply -f https://raw.githubusercontent.com/ROCm/k8s-device-plugin/master/docker-compose/yaml/amd-device-plugin.yml

⚠️ 上述 raw URL 路径可能随仓库结构调整变动,以官方仓库 README 的最新 manifest 为准。注册后的资源名为 amd.com/gpu

重点(回应你原始问题):你说「有多家 GPU 就要装多个 plugin」——完全正确。每个厂商提供自己的 device-plugin,把自家卡注册成对应扩展资源。AI Infra 工程师要处理的是「不同厂商卡怎么共存、怎么按任务调度到合适的卡」——这正是 08 工业调度器的价值。

⚠️ 内幕:异构接入的「坑」不在装 plugin,而在框架适配——算法方的 PyTorch 默认认 CUDA,跑到昇腾上要换 CANN 适配层,算子可能缺失导致训不动。所以异构集群常按「卡类型分节点池 + 污点/亲和」隔离,避免 Pod 误调度到不兼容的卡上。


4.7 GFD 标签(让调度能「认卡」)

GPU Feature Discovery (GFD) 给节点自动打标签,例如:

  • nvidia.com/gpu.present=true
  • nvidia.com/gpu.product=NVIDIA-A100-SXM4-80GB
  • nvidia.com/gpu.machine=...

调度器/Volcano 可用这些标签做节点亲和性(Node Affinity)——「这个任务只跑在 A100 上」。

名词解释节点亲和性(Node Affinity) 是 K8s 里「让 Pod 调度到满足特定标签的节点」的机制,你应该很熟。污点/容忍(Taint/Toleration) 是反过来「把某些节点排除掉除非 Pod 明确容忍」。异构集群常用污点把不同卡型节点隔开。

kubectl get node <gpu-node> --show-labels | tr ',' '\n' | grep nvidia

4.8 异构算力统一编排抽象(多厂商集群的「屏蔽层」)

4.6 讲了「装多个 device-plugin 注册不同卡」,但真实异构集群要解决的更深问题是:怎么屏蔽 N/AMD/国产卡的差异,给上层一个统一接口——这是「异构算力统一编排」的核心,也是火山引擎/AI Infra 岗位的高频题。

异构统一编排四层抽象

差异在哪

  • 资源名不同(nvidia.com/gpu vs amd.com/gpu vs huawei.com/ascend-910b
  • 驱动/运行时不同(CUDA vs ROCm vs CANN)
  • 框架适配不同(PyTorch 默认认 CUDA,跑昇腾要换适配层)
  • 监控指标不同(DCGM 只管 N 卡,国产卡各有自家工具)

统一编排的几层抽象

  1. 统一资源模型:把不同卡抽象成统一「算力单元」(如按 FP16 算力/显存量归一化),调度器按能力而非卡型分配。
  2. CDI(Container Device Interface):K8s 社区正在推进的设备接入标准,目标是让「设备声明」与厂商解耦,未来 device-plugin 走 CDI 标准化。
  3. Kueue 的 ResourceFlavor:用 ResourceFlavor CRD 抽象「资源形态」(如 nvidia-h100ascend-910b),队列按 flavor 申请,调度器按节点标签匹配——这是当前最实用的异构抽象方式。
  4. 统一调度:调度器识别卡型标签(GFD 打的),按任务要求的「能力」匹配(如「要 FP8 算力」→ 选 H100/B200,不绑定具体型号)。
  5. 统一监控层:在 DCGM/国产工具之上建一层指标归一化(统一字段:利用率/显存/温度/算力),给 Prometheus/Grafana 统一视图。

面试要点:能说清「异构编排 = 资源模型 + 接入标准 + 调度抽象 + 监控归一」四层,并指出当前最实用的是 Kueue ResourceFlavor + GFD 标签 + 统一指标层,就显得有体系。


4.9 资源名的演进:从整卡到切分(引出 05

原生 device-plugin 只支持「整卡分配」(nvidia.com/gpu: 1 = 一整张卡)。但很多小任务用不满整张卡 → 浪费。于是有:

  • MIG:把卡切成硬件实例,注册成 nvidia.com/mig-1g.10gb 等资源(见 05)。
  • qGPU / vGPU:软件级细粒度切分,注册成带算力百分比的资源。

⭐ 这就是「资源接入层」从「看见卡」到「精细分配卡」的演进,下一章 05 专门讲。


4.10 实战:在 MacBook/Linux 上模拟 K8s GPU 接入全链路

你没有真实 GPU 完全没关系——这一节用 kind(Kubernetes in Docker)+ fake-device-plugin 在 MacBook 或 Linux 服务器上跑通「GPU 发现→注册→调度→Pod 拿到设备」的完整链路。重点是理解机制,不是真算模型。

4.10.1 环境准备(MacBook / Linux 通用)

# ① 安装 Docker Desktop(Mac)或 Docker Engine(Linux)
# Mac: 下载 Docker Desktop https://www.docker.com/products/docker-desktop
# Linux: curl -fsSL https://get.docker.com | sh
docker --version  # 确认安装成功

# ② 安装 kind(Kubernetes in Docker,本地轻量集群)
# Mac:
brew install kind
# Linux:
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/
kind version  # 确认安装成功

# ③ 安装 kubectl(K8s 命令行工具)
# Mac:
brew install kubectl
# Linux:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/
kubectl version --client  # 确认安装成功

# ④ 安装 Helm(K8s 包管理器,装 GPU Operator 要用)
# Mac:
brew install helm
# Linux:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version  # 确认安装成功

每步的目的:Docker 提供 kind 运行的容器环境;kind 在 Docker 里跑一个完整 K8s 集群(控制面+节点都在容器里);kubectl 是你操作集群的命令行;Helm 用来一键安装复杂应用(如 GPU Operator)。

4.10.2 创建 kind 集群

# 创建一个 1 主 1 节点的 kind 集群
cat <<EOF | kind create cluster --name gpu-lab --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker       # 这个 worker 节点用来模拟 GPU 节点
EOF

# 等待集群就绪(约 30 秒)
kubectl get nodes
# NAME                    STATUS   ROLES           AGE   VERSION
# gpu-lab-control-plane   Ready    control-plane   30s   v1.30.0
# gpu-lab-worker          Ready    <none>          30s   v1.30.0

# 验证 kubelet 在运行
kubectl get pods -n kube-system | grep kube-proxy

目的:你现在有了一个完整 K8s 集群。gpu-lab-worker 节点将模拟「有 GPU 的节点」。

4.10.3 安装 fake-device-plugin(模拟 GPU 注册)

没有真实 GPU,我们用社区的 fake-device-plugin——它伪装成一个设备插件,注册假的 GPU 资源到节点上,让 K8s 以为这个节点有 GPU。

# 创建 fake-device-plugin 的 DaemonSet
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fake-gpu-device-plugin
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: fake-gpu-device-plugin
  template:
    metadata:
      labels:
        app: fake-gpu-device-plugin
    spec:
      containers:
      - name: device-plugin
        image: registry.k8s.io/e2e-test-images/fake-device-plugin:1.0
        # 这个镜像实现了 K8s Device Plugin gRPC 接口
        # 但注册的不是真 GPU,而是假的 "fake.example.com/device"
        resources:
          limits:
            memory: "50Mi"
            cpu: "10m"
EOF

# 等待 Pod 运行
kubectl get pods -n kube-system -l app=fake-gpu-device-plugin -w
# 等到 STATUS 为 Running

# ★ 验证:假设备已注册为节点资源
kubectl describe node gpu-lab-worker | grep -A5 "Allocatable"
# 你应该看到类似:
#   fake.example.com/device:  2
# 这就是 Device Plugin 机制的效果——节点上「多了一种资源」

目的:这一步完美模拟了 4.5 讲的链路——device-plugin 启动 → 调 gRPC Register 向 kubelet 注册 → kubelet 持续 ListAndWatch 上报设备列表 → 资源进入 Node.status.allocatable。虽然设备是假的,但 K8s 的注册机制和真实 GPU 完全一样

4.10.4 提交 Pod 申请「GPU」

# 提交一个申请假设备的 Pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: gpu-test-pod
spec:
  containers:
  - name: test
    image: busybox:1.36
    command: ["sh", "-c", "echo '我拿到了 GPU 设备!'; sleep 3600"]
    resources:
      limits:
        fake.example.com/device: 1   # 申请 1 个假 GPU
  restartPolicy: Never
EOF

# 看 Pod 状态
kubectl get pod gpu-test-pod -w
# 等到 Running

# 看 Pod 日志
kubectl logs gpu-test-pod
# 应输出:我拿到了 GPU 设备!

# ★ 验证 Pod 确实被分配了设备
kubectl describe pod gpu-test-pod | grep -A5 "Limits"
# 应看到 fake.example.com/device: 1

目的:这一步模拟了「Pod 申请 GPU → 调度器找到有资源的节点 → kubelet 调 Allocate → 设备分配给容器」的全链路。真实 GPU 唯一多了的是 NVIDIA Container Toolkit 挂载设备文件,但调度和分配机制完全一致。

4.10.5 观察调度器行为

# 看调度器把 Pod 调度到了哪个节点
kubectl get pod gpu-test-pod -o wide
# NODE 列应显示 gpu-lab-worker(因为只有它有 fake 设备)

# 再提交一个 Pod,申请 3 个设备(但节点只有 2 个)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: gpu-oversub
spec:
  containers:
  - name: test
    image: busybox:1.36
    command: ["sleep", "3600"]
    resources:
      limits:
        fake.example.com/device: 3   # 申请 3 个,但只有 2 个
EOF

# 这个 Pod 会一直 Pending(资源不够)
kubectl get pod gpu-oversub
# STATUS 应为 Pending

# 看 Pending 原因
kubectl describe pod gpu-oversub | tail -5
# 应看到:0/2 nodes are available: 2 Insufficient fake.example.com/device

目的:这一步让你亲眼看到「资源不够 → Pod Pending」——这就是调度器 Filter 阶段的作用。真实 GPU 集群里,卡不够时 Pod 也会这样 Pending。

4.10.6 安装 GPU Operator(观察组件结构)

# 添加 NVIDIA Helm 仓库
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

# 安装 GPU Operator(在没有真实 GPU 的节点上会部分组件报错,但能观察组件结构)
helm install gpu-operator nvidia/gpu-operator \
  -n gpu-operator --create-namespace \
  --set driver.enabled=false \
  --set toolkit.enabled=false

# 观察部署了哪些组件
kubectl get pods -n gpu-operator
# 你会看到:gpu-operator-xxx(Operator 本体)
# device-plugin 在没有 GPU 的节点不会调度
# 这就是 4.3 讲的「全家桶」——即使不工作,也能看到组件结构

# 看完整的 CRD
kubectl get crd | grep nvidia
# 会列出 ClusterPolicy 等 GPU Operator 定义的 CRD

# 清理(避免占用资源)
helm uninstall gpu-operator -n gpu-operator
kubectl delete namespace gpu-operator

目的:即使没有 GPU,安装 GPU Operator 能让你看到它的完整组件架构(4.3 的表格变成活的)。driver.enabled=falsetoolkit.enabled=false 跳过真实驱动安装,避免在没有 GPU 的节点上报错。

4.10.7 验证 Device Plugin gRPC 机制

# 进入 kind worker 节点,看 device-plugin 的 gRPC socket
docker exec -it gpu-lab-worker ls /var/lib/kubelet/device-plugins/
# 你会看到 fake-device-plugin 注册的 socket 文件
# 这就是 kubelet 和 device-plugin 通信的 gRPC 端点

# 看 kubelet 日志中的设备注册记录
docker exec -it gpu-lab-worker journalctl -u kubelet 2>/dev/null | grep -i device | tail -10
# 或通过 kind 的方式看 kubelet 日志
kubectl logs -n kube-system -l app=fake-gpu-device-plugin

目的:亲眼看到 /var/lib/kubelet/device-plugins/ 下的 socket 文件——这就是 4.5.2 讲的 Device Plugin gRPC 通信端点。kubelet 通过这个 socket 调 ListAndWatchAllocate

4.10.8 清理实验环境

# 删除测试 Pod
kubectl delete pod gpu-test-pod gpu-oversub --ignore-not-found
kubectl delete ds fake-gpu-device-plugin -n kube-system --ignore-not-found

# 删除整个 kind 集群(释放资源)
kind delete cluster --name gpu-lab

4.10.9 实战总结

你做了什么 对应文档章节
创建 kind 集群 模拟 K8s 集群环境
安装 fake-device-plugin 4.2/4.5 Device Plugin 机制
验证节点 allocatable 有 GPU 资源 4.5.2 gRPC 注册链路
提交 Pod 申请 GPU 4.4 实战验证
观察 Pending Pod 4.10.5 调度器 Filter
安装 GPU Operator 观察组件 4.3 全家桶
看 device-plugins socket 4.5.2 gRPC 通信端点

核心收获:即使没有真实 GPU,你跑通了「device-plugin 注册 → kubelet 上报 → 调度器分配 → Pod 拿到设备」的完整链路。真实 GPU 的唯一区别是多了 NVIDIA Container Toolkit 挂载 /dev/nvidia* 和 CUDA 库——调度和分配机制完全一样。


4.11 自测题

  1. K8s 原生为什么不能直接调度 GPU?靠什么机制解决?
  2. Device Plugin 和 CSI/CNI 在设计思想上有什么共性?它负责什么、不负责什么?
  3. GPU Operator 包含哪些核心组件?它和 device-plugin 是什么关系?(包含,非并列)
  4. ⭐ 画出「Pod 申请 GPU → 拿到卡」的完整链路(从 device-plugin 扫描到容器内 nvidia-smi 可见)。
  5. Device Plugin 的 gRPC 接口有哪些?ListAndWatchAllocate 分别干什么?
  6. ⭐ 容器里为什么能用 GPU?NVIDIA Container Toolkit 用了 Linux 的哪些内核机制(cgroup device / bind mount / namespace)?
  7. 一条命令验证节点上 GPU 成了可调度资源,应该看什么字段?
  8. 集群同时有 N 卡、AMD 卡、昇腾卡,资源名分别是什么?要装几个 plugin?
  9. GFD 打的标签在调度中起什么作用?异构集群为什么常用污点隔离?
  10. ⭐ 异构集群「卡多但用不好」的根因通常在接入层还是框架适配层?为什么?
  11. 异构统一编排的四层抽象是什么?当前最实用的是哪个?
  12. CDI 是什么?它要解决什么问题?

答上即可进入 05(怎么把一张大卡切小、给多团队共享)。

← 返回专栏