GPU 资源调度:k8s-device-plugin 知多少 ?

    Hello folks,我是 Luga,今天我们来聊一下人工智能应用场景 - 基于 k8s-device-plugin 机制所实现的 GPU 资源动态调度。

    近几年,随着大数据和人工智能技术的迅猛发展,AI 应用场景日益丰富,成为推动产业升级的重要驱动力。云计算作为 AI 发展的坚实基础,提供了强大的计算资源和灵活的部署环境。

     而 Kubernetes 作为云原生领域的旗舰项目,凭借其出色的可扩展性、高可用性和自动化能力,成为了管理云原生应用的事实标准。

     而在 AI 领域,GPU 作为加速计算的利器,其重要性不言而喻。云原生编排系统 Kubernetes 凭借其强大的资源调度能力,为 GPU 资源的管理提供了高效可靠的解决方案。

01 

如何理解 Kubernetes Device Plugin ?

     Kubernetes Device Plugin 是 Kubernetes 中的一种标准化资源扩展机制,旨在将特殊硬件(如 GPU、FPGA)作为可调度资源集成到 Kubernetes 的资源管理框架中。

    对于 NVIDIA GPU,Device Plugin 充当了连接 Kubernetes 调度器与底层 GPU 硬件的桥梁,负责发现、注册和管理 GPU 资源,使工作负载能够无缝利用 GPU 提供的计算能力。

apiVersion: v1kind: Podmetadata:  name: gpu-podspec:  restartPolicy: Never  containers:    - name: cuda-container      image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2      resources:        limits:          nvidia.com/gpu: 1   tolerations:  - key: nvidia.com/gpu    operator: Exists    effect: NoSchedule

     从本质上来讲,Kubernetes Device Plugin 设计目标是提供一个通用的接口,使硬件供应商能够开发自定义插件来支持其设备,同时确保与 Kubernetes 调度系统的良好兼容性。

     众所周知,Kubernetes 本身通过 Linux 的 namespace 和 cgroups 实现资源隔离与限制,将 CPU 和内存等资源按需分配给各个 Pod。例如,Kubernetes 可以根据节点的资源状况,将需要计算和存储的 Pod 调度到具有相应空闲资源的节点上,并为容器中的进程设置 CPU 和内存的配额。然而,当前 Kubernetes 内部尚不支持对 GPU 资源的直接调度和隔离,而是依赖于设备插件(Device Plugin) 和调度器扩展,通过第三方工具来完成这项功能。

     NVIDIA Kubernetes 设备插件 是 Kubernetes 中最常用的 GPU 设备插件之一,专门为 NVIDIA GPU 的资源分配与调度提供支持。其核心功能包括如下:

     1、自动发现 GPU 设备

     Device Plugin 能够自动检测节点中的 GPU 硬件,并将其作为资源注册到 Kubernetes API 中,使调度器能够识别和分配 GPU。

     2、资源暴露与分配

    Device Plugin 通过标准的 Kubernetes API,将 GPU 资源暴露给 Pods,使其能够根据配置请求 GPU 加速计算资源。

     3、硬件抽象

    通过屏蔽底层 GPU 设备的复杂性,Device Plugin 提供了一个高层次的抽象接口,让开发者专注于工作负载的逻辑,而无需关心硬件的具体细节。

     4、热插拔支持

    Device Plugin 支持动态设备注册,使得在集群运行时新增或移除 GPU 等硬件资源成为可能,大大增强了系统的灵活性。

     5、与调度器的无缝集成

    Device Plugin 能够与 Kubernetes 调度器协同工作,根据 Pod 的资源需求高效调度 GPU 资源,避免资源争抢或浪费。

02 

Kubernetes Device Plugin 实现原理解析

   当使用 Kubernetes 时,用户通常希望能够在 Pod 对象中直接声明所需的 GPU 资源,并通过 Kubernetes 的调度器(kube-scheduler)自动完成所需 GPU 的分配。例如,以下 Pod 清单中使用了 limits 字段来指定对 NVIDIA GPU 的资源需求:

apiVersion: v1  kind: Pod  metadata:    name: cuda-vector-add  spec:    restartPolicy: OnFailure    containers:      - name: cuda-vector-add        image: "k8s.gcr.io/cuda-vector-add:v0.1"        resources:          limits:            nvidia.com/gpu: 1  

     在上述配置中,nvidia.com/gpu 是声明 GPU 需求的一种特殊字段,称为 Extended Resource(扩展资源),其值为 1,表示该 Pod 请求一个 NVIDIA 类型的 GPU。通过这种方式,用户能够在 Pod 级别直观地表达对 GPU 资源的需求,极大地简化了对高性能计算资源的使用和管理。

    为了使 Kubernetes 能够正确地调度 GPU 资源,调度器需要了解每个节点上的 GPU 可用情况。GPU 资源的可用量通常通过 Node 对象 的 Status 字段报告。例如,一个节点对象可以包含以下状态信息:

apiVersion: v1  kind: Node  ...  Status:    Capacity:      cpu:  2      memory:  2049008Ki      nvidia.com/gpu: 1  

    在上述状态中,nvidia.com/gpu: 1 表示该节点拥有 1 个可用的 NVIDIA GPU。调度器根据这些信息,将 GPU 请求的 Pod 调度到满足条件的节点上。

    通常,在实际的业务场景中,为节点添加 GPU 资源的可用量信息,可以通过以下两种方式实现:

     1、手动更新 Extended Resource 

     Extended Resource 是一种基于节点级别(Node-level)的 API,允许用户直接更新节点的 Status 字段以报告 GPU 的可用资源。例如,使用 PATCH 请求更新某节点的 GPU 资源。

$ curl --header "Content-Type: application/json-patch+json" \    --request PATCH \    --data '[{"op": "add", "path": "/status/capacity/nvidia.com/gpu", "value": "1"}]' \    http://localhost:8001/api/v1/nodes/<your-node-name>/status  

    这种方式适用于简单场景,可以独立于设备插件(Device Plugin)使用。通过手动更新节点的状态信息,调度器便可记录 GPU 类型及其可用数量。然而,这种方法需要额外的脚本化管理,维护成本较高,且不具备动态检测硬件变化的能力。

    2、使用 Device Plugin 自动维护

    更常见和推荐的方式是通过 Device Plugin 来完成 GPU 资源的自动管理。在 Kubernetes 中,所有硬件资源(如 GPU、NIC、TPU 等)的发现、报告和分配通常由 Device Plugin 统一管理。

      Device Plugin 会运行在每个节点上,自动检测硬件资源的类型和数量,并通过 Kubernetes API 动态更新节点的状态信息。

      在 Kubernetes 中,所有专用硬件资源的管理都依赖于 Device Plugin(设备插件) 框架进行扩展和实现。Device Plugin 提供了一种标准化的机制,用于发现、注册和管理节点上的专用硬件设备(例如 GPU、FPGA、NIC 等)。通过这一框架,Kubernetes 可以将这些硬件资源统一抽象为节点资源的一部分,从而纳入标准的调度和管理流程。

     不仅如此,Device Plugin 还负责汇报和维护 Extended Resources(扩展资源) 的状态,这些扩展资源是 Kubernetes 用于表示专用硬件资源的一种逻辑抽象形式。在扩展资源汇报的过程中,设备插件会基于节点上的硬件状态,动态更新资源的可用性和健康状况,确保调度器能够准确了解节点的资源分布情况。

     如下图所示,Device Plugin 在 Kubernetes 的资源管理体系中承担了关键角色,通过与 Kubelet 的紧密协作,将专用硬件资源的发现与分配无缝集成到 Kubernetes 的调度框架中,为分布式环境中的硬件资源调配提供了标准化的支持。这一机制不仅提升了资源管理的灵活性,还极大扩展了 Kubernetes 的硬件适配能力,使其能够支持各种复杂的异构计算场景,如 AI 训练、数据分析和高性能网络通信等。

图片

     通常而言,Kubernetes Device Plugin 的工作流程包括以下几个关键步骤,具体可参考:

     1、 设备发现

     Device Plugin 会扫描工作节点上的 GPU 设备,并通过 gRPC 接口向 kubelet 报告设备列表,包括每个设备的标识符和状态信息。  

     2、设备注册

    Device Plugin 向 kubelet 注册后,这些设备会被 Kubernetes API 服务器识别并记录,成为 Kubernetes 资源调度的一部分。例如,GPU 资源可能被标记为 nvidia.com/gpu。

    3、资源分配

   当一个 Pod 请求 GPU 时,调度器根据请求的资源规格将任务分配到具有可用 GPU 的节点上。kubelet 与 Device Plugin 协作,为 Pod 分配所需的 GPU 设备,并将设备信息挂载到容器中。

    4、生命周期管理

   Device Plugin 负责监控 GPU 的状态,确保其健康运行。当设备状态发生变化(如故障或移除)时,Device Plugin 会通知 kubelet,触发资源更新。

03 

Kubernetes Device Plugin 优劣势分析

    随着 Kubernetes 在异构计算场景(如 GPU、FPGA、NIC 等专用硬件资源)中的广泛应用,Device Plugin(设备插件)机制成为 Kubernetes 内部管理专用硬件资源的标准化扩展方案。作为 Kubernetes 原生的设备管理框架,Device Plugin 提供了一种高效、灵活且可扩展的机制,用于发现、注册和调度节点上的专用硬件资源。然而,尽管其在解决硬件资源调度难题方面表现出色,但仍然存在一定的局限性。

   尽管通过 Device Plugin,Kubernetes 在调度时可以一定程度上满足对 Extended Resource 的基本需求,但其调度机制主要基于设备数量的匹配。这种简单的资源分配方式在面对异构设备或复杂硬件属性需求时显得不足。例如,当硬件设备的属性多样化且 Pod 对某些特定硬件属性有明确要求时,单纯依赖数量匹配的调度策略无法提供所需的精细化调度能力。此外,在资源分配方面,Device Plugin 对于配额(Quota)的限制粒度较粗,难以满足更细化的资源管理需求。


    1、基于节点标签与节点选择器的调度方案

    为了弥补上述不足,一种常见的替代方案是使用 Node Label 和 Node Selector 来优化资源调度。通过为节点设置标签(Node Label)并在 Pod 配置中使用选择器(Node Selector),可以实现某种类型硬件的定向分配。例如,将同一类型的 GPU(如 NVIDIA Tesla K80)挂载到同一个节点,并通过节点标签将资源属性暴露给调度器,从而实现按类型资源的调度。然而,这种方式依然存在明显的限制:

  (1)属性覆盖不足:这种方法仅适用于同类型硬件资源的分配,无法支持更复杂的调度需求。例如,当一个节点上挂载了多块显存不同的 GPU(如 8GB 和 12GB),某些应用希望使用显存大于 10GB 的 GPU,这种需求无法通过节点标签和选择器实现。

  (2)灵活性不足:节点标签是一种静态配置,无法动态感知节点上硬件资源的变化。

    2、基于 CRD 和调度器扩展机制的解决方案

   为了解决上述问题,可以借助 Kubernetes 的 CustomResourceDefinition (CRD) 和 Scheduler Extender 机制。这种方法通过定义自定义资源(CR)来详细描述硬件资源的属性,并通过扩展调度器逻辑,实现对异构设备的精准调度。以下是具体思路:

  (1)定义 CRD 表达设备属性

    自定义资源定义(CRD)可用来表示设备的复杂属性,例如显存大小、计算能力(Compute Capability)等。节点上的设备插件负责动态发现这些属性并将其通过 CR 报告给 Kubernetes 集群。

  (2)扩展调度器逻辑

   使用 Scheduler Extender,可自定义调度逻辑,使调度器能够根据 Pod 对资源的特定需求(如显存大于 10GB)筛选符合条件的节点。这种方法通过外部扩展避免直接修改 Kubernetes 核心代码,同时提高调度的灵活性和可扩展性。

  (3)动态资源调度

    结合 CRD 和调度器扩展,设备插件能够动态报告硬件资源的状态,并根据 Pod 的资源请求进行智能调度。这种方法特别适合处理异构设备场景。

    尽管 CRD 和 Scheduler Extender 提供了更强大的功能,但它们的使用往往涉及到一定程度的 Kubernetes 源码修改或额外的开发投入:

  (1)复杂性增加:引入自定义资源和扩展调度器需要开发和维护额外的逻辑,对团队的技术能力提出了更高要求。

  (2)兼容性风险:扩展方案可能与 Kubernetes 的未来版本更新产生兼容性问题,增加了维护成本。

    综上所述,Kubernetes Device Plugin 是实现 GPU 资源调度的基础组件,通过提供标准化接口将 GPU 资源透明地暴露给 Kubernetes 调度器,为工作负载的 GPU 需求提供精准匹配。其轻量化设计专注于资源分配,简化了 GPU 与 Kubernetes 的集成,是满足基本 GPU 使用需求的理想选择。

    然而,Kubernetes Device Plugin 功能相对单一,无法覆盖驱动安装、高级功能配置等更复杂的运维需求。因此,它更适合已完成基础 GPU 环境搭建的场景,尤其是在对调度性能和资源开销要求较高的工作负载中展现了独特优势。对于寻求灵活、高效 GPU 资源调度的用户,Device Plugin 是一种低门槛、高适配性的解决方案。

    Happy Coding ~

Reference :

    Adiós !

··································

对云原生网关 Traefik 技术感兴趣的朋友们,可以了解一下我的新书,感谢支持!

Hello folks,我是 Luga,Traefik Ambassador,Jakarta EE Ambassador, 一个 15 年+ 技术老司机,从 IT 屌丝折腾到码畜,最后到“酱油“架构师。如果你喜欢技术,不喜欢呻吟,那么恭喜你,来对地方了,关注我,共同学习、进步、超越~