设备插件

设备插件允许您配置集群,以支持需要供应商特定设置的设备或资源,例如 GPU、网卡、FPGA 或非易失性主内存。
功能状态: Kubernetes v1.26 [稳定]

Kubernetes 提供了一个设备插件框架,您可以使用它来向 Kubelet 宣传系统硬件资源。

供应商可以实现一个设备插件,您可以通过手动方式或作为 DaemonSet 部署该插件,而无需自定义 Kubernetes 本身的代码。目标设备包括 GPU、高性能网卡、FPGA、InfiniBand 适配器以及可能需要供应商特定初始化和设置的其他类似计算资源。

设备插件注册

kubelet 导出一个 Registration gRPC 服务

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

设备插件可以通过此 gRPC 服务向 kubelet 注册自身。在注册期间,设备插件需要发送

  • 其 Unix 套接字的名称。
  • 它构建的 Device Plugin API 版本。
  • 它想要宣传的 ResourceName。此处 ResourceName 需要遵循 扩展资源命名方案,即 vendor-domain/resourcetype。(例如,NVIDIA GPU 宣传为 nvidia.com/gpu。)

成功注册后,设备插件会将它管理的设备列表发送给 kubelet,然后 kubelet 负责将这些资源作为 kubelet 节点状态更新的一部分宣传到 API 服务器。例如,在设备插件注册 hardware-vendor.example/foo 并报告节点上的两个正常设备后,节点状态将更新为宣传该节点安装了 2 个“Foo”设备并且可用。

然后,用户可以在 Pod 规范中请求设备(请参阅 container)。请求扩展资源类似于管理其他资源的请求和限制,但存在以下差异

  • 扩展资源仅支持整数资源,并且不能超额承诺。
  • 设备不能在容器之间共享。

示例

假设 Kubernetes 集群正在运行一个设备插件,该插件在某些节点上宣传资源 hardware-vendor.example/foo。以下是一个 Pod 请求此资源以运行演示工作负载的示例

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: registry.k8s.io/pause:3.8
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# This Pod needs 2 of the hardware-vendor.example/foo devices
# and can only schedule onto a Node that's able to satisfy
# that need.
#
# If the Node has more than 2 of those devices available, the
# remainder would be available for other Pods to use.

设备插件实现

设备插件的一般工作流程包括以下步骤

  1. 初始化。在此阶段,设备插件执行供应商特定的初始化和设置,以确保设备处于就绪状态。

  2. 插件启动一个 gRPC 服务,并在主机路径 /var/lib/kubelet/device-plugins/ 下使用 Unix 套接字,该服务实现以下接口

    service DevicePlugin {
          // GetDevicePluginOptions returns options to be communicated with Device Manager.
          rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
          // ListAndWatch returns a stream of List of Devices
          // Whenever a Device state change or a Device disappears, ListAndWatch
          // returns the new list
          rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
          // Allocate is called during container creation so that the Device
          // Plugin can run device specific operations and instruct Kubelet
          // of the steps to make the Device available in the container
          rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
          // GetPreferredAllocation returns a preferred set of devices to allocate
          // from a list of available ones. The resulting preferred allocation is not
          // guaranteed to be the allocation ultimately performed by the
          // devicemanager. It is only designed to help the devicemanager make a more
          // informed allocation decision when possible.
          rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
          // PreStartContainer is called, if indicated by Device Plugin during registration phase,
          // before each container start. Device plugin can run device specific operations
          // such as resetting the device before making devices available to the container.
          rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    
  3. 插件通过主机路径 /var/lib/kubelet/device-plugins/kubelet.sock 处的 Unix 套接字向 kubelet 注册自身。

  4. 成功注册自身后,设备插件以服务模式运行,在此期间它会持续监控设备健康状况,并在设备状态发生任何变化时向 kubelet 报告。它还负责服务 Allocate gRPC 请求。在 Allocate 期间,设备插件可以执行设备特定的准备工作;例如,GPU 清理或 QRNG 初始化。如果操作成功,设备插件将返回一个 AllocateResponse,其中包含用于访问分配设备的容器运行时配置。kubelet 将此信息传递给容器运行时。

    一个 AllocateResponse 包含零个或多个 ContainerAllocateResponse 对象。在这些对象中,设备插件定义了必须对容器定义进行修改才能提供对设备的访问权限。这些修改包括

    • 注解
    • 设备节点
    • 环境变量
    • 挂载
    • 完全限定的 CDI 设备名称

处理 kubelet 重启

预计设备插件会检测 kubelet 重启并使用新的 kubelet 实例重新注册自身。新的 kubelet 实例在启动时会删除 /var/lib/kubelet/device-plugins 下的所有现有 Unix 套接字。设备插件可以监视其 Unix 套接字的删除,并在发生这种情况时重新注册自身。

设备插件和不健康的设备

在设备发生故障或关闭的情况下,设备插件的责任是使用 ListAndWatchResponse API 向 kubelet 通知这种情况。

一旦设备标记为不健康,kubelet 将减少节点上此资源的 allocatable 计数,以反映可以用于调度新 Pod 的设备数量。资源的 capacity 计数不会更改。

分配给失败设备的 Pod 将继续分配给该设备。通常,依赖于该设备的代码将开始失败,并且如果 Pod 的 restartPolicy 未设置为 Always,则 Pod 可能会进入 Failed 阶段,否则会进入崩溃循环。

在 Kubernetes v1.31 之前,了解 Pod 是否与失败的设备关联的方法是使用 PodResources API

特性状态: Kubernetes v1.31 [alpha](默认禁用)

通过启用 ResourceHealthStatus 特性门控,将向每个容器状态添加 allocatedResourcesStatus 字段,位于每个 Pod 的 .status 中。allocatedResourcesStatus 字段报告分配给容器的每个设备的健康信息。

对于失败的 Pod,或者您怀疑存在故障时,可以使用此状态来了解 Pod 行为是否可能与设备故障相关。例如,如果加速器报告过热事件,则 allocatedResourcesStatus 字段可能能够报告此事件。

设备插件部署

您可以将设备插件作为 DaemonSet、作为节点操作系统的软件包或手动部署。

规范目录 /var/lib/kubelet/device-plugins 需要特权访问权限,因此设备插件必须在特权安全上下文中运行。如果您将设备插件作为 DaemonSet 部署,则必须将 /var/lib/kubelet/device-plugins 作为插件的 PodSpec 中的 Volume 挂载。

如果您选择 DaemonSet 方法,则可以依赖 Kubernetes 来:将设备插件的 Pod 放置到节点上,在插件失败后重启 daemon Pod,并帮助自动化升级。

API 兼容性

以前,版本控制方案要求设备插件的 API 版本与 Kubelet 的版本完全匹配。自该特性在 v1.12 中升级为 Beta 以来,这不再是硬性要求。API 已版本化,并且自该特性升级为 Beta 以来一直保持稳定。因此,kubelet 升级应该无缝进行,但 API 在稳定之前仍可能发生更改,这可能导致升级不保证不中断。

作为项目,Kubernetes 建议设备插件开发人员

  • 关注未来版本中设备插件 API 的更改。
  • 为了向后/向前兼容性,支持多个版本的设备插件 API。

为了在节点上运行需要升级到具有较新设备插件 API 版本的 Kubernetes 版本的设备插件,请在升级这些节点之前升级您的设备插件以支持这两个版本。采取这种方法将确保在升级期间设备分配的持续运行。

监控设备插件资源

功能状态: Kubernetes v1.28 [稳定]

为了监控设备插件提供的资源,监控代理需要能够发现节点上使用的设备集,并获取元数据以描述应将指标关联到哪个容器。 Prometheus 由设备监控代理公开的指标应遵循 Kubernetes 仪器化指南,使用 podnamespacecontainer prometheus 标签识别容器。

kubelet 提供了一个 gRPC 服务,用于启用对使用的设备的发现,并为这些设备提供元数据

// PodResourcesLister is a service provided by the kubelet that provides information about the
// node resources consumed by pods and containers on the node
service PodResourcesLister {
    rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {}
    rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {}
    rpc Get(GetPodResourcesRequest) returns (GetPodResourcesResponse) {}
}

List gRPC 端点

List 端点提供有关正在运行的 Pod 资源的信息,包括独占分配的 CPU 的 ID、设备插件报告的设备 ID 以及这些设备分配到的 NUMA 节点的 ID。此外,对于基于 NUMA 的机器,它包含为容器保留的内存和巨页信息。

从 Kubernetes v1.27 开始,List 端点可以提供由 DynamicResourceAllocation API 分配给 ResourceClaims 的正在运行的 Pod 的资源信息。从 Kubernetes v1.34 开始,此功能默认启用。要禁用,必须使用以下标志启动 kubelet

--feature-gates=KubeletPodResourcesDynamicResources=false
// ListPodResourcesResponse is the response returned by List function
message ListPodResourcesResponse {
    repeated PodResources pod_resources = 1;
}

// PodResources contains information about the node resources assigned to a pod
message PodResources {
    string name = 1;
    string namespace = 2;
    repeated ContainerResources containers = 3;
}

// ContainerResources contains information about the resources assigned to a container
message ContainerResources {
    string name = 1;
    repeated ContainerDevices devices = 2;
    repeated int64 cpu_ids = 3;
    repeated ContainerMemory memory = 4;
    repeated DynamicResource dynamic_resources = 5;
}

// ContainerMemory contains information about memory and hugepages assigned to a container
message ContainerMemory {
    string memory_type = 1;
    uint64 size = 2;
    TopologyInfo topology = 3;
}

// Topology describes hardware topology of the resource
message TopologyInfo {
        repeated NUMANode nodes = 1;
}

// NUMA representation of NUMA node
message NUMANode {
        int64 ID = 1;
}

// ContainerDevices contains information about the devices assigned to a container
message ContainerDevices {
    string resource_name = 1;
    repeated string device_ids = 2;
    TopologyInfo topology = 3;
}

// DynamicResource contains information about the devices assigned to a container by Dynamic Resource Allocation
message DynamicResource {
    string class_name = 1;
    string claim_name = 2;
    string claim_namespace = 3;
    repeated ClaimResource claim_resources = 4;
}

// ClaimResource contains per-plugin resource information
message ClaimResource {
    repeated CDIDevice cdi_devices = 1 [(gogoproto.customname) = "CDIDevices"];
}

// CDIDevice specifies a CDI device information
message CDIDevice {
    // Fully qualified CDI device name
    // for example: vendor.com/gpu=gpudevice1
    // see more details in the CDI specification:
    // https://github.com/container-orchestrated-devices/container-device-interface/blob/main/SPEC.md
    string name = 1;
}

GetAllocatableResources gRPC 端点

功能状态: Kubernetes v1.28 [稳定]

GetAllocatableResources 提供有关工作节点上最初可用的资源的信息。它比 kubelet 导出到 APIServer 的信息更多。

// AllocatableResourcesResponses contains information about all the devices known by the kubelet
message AllocatableResourcesResponse {
    repeated ContainerDevices devices = 1;
    repeated int64 cpu_ids = 2;
    repeated ContainerMemory memory = 3;
}

ContainerDevices 确实会暴露拓扑信息,声明设备与哪个 NUMA 单元相关联。NUMA 单元使用不透明的整数 ID 标识,其值与设备插件向 kubelet 注册时报告的值一致 与拓扑管理器集成

gRPC 服务通过 unix socket 在 /var/lib/kubelet/pod-resources/kubelet.sock 上提供。设备插件资源的监控代理可以作为守护进程或作为 DaemonSet 部署。规范目录 /var/lib/kubelet/pod-resources 需要特权访问权限,因此监控代理必须在特权安全上下文中运行。如果设备监控代理作为 DaemonSet 运行,则必须将 /var/lib/kubelet/pod-resources 作为 Volume 挂载到设备监控代理的 PodSpec 中。

Get gRPC 端点

功能状态: Kubernetes v1.34 [beta]

Get 端点提供有关正在运行的 Pod 的资源信息。它暴露的信息与 List 端点中描述的信息类似。Get 端点需要正在运行的 Pod 的 PodNamePodNamespace

// GetPodResourcesRequest contains information about the pod
message GetPodResourcesRequest {
    string pod_name = 1;
    string pod_namespace = 2;
}

要禁用此功能,必须使用以下标志启动 kubelet 服务

--feature-gates=KubeletPodResourcesGet=false

Get 端点可以提供与由动态资源分配 API 分配的动态资源相关的 Pod 信息。从 Kubernetes v1.34 开始,此功能默认启用。要禁用,必须使用以下标志启动 kubelet

--feature-gates=KubeletPodResourcesDynamicResources=false

设备插件与拓扑管理器的集成

功能状态: Kubernetes v1.27 [稳定]

拓扑管理器是 Kubelet 组件,允许以拓扑对齐的方式协调资源。为此,设备插件 API 扩展为包含 TopologyInfo 结构体。

message TopologyInfo {
    repeated NUMANode nodes = 1;
}

message NUMANode {
    int64 ID = 1;
}

希望利用拓扑管理器的设备插件可以将填充的 TopologyInfo 结构体作为设备注册的一部分发送回来,以及设备 ID 和设备的健康状况。然后,设备管理器将使用此信息咨询拓扑管理器并做出资源分配决策。

TopologyInfo 支持设置一个 nodes 字段,该字段可以是 nil 或 NUMA 节点的列表。这允许设备插件宣传跨多个 NUMA 节点的设备。

TopologyInfo 设置为 nil 或为给定设备提供空的 NUMA 节点列表,表示设备插件对该设备没有 NUMA 亲和性偏好。

设备插件填充设备的示例 TopologyInfo 结构体

pluginapi.Device{ID: "25102017", Health: pluginapi.Healthy, Topology:&pluginapi.TopologyInfo{Nodes: []*pluginapi.NUMANode{&pluginapi.NUMANode{ID: 0,},}}}

设备插件示例

以下是一些设备插件实现的示例

接下来

此页面上的项目引用提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南

在提出添加额外的第三方链接的更改之前,您应该阅读 内容指南

上次修改时间为 2026 年 1 月 2 日下午 10:16 PST:修复 GetAllocatableResources 引用中的错别字 (c5f5590af6)