一个AI训练任务从开发者电脑提交到真正开始使用GPU,中间并不是简单的“Scheduler找一块空闲GPU”。Kubernetes首先处理的是Pod、节点和资源请求,GPU则通过Device Plugin等机制被转换成Kubernetes能够识别的扩展资源。等Scheduler选定节点以后,kubelet、GPU Device Plugin和容器运行时还要继续完成设备分配和注入,最终容器中的CUDA程序才能访问节点上的NVIDIA GPU。
第一步通常从Pod提交开始。开发者通过Kubernetes API创建Pod、Job或更高级的工作负载对象。对于普通NVIDIA GPU任务,资源声明可能类似于:
resources:
limits:
nvidia.com/gpu: 1
这里的nvidia.com/gpu是扩展资源,不是一个普通CPU核心,也不是直接写入“A100”或者某个物理GPU插槽。它表达的是这个Pod需要一块由相应GPU资源插件提供的GPU设备。
如果任务对GPU型号有要求,例如只能运行在某一类GPU上,则通常需要结合节点标签和Node Affinity等机制进行约束。GPU Feature Discovery等组件可以把GPU型号、架构和其他硬件特征转换成节点标签,使Kubernetes能够根据这些属性选择合适的节点。
因此,Pod提交以后,Scheduler首先面对的问题不是“哪块GPU的显存还剩多少”,而是“哪个节点满足这个Pod的资源和调度约束”。CPU、内存、GPU扩展资源、节点标签、Affinity、Taints和Tolerations等条件都会影响最终的节点选择。
GPU资源能够被Kubernetes识别,依赖节点侧的Device Plugin机制。以NVIDIA环境为例,NVIDIA Kubernetes Device Plugin向kubelet注册GPU设备资源,节点随后能够向Kubernetes报告相应的GPU容量和可分配资源。Scheduler利用这些扩展资源进行节点筛选。
这里有一个非常容易混淆的地方:GPU的实时利用率并不是普通Kubernetes Scheduler默认的GPU资源模型。假设一台节点有8块GPU,其中4块已经分配给Pod,Kubernetes可以知道还剩多少可分配GPU资源,但它并不会因为DCGM发现某块已经分配的GPU当前只有10% utilization,就自动把这块GPU当成一块“空闲GPU”重新分配给另一个Pod。
这就是GPU调度与GPU监控之间的区别。DCGM以及DCGM Exporter可以提供GPU utilization、显存使用量、温度、功耗等运行状态,而Scheduler主要依据Kubernetes资源模型进行调度。想让调度决策真正考虑实时GPU utilization、显存占用或者任务吞吐量,需要额外的调度器扩展、指标系统或者专门的GPU调度方案。
节点确定以后,真正的GPU设备分配还没有结束。kubelet会根据Pod的资源需求与节点侧Device Plugin提供的信息完成设备管理,并通过容器运行时启动容器。现代NVIDIA容器环境还会涉及NVIDIA Container Toolkit以及Container Device Interface,也就是CDI等机制,用于把已经分配的GPU设备正确提供给容器。
因此可以把整个过程拆成几个阶段:API Server接收工作负载;Scheduler选择节点;kubelet在目标节点执行Pod;Device Plugin参与GPU设备分配;containerd或CRI-O等容器运行时创建容器;NVIDIA Container Toolkit/CDI完成GPU设备和相关用户态环境的注入;最终CUDA程序通过节点上的NVIDIA Driver访问GPU。
这里还需要特别区分CUDA Runtime和NVIDIA Driver。AI镜像通常可以包含CUDA用户态库以及PyTorch等软件,而真正控制物理GPU的NVIDIA Driver属于节点侧GPU软件栈。容器获得的是访问GPU所需的设备和用户态运行环境,并不是每个Pod都拥有一套独立的物理GPU驱动。
对于普通单GPU任务,这个流程已经足够使用。但当任务需要多块GPU时,调度问题开始复杂起来。假设一个训练任务需要8块GPU,简单地找到8块可用GPU并不一定意味着性能理想。GPU之间可能存在PCIe、NVLink或者NVSwitch等不同连接关系,CPU NUMA布局和高速网络接口的位置也会影响多GPU通信。
特别是分布式训练任务通常会使用NCCL进行GPU间通信。此时,8块GPU是否位于适合高速通信的拓扑结构中,比单纯的“GPU数量=8”更加重要。如果GPU分布在不同PCIe层级,或者通信路径需要经过较慢的连接,那么任务虽然能够正常启动,AllReduce等通信操作的效率却可能受到影响。
因此,高级GPU调度系统通常会进一步考虑GPU拓扑。但这并不意味着Kubernetes默认Scheduler已经自动理解每一个GPU之间的完整通信关系。生产环境可以通过节点标签、Affinity以及拓扑相关机制表达部分约束,也可以使用专门的GPU调度扩展。NVIDIA的DRA Driver for NVIDIA GPUs则提供了更加细粒度的GPU资源选择能力,可以根据GPU型号、显存、架构、UUID和拓扑等属性表达资源需求。
显存也是另一个容易被误解的问题。普通的nvidia.com/gpu: 1并不等于“自动申请这块GPU的全部显存,并由Kubernetes实时管理显存”。如果一个模型加载后需要超过GPU实际可用显存,最终仍可能在CUDA层面发生Out Of Memory。Kubernetes不会因为发现某块GPU显存使用率已经达到95%,就自动把新任务搬到另一块GPU。
如果希望多个工作负载共享GPU,就需要使用具体的GPU共享机制。例如,支持MIG的GPU可以划分成多个GPU实例;Time-Slicing可以让多个工作负载共享GPU执行时间。这些机制改变了Kubernetes看到的资源形态,也改变了GPU隔离和性能特征。它们不能简单归结为“给每个容器分配一定比例显存”。
GPU与存储之间也存在类似的误解。GPUDirect Storage可以在符合硬件、驱动、文件系统和存储配置条件的情况下缩短GPU与存储系统之间的数据路径,减少部分CPU参与。但这并不意味着Kubernetes Scheduler默认会检查某块GPU是否“直连某块NVMe SSD”,然后自动把任务安排在那里。要做到这种程度,必须把硬件拓扑、存储拓扑和工作负载特征纳入专门的资源调度体系。
云环境中的GPU扩容又是另一层问题。当集群已经没有满足条件的GPU节点时,Scheduler本身不会凭空制造一台GPU服务器。Pod可能保持Pending状态,随后由Cluster Autoscaler或者云厂商提供的节点自动扩展机制请求新的GPU节点。新的GPU节点还需要经历实例创建、操作系统启动、NVIDIA Driver准备、Device Plugin注册、镜像下载以及模型数据准备等过程。
这也是为什么AI平台经常会出现一种现象:Scheduler很快完成了调度决策,但用户仍然需要等待较长时间才能看到训练真正开始。真正耗时的可能不是Kubernetes Scheduler,而是GPU节点冷启动、驱动初始化、容器镜像下载、模型加载以及数据准备。
任务优先级和抢占也可以影响调度。当集群资源紧张时,Kubernetes Priority和Preemption机制可以根据优先级处理Pod之间的资源竞争。但它解决的是Pod调度层面的资源竞争,并不意味着Scheduler能够把已经运行的CUDA进程中的某部分显存“切出来”交给另一个任务。GPU抢占与CPU Pod抢占不是同一个概念,具体GPU共享能力仍然取决于底层GPU资源管理机制。
从实际AI平台建设来看,GPU调度最好分成三个层次理解。第一层是资源数量,例如任务需要1块还是8块GPU;第二层是资源类型,例如需要什么GPU架构、多少显存以及是否需要MIG;第三层是资源拓扑,例如多块GPU之间是否具有合适的NVLink或NVSwitch连接,以及GPU与CPU、NIC之间的NUMA和PCIe关系。
再往上,还有工作负载本身的特征。训练任务可能需要长时间占用多块GPU,并且对GPU间通信非常敏感;推理任务可能更关注延迟和显存容量;批处理任务则可能更关心吞吐量和资源利用率。相同的GPU硬件,在不同工作负载下也可能产生完全不同的调度需求。
所以,一个AI任务进入Kubernetes以后,并不是Scheduler简单地寻找“最空闲的GPU”。标准Kubernetes首先完成Pod级资源调度,GPU Device Plugin或DRA负责把GPU作为可管理的设备资源提供给集群,kubelet和容器运行时完成设备交付,NVIDIA Container Toolkit等组件负责让容器正确访问GPU,而NCCL、MIG以及其他GPU技术则解决通信和资源共享等更底层的问题。
真正成熟的AI调度系统,最终需要回答的不只是“哪台机器有GPU”,而是“什么GPU、多少显存、怎样的GPU拓扑、怎样的CPU和网络连接,以及这种硬件组合是否适合当前任务”。这也是AI工作负载进入Kubernetes以后,GPU调度逐渐从普通资源分配走向专用调度体系的原因。
AI 工作负载进入 Kubernetes 后如何被调度,从提交任务到获得 GPU 完整解释
图片说明:示意图 图片来源:Public Domain(公有领域)
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP