kubelet 源码分析 devicemanager
device-plugin 是 Kubernetes 用来扩展除 CPU Memory 之外的硬件设备,比如常见的 Nvidia GPU .
下面主要来分析一下在 Kubelet 中是如何调用 device-plugin 的,以及如何生成 OCI Spec 内容。
device-plugin 也存在一些限制,因为它不感知 Pod 和 Container ,就导致没办法做一些动态的资源分配,比如根据用户设置选用某个 GPU 型号或者是指定使用某个设备的 UUID ;目前社区主要是在推进 DRA 来解决这些问题。
kubelet 调用流程
device plugin 的工作原理其实不复杂。主要有以下步骤:
- 首先
device plugin可以通过daemonset部署到需要的节点上。 - 为了让
Kubernetes发现device plugin,需要向kubelet的unix socket。 进行注册,注册的信息包括device plugin的unix socketAPI VersionResourceName kubelet通过grpc向device plugin调用ListAndWatch, 获取当前节点上的资源。kubelet向api server更新节点状态来通知资源变更。- 用户创建
pod,请求资源并调度到节点上后,kubelet调用device plugin的Allocate进行资源分配。
时序图如下:

device-plugin 源码模块在 pkg/kubelet/cm/devicemanager 目录下,是 ContainerManager 模块的一部分,主要是用来在创建 Container 过程中,生成 OCI Spec 内容的。
下面按照两个流程来讲解源码,一个是注册流程和获取节点设备,还有一个是分配流程;
注册和获取可用设备流程:
资源分配流程
从 ContainerManager 的 GetResources 方法进入 devicemanager 模块中,开始获取分配的设备,最后返回一个 DeviceRunContainerOptions 对象:
type DeviceRunContainerOptions struct {
// The environment variables list.
Envs []kubecontainer.EnvVar
// The mounts for the container.
Mounts []kubecontainer.Mount
// The host devices mapped into the container.
Devices []kubecontainer.DeviceInfo
// The Annotations for the container
Annotations []kubecontainer.Annotation
// CDI Devices for the container
CDIDevices []kubecontainer.CDIDevice
}
如果启用了 CDI 则 CDIDevices 这个字段非空,此时需要挂载哪些设备就需要等容器运行时解析 CDI 文件然后 Merge 到 OCI Spec 文件中了。
下面主要讲一下几个函数的实现逻辑;
ListAndWatch
ListAndWatch 主要在启动的时候发送 Send 一次设备,之后进入监听本地设备的环节,如果出现设备不健康,则重新 Send 所有健康的设备。
Allocate
在用户创建的 Pod 请求资源时,Kubernetes 的调度器会进行调度,并通过 kubelet 向 device plugin 发出 Allocate 调用,这一步的调用主要是为了让 device plugin 为容器调度资源。 在调度成功后向 kubelet 返回调度结果即可。
GetDevicePluginOption
主要返回与 Device Manager 通信的一些可选参数设置,主要返回如下结构:
type DevicePluginOptions struct {
// 每个容器启动前是否需要调用 PreStartContainer
PreStartRequired bool
// GetPreferredAllocationAvailable: 是否提供优选方法
GetPreferredAllocationAvailable bool
}
PreStartContainer
如果设备插件在注册阶段设置 PreStartRequired=true 了要调用,则在每个容器启动之前调用 PreStartContainer。 设备插件可以运行设备特定的操作,例如在使设备可供容器使用之前重置设备。
GetPreferredAllocation
如果设备插件在注册阶段设置 PreStartRequired=true 了要调用,可以针对指定的设备列表做二次最优分配。
官方的 nvidia-device-plugin 在 GetPreferredAllocation 内部实现了节点内多卡最优选择的逻辑,通过确定多卡之间的拓扑信息来决定通信量。