本文基于kubernetes1.25.4版本,所有代码引用为了简洁都去掉了日志打印相关的代码,尽量只保留有价值的内容。

首先,我们先看看官方对 device-plugins 的定义是什么?
Starting in version 1.8, Kubernetes provides a device plugin framework for vendors to advertise their resources to the kubelet without changing Kubernetes core code. Instead of writing custom Kubernetes code, vendors can implement a device plugin that can be deployed manually or as a DaemonSet. The targeted devices include GPUs, High-performance NICs, FPGAs, InfiniBand, and other similar computing resources that may require vendor specific initialization and setup.

大概意思是:从kubernetes1.8版本开始,提供了设备插件框架,设备厂商无需修改kubernetes核心代码就可以将自己生产的设备的资源(kubernetes可管理的资源包括CPU、内存和存储资源)可以让kubelet使用(这一点与操作系统一样,所有设备厂商自己实现驱动)。设备厂商可以自己人工或者以DaemonSet方式部署,而不是定制kubernetes代码。目标设备包括GPU、高性能NIC(网络接口卡)、FPGA、InfiniBand以及其他类似的需要厂商指定初始化和安装的计算资源。上文引用自kubernetes官方文档,读者可以自行了解一下官方对于device-plugin的说明,如下图所示(用图比链接好,担心链接以后会变):

image.png

了解device-plugin的是什么了,接下来就是看看kubernetes是如何实现并工作的。我写这篇文章的核心目的是了解kubernetes如何管理GPU的,因为我的项目需要一个集群同时管理CPU和GPU,根据用户的需求选择合适的资源计算。所以,后面所有的分析都是以GPU为例,读者如果需要了解其他类型的设备根据本文的思路自行分析即可。

好了,我们可以进入正题了。让我们先忘记一部分内容,看看下面这个图:

image.png

如果我作为kubernetes开发者,思路是由kubelet汇总所有的资源,然后在汇总到管理端,kubernetes也就是apiserver。当创建Pod时,请求会发送给scheduler,scheduler根据节点状态选择一个最优的节点,最后由最优节点的kubelet创建这个Pod。嗯,这个思路应该没什么大毛病,至少我开发的一个分布式计算系统采用的就是这个方式,没问题!好,我们先假设这个想法是就是kubernetes的设计方案,此处我们不讲内存、CPU、存储这些资源是kubelet是怎么获取的,因为本文的重点是device-plugins,我们只说GPU这个kubelet是怎么获取的。

上面说到了,kubernetes有设备插件框架,那这个框架又是什么样的呢?说白了也很简单,就是kubernetes定义了一套机制和接口,各设备厂商按照协议开发就可以了,这个和Linux驱动原理是一样的,只是实现方式不一样而已。我们来看看kubernetes是怎么实现的,首先我们先说说机制:

  • 厂商自行实现一个管理设备资源的程序,部署到相应的节点上,我们称之为插件;
  • 插件需要向kubelet注册,注册内容要包含自己的endpoint(endpoint就是一个用于通信的地址)以及一些其他信息(后面会说明);
  • kubelet连接插件的endpoint,就此kubelet和插件就建立了联系;
  • kubelet监听/var/lib/kubelet/device-plugins/kubelet.sock(unix sockets)这个地址,插件监听的也是类似的地址,只是地址变成了/var/lib/kubelet/device-plugins/gpu.sock(举个例子)

以上是插件如何让kubernetes发现自己,接下来就是插件和kubelet之间的通信接口了。


kubelet与插件采用grpc通信,通信接口定义在
kubernetes/pkg/kubelet/apis/deviceplugin/v1beta1/api.proto

service Registration {
    rpc Register(RegisterRequest) returns (Empty) {}
}
service DevicePlugin {
    rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
}

一尾
7 声望0 粉丝