Skip to main content

kubelet 源码分析 devicemanager

· 5 min read
Softwore Developer

device-pluginKubernetes 用来扩展除 CPU Memory 之外的硬件设备,比如常见的 Nvidia GPU .

下面主要来分析一下在 Kubelet 中是如何调用 device-plugin 的,以及如何生成 OCI Spec 内容。

device-plugin 也存在一些限制,因为它不感知 PodContainer ,就导致没办法做一些动态的资源分配,比如根据用户设置选用某个 GPU 型号或者是指定使用某个设备的 UUID ;目前社区主要是在推进 DRA 来解决这些问题。

kubelet 调用流程

device plugin 的工作原理其实不复杂。主要有以下步骤:

  • 首先 device plugin 可以通过 daemonset 部署到需要的节点上。
  • 为了让 Kubernetes 发现 device plugin,需要向 kubeletunix socket。 进行注册,注册的信息包括 device pluginunix socket API Version ResourceName
  • kubelet 通过 grpcdevice plugin 调用 ListAndWatch, 获取当前节点上的资源。
  • kubeletapi server 更新节点状态来通知资源变更。
  • 用户创建 pod,请求资源并调度到节点上后, kubelet 调用 device pluginAllocate 进行资源分配。

时序图如下:

image.png

device-plugin 源码模块在 pkg/kubelet/cm/devicemanager 目录下,是 ContainerManager 模块的一部分,主要是用来在创建 Container 过程中,生成 OCI Spec 内容的。

下面按照两个流程来讲解源码,一个是注册流程和获取节点设备,还有一个是分配流程;

注册和获取可用设备流程:

资源分配流程

ContainerManagerGetResources 方法进入 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
}

如果启用了 CDICDIDevices 这个字段非空,此时需要挂载哪些设备就需要等容器运行时解析 CDI 文件然后 MergeOCI Spec 文件中了。

下面主要讲一下几个函数的实现逻辑;

ListAndWatch

ListAndWatch 主要在启动的时候发送 Send 一次设备,之后进入监听本地设备的环节,如果出现设备不健康,则重新 Send 所有健康的设备。

Allocate

在用户创建的 Pod 请求资源时,Kubernetes 的调度器会进行调度,并通过 kubeletdevice 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-pluginGetPreferredAllocation 内部实现了节点内多卡最优选择的逻辑,通过确定多卡之间的拓扑信息来决定通信量。