设备插件
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.
设备插件实现
设备插件的一般工作流程包括以下步骤
初始化。在此阶段,设备插件执行供应商特定的初始化和设置,以确保设备处于就绪状态。
插件启动一个 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 报告。它还负责服务
AllocategRPC 请求。在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 将减少节点上此资源的 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 和版本兼容性的信息,请阅读 Device Plugin API 版本。作为项目,Kubernetes 建议设备插件开发人员
- 关注未来版本中设备插件 API 的更改。
- 为了向后/向前兼容性,支持多个版本的设备插件 API。
为了在节点上运行需要升级到具有较新设备插件 API 版本的 Kubernetes 版本的设备插件,请在升级这些节点之前升级您的设备插件以支持这两个版本。采取这种方法将确保在升级期间设备分配的持续运行。
监控设备插件资源
Kubernetes v1.28 [稳定]为了监控设备插件提供的资源,监控代理需要能够发现节点上使用的设备集,并获取元数据以描述应将指标关联到哪个容器。 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 的机器,它包含为容器保留的内存和巨页信息。
从 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 [稳定]GetAllocatableResources 提供有关工作节点上最初可用的资源的信息。它比 kubelet 导出到 APIServer 的信息更多。
说明
GetAllocatableResources 仅应用于评估节点上的 可分配 资源。如果目标是评估空闲/未分配资源,则应将其与 List() 端点结合使用。通过 GetAllocatableResources 获取的结果在底层暴露给 kubelet 的资源发生更改之前将保持不变。这种情况很少发生,但当发生时(例如:热插拔/热拔出、设备健康状况更改),客户端预计会调用 GetAllocatableResources 端点。
但是,在 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 socket 在 /var/lib/kubelet/pod-resources/kubelet.sock 上提供。设备插件资源的监控代理可以作为守护进程或作为 DaemonSet 部署。规范目录 /var/lib/kubelet/pod-resources 需要特权访问权限,因此监控代理必须在特权安全上下文中运行。如果设备监控代理作为 DaemonSet 运行,则必须将 /var/lib/kubelet/pod-resources 作为 Volume 挂载到设备监控代理的 PodSpec 中。
说明
从 DaemonSet 或作为容器在主机上部署的任何其他应用程序访问 /var/lib/kubelet/pod-resources/kubelet.sock 时,将目录 /var/lib/kubelet/pod-resources/ 挂载为 Volume,而不是 /var/lib/kubelet/pod-resources/kubelet.sock,是一种很好的做法。这将确保在 kubelet 重新启动后,容器能够重新连接到此 socket。
容器挂载通过引用 socket 或目录的 inode 进行管理,具体取决于挂载的内容。当 kubelet 重新启动时,socket 会被删除并创建一个新的 socket,而目录保持不变。因此,socket 的原始 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;
}
希望利用拓扑管理器的设备插件可以将填充的 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,},}}}
设备插件示例
以下是一些设备插件实现的示例
- Akri,它使您可以轻松暴露异构叶设备(例如 IP 摄像头和 USB 设备)。
- AMD GPU 设备插件
- 用于通用 Linux 设备和 USB 设备的 通用设备插件
- 用于异构 AI 计算虚拟化中间件(例如,NVIDIA、Cambricon、Hygon、Iluvatar、MThreads、Ascend、Metax)的 HAMi
- 用于 Intel GPU、FPGA、QAT、VPU、SGX、DSA、DLB 和 IAA 设备的 Intel 设备插件
- 用于硬件辅助虚拟化的 KubeVirt 设备插件
- NVIDIA 的官方设备插件,用于暴露 NVIDIA GPU 并监控 GPU 健康状况的 NVIDIA GPU 设备插件
- 用于 Container-Optimized OS 的 NVIDIA GPU 设备插件
- RDMA 设备插件
- SocketCAN 设备插件
- Solarflare 设备插件
- SR-IOV 网络设备插件
- 用于 Xilinx FPGA 设备的 Xilinx FPGA 设备插件
接下来
- 了解如何使用设备插件 调度 GPU 资源
- 了解如何在节点上 通告扩展资源
- 了解 拓扑管理器
- 阅读有关使用 Kubernetes 进行 硬件加速的 TLS 入站 的内容
- 了解更多关于 DRA 扩展资源分配 的信息
此页面上的项目引用提供 Kubernetes 所需功能的第三方产品或项目。Kubernetes 项目作者不对这些第三方产品或项目负责。有关更多详细信息,请参阅 CNCF 网站指南。
在提出添加额外的第三方链接的更改之前,您应该阅读 内容指南。