设备插件
Kubernetes v1.26 [stable]
Kubernetes 提供了一个设备插件框架,你可以使用它向 Kubelet 宣告系统硬件资源。
供应商可以实现一个设备插件,你可以手动部署或作为 DaemonSet 部署,而不是自定义 Kubernetes 代码本身。目标设备包括 GPU、高性能 NIC、FPGA、InfiniBand 适配器以及其他可能需要供应商特定初始化和设置的类似计算资源。
设备插件注册
kubelet 导出 Registration
gRPC 服务
service Registration {
rpc Register(RegisterRequest) returns (Empty) {}
}
设备插件可以通过此 gRPC 服务向 kubelet 注册自己。在注册期间,设备插件需要发送
- 其 Unix 套接字名称。
- 其构建所针对的设备插件 API 版本。
- 它想要宣告的
ResourceName
。这里的ResourceName
需要遵循 扩展资源命名方案,格式为vendor-domain/resourcetype
。(例如,NVIDIA GPU 被宣告为nvidia.com/gpu
。)
成功注册后,设备插件会将它所管理的设备列表发送给 kubelet,然后 kubelet 负责将这些资源作为 kubelet 节点状态更新的一部分,通告给 API 服务器。例如,当设备插件向 kubelet 注册 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.
设备插件实现
设备插件的一般工作流程包括以下步骤:
初始化。在此阶段,设备插件执行供应商特定的初始化和设置,以确保设备处于就绪状态。
该插件启动一个 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) {} }
注意
插件无需为GetPreferredAllocation()
或PreStartContainer()
提供有用的实现。如果这些调用可用,则应在调用GetDevicePluginOptions()
返回的DevicePluginOptions
消息中设置指示这些调用可用的标志。kubelet
将始终在直接调用任何可选函数之前调用GetDevicePluginOptions()
以查看哪些可选函数可用。插件通过主机路径
/var/lib/kubelet/device-plugins/kubelet.sock
上的 Unix 套接字向 kubelet 注册自己。注意
工作流程的顺序很重要。插件必须在向 kubelet 注册之前启动 gRPC 服务,才能成功注册。成功注册后,设备插件以服务模式运行,在此期间它会持续监控设备健康状况并在任何设备状态变化时向 kubelet 报告。它还负责处理
Allocate
gRPC 请求。在Allocate
期间,设备插件可能会进行设备特定的准备工作;例如,GPU 清理或 QRNG 初始化。如果操作成功,设备插件会返回一个AllocateResponse
,其中包含用于访问已分配设备的容器运行时配置。kubelet 会将此信息传递给容器运行时。AllocateResponse
包含零个或多个ContainerAllocateResponse
对象。在这些对象中,设备插件定义了必须对容器定义进行的修改,以提供对设备的访问。这些修改包括:- 注解
- 设备节点
- 环境变量
- 挂载点
- 完全限定的 CDI 设备名称
注意
设备管理器处理完全限定的 CDI 设备名称要求为 kubelet 和 kube-apiserver 均启用DevicePluginCDIDevices
功能门控。这在 Kubernetes v1.28 中作为 Alpha 功能添加,在 v1.29 中升级为 Beta,并在 v1.31 中升级为 GA。
处理 kubelet 重启
设备插件应检测 kubelet 重启,并重新向新的 kubelet 实例注册自己。新的 kubelet 实例在启动时会删除 /var/lib/kubelet/device-plugins
下所有现有的 Unix 套接字。设备插件可以监控其 Unix 套接字的删除,并在发生此类事件时重新注册自己。
设备插件和不健康的设备
有时设备会发生故障或关闭。在这种情况下,设备插件的责任是使用 ListAndWatchResponse
API 通知 kubelet 情况。
一旦设备被标记为不健康,kubelet 将减少节点上该资源的可分配数量,以反映可用于调度新 Pod 的设备数量。资源的容量数量不会改变。
分配给故障设备的 Pod 将继续分配给该设备。通常情况下,依赖于该设备的代码将开始失败,如果 Pod 的 restartPolicy
不是 Always
,Pod 可能会进入 Failed 阶段,否则会进入崩溃循环。
在 Kubernetes v1.31 之前,了解 Pod 是否与故障设备关联的方法是使用 PodResources API。
Kubernetes v1.31 [alpha]
(默认启用:false)通过启用功能门控 ResourceHealthStatus
,.status
中每个 Pod 的每个容器状态都会添加 allocatedResourcesStatus
字段。allocatedResourcesStatus
字段报告分配给容器的每个设备的健康信息。
对于失败的 Pod,或者你怀疑有故障的 Pod,你可以使用此状态来了解 Pod 行为是否可能与设备故障有关。例如,如果加速器报告过温事件,allocatedResourcesStatus
字段可能能够报告此情况。
设备插件部署
你可以将设备插件部署为 DaemonSet、作为节点操作系统的包,或者手动部署。
规范目录 /var/lib/kubelet/device-plugins
需要特权访问,因此设备插件必须在特权安全上下文中运行。如果你将设备插件部署为 DaemonSet,/var/lib/kubelet/device-plugins
必须作为 卷 挂载到插件的 PodSpec 中。
如果你选择 DaemonSet 方式,你可以依赖 Kubernetes 来:将设备插件的 Pod 放置到节点上,在故障后重新启动守护进程 Pod,并帮助自动化升级。
API 兼容性
以前,版本控制方案要求设备插件的 API 版本与 Kubelet 的版本完全匹配。自此功能在 v1.12 升级到 Beta 版后,这不再是硬性要求。该 API 已版本化,并且自此功能升级到 Beta 版后一直保持稳定。因此,kubelet 升级应该无缝进行,但在稳定之前 API 可能仍然会发生变化,导致升级无法保证不中断。
注意
尽管 Kubernetes 的设备管理器组件是一个普遍可用的功能,但**设备插件 API** 并不稳定。有关设备插件 API 和版本兼容性的信息,请阅读 设备插件 API 版本。作为项目,Kubernetes 建议设备插件开发人员:
- 关注未来版本中设备插件 API 的变化。
- 支持多个设备插件 API 版本以实现向后/向前兼容性。
要在需要升级到具有更新设备插件 API 版本的 Kubernetes 版本的节点上运行设备插件,请在升级这些节点之前升级你的设备插件以支持这两个版本。采用这种方法将确保在升级期间设备分配的持续运行。
监控设备插件资源
Kubernetes v1.28 [stable]
为了监控设备插件提供的资源,监控代理需要能够发现节点上正在使用的设备集合,并获取元数据以描述指标应与哪个容器关联。Prometheus 监控代理暴露的指标应遵循 Kubernetes 观测指导原则,使用 pod
、namespace
和 container
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 的机器,它还包含为容器预留的内存和 Hugepages 信息。
从 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;
}
注意
List
端点中 ContainerResources
中的 cpu_ids
对应于分配给特定容器的独占 CPU。如果目标是评估属于共享池的 CPU,则 List
端点需要与 GetAllocatableResources
端点结合使用,如下所述:
- 调用
GetAllocatableResources
获取所有可分配 CPU 的列表。 - 在系统中所有
ContainerResources
上调用GetCpuIds
。 - 从
GetAllocatableResources
调用中减去所有GetCpuIds
调用中的 CPU。
GetAllocatableResources
gRPC 端点
Kubernetes v1.28 [stable]
GetAllocatableResources 提供工作节点上最初可用资源的信息。它比 kubelet 导出到 APIServer 的信息更多。
注意
GetAllocatableResources
仅应用于评估节点上的可分配资源。如果目标是评估空闲/未分配资源,则应与 List() 端点结合使用。GetAllocatableResources
获得的结果将保持不变,除非暴露给 kubelet 的底层资源发生变化。这种情况很少发生,但当发生时(例如:热插拔/热拔插,设备健康状况变化),客户端应调用 GetAlloctableResources
端点。
但是,在 CPU 和/或内存更新的情况下,调用 GetAllocatableResources
端点是不够的,需要重新启动 Kubelet 才能反映正确的资源容量和可分配性。
// 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 套接字 /var/lib/kubelet/pod-resources/kubelet.sock
提供。设备插件资源的监控代理可以作为守护进程部署,也可以作为 DaemonSet 部署。规范目录 /var/lib/kubelet/pod-resources
需要特权访问,因此监控代理必须在特权安全上下文中运行。如果设备监控代理作为 DaemonSet 运行,/var/lib/kubelet/pod-resources
必须作为 卷 挂载到设备监控代理的 PodSpec 中。
注意
当从 DaemonSet 或任何其他作为容器部署在主机上的应用程序访问 /var/lib/kubelet/pod-resources/kubelet.sock
并将套接字挂载为卷时,最好挂载目录 /var/lib/kubelet/pod-resources/
而不是 /var/lib/kubelet/pod-resources/kubelet.sock
。这将确保在 kubelet 重启后,容器能够重新连接到此套接字。
容器挂载由引用套接字或目录的 inode 管理,具体取决于挂载的内容。当 kubelet 重启时,套接字被删除并创建一个新套接字,而目录保持不变。因此,套接字的原始 inode 变得不可用。目录的 inode 将继续工作。
Get
gRPC 端点
Kubernetes v1.34 [beta]
Get
端点提供有关正在运行的 Pod 资源的信息。它暴露的信息与 List
端点中描述的信息类似。Get
端点需要正在运行的 Pod 的 PodName
和 PodNamespace
。
// 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;
}
希望利用拓扑管理器的设备插件可以在设备注册时,连同设备 ID 和设备健康状况一起,返回填充好的 TopologyInfo
结构。然后,设备管理器将使用此信息与拓扑管理器协商并做出资源分配决策。
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,},}}}
设备插件示例
以下是一些设备插件实现的示例:
- Akri,让你轻松暴露异构叶设备(如 IP 摄像头和 USB 设备)。
- AMD GPU 设备插件
- 适用于通用 Linux 设备和 USB 设备的通用设备插件
- HAMi,用于异构 AI 计算虚拟化中间件(例如,NVIDIA、寒武纪、海光、天数、MThreads、昇腾、Metax)
- Intel 设备插件,用于 Intel GPU、FPGA、QAT、VPU、SGX、DSA、DLB 和 IAA 设备
- 用于硬件辅助虚拟化的KubeVirt 设备插件
- NVIDIA GPU 设备插件,NVIDIA 官方设备插件,用于暴露 NVIDIA GPU 并监控 GPU 健康状况
- 用于容器优化型操作系统 (Container-Optimized OS) 的NVIDIA GPU 设备插件
- RDMA 设备插件
- SocketCAN 设备插件
- Solarflare 设备插件
- SR-IOV 网络设备插件
- 用于 Xilinx FPGA 设备的Xilinx FPGA 设备插件
下一步
- 了解如何使用设备插件调度 GPU 资源
- 了解如何在节点上宣告扩展资源
- 了解拓扑管理器
- 阅读有关使用 Kubernetes 设备插件和 RuntimeClass 实现 TLS 入口硬件加速的信息
- 阅读更多关于 DRA 扩展资源分配的信息
此页面上的项目是指提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目的作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南。
在提议添加额外第三方链接的更改之前,你应该阅读内容指南。