
Kubernetes 如何管理和调度 GPU从架构、核心组件到第一个 GPU Pod本文适合已经熟悉 Kubernetes 基础概念但还不了解 Kubernetes 如何使用和管理 GPU 的读者。文章标签Kubernetes、GPU、NVIDIA、Device Plugin、GPU Operator、AI Infra前言在普通 Kubernetes 集群中我们经常为容器配置 CPU 和内存resources:requests:cpu:2memory:4Gilimits:cpu:4memory:8Gi到了 AI 训练或模型推理场景工作负载还需要使用 GPU。看起来似乎只要再增加一行nvidia.com/gpu: 1就可以了resources:limits:nvidia.com/gpu:1但这一行配置背后实际上涉及 GPU Driver、Container Runtime、Device Plugin、Kubelet、Scheduler 等多个组件。本文将回答以下几个问题Kubernetes 本身是否认识 GPUGPU 是如何变成 Kubernetes 可调度资源的Scheduler 如何选择 GPU NodeGPU 是如何被分配给 Container 的怎样运行第一个使用 GPU 的 Pod一、先说结论Kubernetes 本身并不直接管理 GPUKubernetes 原生认识的核心计算资源主要是cpumemoryephemeral-storageGPU 属于厂商相关的特殊硬件。Kubernetes 不会自己安装 NVIDIA Driver也不知道某张 GPU 的 Device ID更不会直接把/dev/nvidia0挂载给 Container。Kubernetes 通过一套可扩展机制管理 GPUGPU Vendor 提供 Device Plugin将 GPU 注册成 Kubernetes Extended ResourceScheduler 根据资源数量选择 NodeKubelet 再调用 Device Plugin 完成具体设备分配。对于 NVIDIA GPU集群中通常会看到下面这个资源名nvidia.com/gpu例如某个 Node 有 4 张可用 GPUNode Status 中会出现capacity:nvidia.com/gpu:4allocatable:nvidia.com/gpu:4从 Kubernetes 的视角看nvidia.com/gpu与 CPU、内存类似都是可以被 Scheduler 计算的资源但它属于Extended Resource由外部 Device Plugin 上报。二、Kubernetes 管理 GPU 的整体架构可以把整个架构分成四层。1. Hardware Layer最底层是真实的 GPU Hardware例如 NVIDIA T4、L40S、A100 或 H100。这一层提供GPU ComputeGPU MemoryPCIe、NVLink 或 NVSwitch 等互联能力2. GPU Enablement Layer这一层把 GPU Hardware 变成 Container 可以使用的资源核心组件包括NVIDIA DriverNVIDIA Container ToolkitNVIDIA Device Plugin它们分别解决主机访问 GPU、Container 使用 GPU、Kubernetes 发现和分配 GPU 的问题。3. Kubernetes Layer主要涉及API ServerSchedulerKubeletAPI Server保存 Node 和 Pod 的状态Scheduler选择有空闲 GPU 的 NodeKubelet在目标 Node 上调用 Device Plugin 并创建 Container。4. Workload Layer最上层是实际的 AI 工作负载例如Training JobInference ServiceJupyter NotebookBatch Inference应用不需要直接指定某张物理卡只需要在 Pod Spec 中声明 GPU 数量。三、核心组件介绍1. NVIDIA DriverNVIDIA Driver运行在 GPU Node 的 Host OS 上负责操作系统与 GPU Hardware 之间的通信。在 Node 上执行下面的命令可以验证 Driver 是否正常nvidia-smi如果 Host 上的nvidia-smi都无法正常工作那么 Kubernetes 上层组件也不可能正常使用 GPU。需要注意Host Driver 和 Container 中的 CUDA Runtime 必须兼容。很多CUDA driver version is insufficient错误本质上都是版本兼容问题。2. Container Runtime现代 Kubernetes 集群通常使用containerd或CRI-O作为 Container Runtime。普通 Container Runtime 只负责创建 Linux Container并不知道如何向 Container 暴露 GPU Device 和 NVIDIA Library因此还需要NVIDIA Container Toolkit。3. NVIDIA Container ToolkitNVIDIA Container Toolkit的作用是让 Container 能够使用 Host 上的 NVIDIA GPU。它主要负责将 GPU Device 暴露给 Container将必要的 NVIDIA Library 注入 Container配合 Container Runtime 创建支持 GPU 的 Container。可以简单理解为Device Plugin 决定“把哪张 GPU 分给这个 Pod”NVIDIA Container Toolkit 负责“让 Container 真正看见并使用这张 GPU”。4. NVIDIA Device PluginNVIDIA Device Plugin是 Kubernetes 管理 NVIDIA GPU 的关键组件通常以DaemonSet方式运行在每个 GPU Node 上。它主要负责三件事发现 Node 上的 GPU向 Kubelet 注册nvidia.com/gpu在创建 Pod 时为 Container 分配具体 GPU Device。查看 Device Pluginkubectl get pods-ngpu-operator\-lappnvidia-device-plugin-daemonset-owide不同安装方式下Namespace 和 Label 可能不同也可以直接搜索相关 DaemonSetkubectl get daemonset-A|grep-invidia5. Kubelet每个 Node 上的Kubelet负责管理本节点的 Pod。Device Plugin 会通过 gRPC 接口向 Kubelet 注册。注册成功后Kubelet 将 GPU 数量更新到 Node Status最终由 API Server 保存。当 GPU Pod 被调度到该 Node 时Kubelet 会调用 Device Plugin 的Allocate获得需要注入 Container 的设备和运行信息。6. SchedulerScheduler的职责是选择 Node而不是直接分配某一张物理 GPU。假设 Pod 请求limits:nvidia.com/gpu:1Scheduler 会过滤没有 GPU 的 NodeGPU 已经全部分配的 Node不满足nodeSelector或nodeAffinity的 NodePod 无法容忍其 Taint 的 NodeCPU、Memory 等其他资源也不满足的 Node。Scheduler 最后只做出一个决定这个 Pod 应该运行在哪个 Node 上。具体使用该 Node 上的哪张 GPU由 Kubelet 和 Device Plugin 在 Node 上完成。7. Node Feature Discovery / GPU Feature DiscoveryNode Feature Discovery简称NFD负责发现 Node Hardware Feature并将其转化为 Kubernetes Label。GPU Feature Discovery简称GFD可以生成与 GPU 相关的 Label例如GPU ProductGPU MemoryMIG CapabilityGPU CountSharing Strategy平台可以根据这些 Label把不同工作负载调度到不同 GPU Model 的 Node。8. DCGM ExporterDCGM Exporter将 GPU Metric 暴露给 Prometheus常用于监控GPU UtilizationGPU Memory UsageTemperaturePower UsageECC ErrorXid Error没有这一层平台虽然可以分配 GPU却很难回答“GPU 是否真的被有效使用”。9. GPU Operator如果逐个安装和维护上面的组件工作量会很大。NVIDIA GPU Operator使用 Operator Pattern 自动部署和管理 GPU 软件栈通常包括NVIDIA DriverNVIDIA Container ToolkitNVIDIA Device PluginGPU Feature DiscoveryDCGM ExporterMIG ManagerValidator因此GPU Operator 不是 GPU 调度器。它的核心价值是自动安装、配置和维护 GPU Node 所需的软件组件。四、一个 GPU Pod 到底是怎样运行起来的整个过程可以拆成六步。第 1 步Pod 声明 GPU Request用户提交 Podresources:limits:nvidia.com/gpu:1这里请求的是“一张由 NVIDIA Device Plugin 管理的 GPU”。在传统 Device Plugin 模式下GPU Extended Resource 必须按整数申请不能写成0.5。第 2 步API Server 保存 Pod SpecAPI Server 接收并保存 Pod但此时 Pod 还没有被分配 Node。第 3 步Scheduler 选择 GPU NodeScheduler 查看各 Node 的allocatable和已经分配的资源找到至少还有一张 GPU 的 Node然后将 Pod 与该 Node 绑定。第 4 步Kubelet 调用 Device Plugin目标 Node 上的 Kubelet 发现 Pod 需要 GPU于是调用 NVIDIA Device Plugin。Device Plugin 从健康 GPU 中选择一个具体 Device并返回设备分配结果。第 5 步Container Runtime 创建 ContainerContainer Runtime 配合 NVIDIA Container Toolkit把选中的 GPU Device 和 NVIDIA Library 注入 Container。第 6 步CUDA Application 使用 GPUContainer 启动后CUDA Application 可以看到分配给自己的 GPU并执行训练或推理。这也是为什么仅仅安装 Driver 还不够完整链路中的任何一层异常都可能导致 GPU Pod 失败。五、动手实验运行第一个 GPU Pod实验前提需要满足Kubernetes Cluster 中至少有一个 NVIDIA GPU NodeHost 上的 NVIDIA Driver 正常NVIDIA Container Toolkit 已正确配置NVIDIA Device Plugin 或 GPU Operator 已安装当前用户拥有创建 Pod 和查看日志的权限。第 1 步确认 Kubernetes 已发现 GPU执行kubectl get nodes\-ocustom-columnsNAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu预期输出类似NAME GPU worker-node-01 none gpu-node-01 1如果 GPU Node 显示none或0暂时不要继续创建 Pod应先检查 Driver、Device Plugin 和 Kubelet。也可以查看完整的 Node Resourcekubectl describenodegpu-node-01重点关注Capacity: nvidia.com/gpu: 1 Allocatable: nvidia.com/gpu: 1第 2 步创建 GPU Pod创建文件gpu-pod.yamlapiVersion:v1kind:Podmetadata:name:cuda-vectoraddspec:restartPolicy:OnFailurecontainers:-name:vectoraddimage:nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda12.5.0-ubuntu22.04resources:limits:nvidia.com/gpu:1应用配置kubectl apply-fgpu-pod.yaml第 3 步观察 Pod 状态kubectl get pod cuda-vectoradd-w正常情况下可以看到NAME READY STATUS RESTARTS AGE cuda-vectoradd 0/1 Completed 0 20s这个示例是一次性计算任务因此最终状态通常是Completed而不是一直保持Running。第 4 步查看运行结果kubectl logs cuda-vectoradd日志会包含类似信息[Vector addition of 50000 elements] CUDA kernel launch with 196 blocks of 256 threads Test PASSED Done看到Test PASSED说明下面这条链路已经打通Pod Request → Scheduler → GPU Node → Kubelet → NVIDIA Device Plugin → NVIDIA Container Toolkit → NVIDIA Driver → GPU Hardware第 5 步查看 Pod 被调度到哪里kubectl get pod cuda-vectoradd-owide也可以查看 Eventkubectl describe pod cuda-vectoradd这两个命令非常重要。以后遇到 GPU Pod Pending第一步通常就是执行kubectl describe pod查看 Scheduling Event。六、为什么 GPU 通常只写在 limits 中Kubernetes 官方文档对 GPU Extended Resource 有以下规则可以只设置 GPUlimitsKubernetes 会将相同数值作为requests如果同时设置 GPUrequests和limits两者必须相等不能只设置 GPUrequests而不设置limits。因此最常见的配置是resources:requests:cpu:2memory:4Gilimits:cpu:4memory:8Ginvidia.com/gpu:1CPU 和 Memory 仍然应该合理设置否则即使拿到了 GPU应用也可能因 CPU、Memory 或数据处理不足而无法有效利用 GPU。七、如何控制 Pod 调度到特定 GPU Node只有一种 GPU 时仅声明nvidia.com/gpu可能已经足够。但生产集群通常包含不同 GPU Model或者需要把 GPU Node 与普通 Node 隔离。1. 使用 Node Label为 Node 添加自定义 Labelkubectl labelnodegpu-node-01acceleratornvidia-gpuPod 使用nodeSelectorspec:nodeSelector:accelerator:nvidia-gpu实际生产环境还可以使用 GFD 自动生成的 GPU Product Label但应先通过下面的命令确认真实 Label Key 和 Valuekubectl getnodegpu-node-01 --show-labels2. 使用 Taint 和 Toleration为了避免普通 Pod 占用昂贵的 GPU Node可以为 GPU Node 添加 Taintkubectl taintnodegpu-node-01 nvidia.com/gputrue:NoSchedule需要 GPU 的 Pod 增加 Tolerationspec:tolerations:-key:nvidia.com/gpuoperator:Equalvalue:trueeffect:NoSchedule需要注意Toleration 只是允许 Pod 进入带 Taint 的 Node并不保证它一定进入该 Node。因此通常还要结合 GPU Resource Request、Node Label 或 Node Affinity。八、常见问题与排查方向问题 1Pod 一直 Pending查看kubectl describe podpod-name如果 Event 中出现Insufficient nvidia.com/gpu可能原因包括GPU 已被其他 Pod 分配Node 没有正确上报 GPUDevice Plugin 异常Pod 请求的 GPU 数量超过单个 Node 的可用数量。问题 2Node 没有 nvidia.com/gpu按照下面的顺序排查# Host Drivernvidia-smi# NVIDIA componentskubectl get pods-A|grep-invidia# Node resourcekubectl describenodegpu-node# Device Plugin logskubectl logs-nnamespacedevice-plugin-pod问题 3Container 内无法使用 GPU如果 Node 已经上报 GPUPod 也被成功调度但应用无法使用 GPU应重点检查NVIDIA Container ToolkitContainer Runtime 配置CUDA Runtime 与 Host Driver 的兼容性Container Image 是否包含应用所需 CUDA Library。问题 4有 GPU但利用率很低这不一定是 GPU 调度问题还可能是CPU 数据预处理成为瓶颈Storage 吞吐不足Network 或分布式通信等待Batch Size 太小多个 Worker 之间存在 Straggler应用本身没有充分使用 GPU。因此 GPU 平台除了监控 GPU Utilization还应同时观察 CPU、Memory、Storage、Network 和业务吞吐。九、Device Plugin 之外的新机制DRA传统 Device Plugin 通常以整数形式申请 GPUnvidia.com/gpu:1随着 AI Infrastructure 对设备属性、拓扑和共享方式的需求不断增加Kubernetes 引入了Dynamic Resource Allocation简称DRA。DRA 使用的核心对象包括DeviceClassResourceClaimResourceClaimTemplate它允许工作负载用更加声明式的方式描述设备要求而不是只申请一个资源数量。DRA 从 Kubernetes v1.35 开始成为 Stable Feature。不过对于刚开始学习 GPU on Kubernetes 的读者建议先完全理解 Device Plugin 的资源链路再学习 DRA生产使用还需要确认 GPU Vendor 提供的 DRA Driver、Kubernetes Version 和现有运维体系是否兼容。十、总结Kubernetes 管理 GPU 的核心逻辑可以概括为NVIDIA Driver让 Host OS 能够访问 GPUNVIDIA Container Toolkit让 Container 能够使用 GPUNVIDIA Device Plugin将 GPU 注册为nvidia.com/gpuKubelet将 GPU Capacity 和 Allocatable 上报给 API ServerScheduler根据 GPU Request 选择合适的 NodeKubelet调用 Device Plugin 分配具体 GPUContainer Runtime 将 GPU Device 和 Library 注入 ContainerCUDA Application 最终使用 GPU 执行计算。最值得记住的一句话是Scheduler 负责选择 GPU NodeDevice Plugin 和 Kubelet 负责在 Node 上完成真实 GPU Device 的分配。理解这条主线后再继续学习 GPU Operator、MIG、Time-Slicing、Kueue、Kubeflow Trainer 和 KServe就会容易很多。参考资料Kubernetes - Schedule GPUshttps://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/Kubernetes - Device Pluginshttps://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/Kubernetes - Dynamic Resource Allocationhttps://kubernetes.io/docs/concepts/resource-management/dynamic-resource-allocation/NVIDIA GPU Operatorhttps://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/index.htmlNVIDIA k8s-device-pluginhttps://github.com/NVIDIA/k8s-device-plugin