【k8s-device plugin】如何编写 k8s device plugin

news2024/9/22 6:00:40

参考

  • Device Plugin 入门笔记(一)

  • Device Plugin 入门笔记(二)

  • 从零开始入门 K8s:GPU 管理和 Device Plugin 工作机制

  • Kubernetes开发知识–device-plugin的实现

  • https://github.com/oceanweave/cola-device-plugin/blob/master/pkg/server/server.go#L117:22

  • https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/ device plugin api spec

  • https://github.com/kubernetes/design-proposals-archive/blob/main/resource-management/device-plugin.md

明确需求

我们想让 k8s 纳入一个硬件设备,假如是 GPU,那接下来怎么管理呢 —— 答:k8s device plugin 机制

由下图其实可以简单看出几个步骤

  1. 将硬件设备信息注册到 k8s 中,也就是由 kubelet 管理,与 kubelet 通信
  2. k8s 如何知道该硬件资源的变化呢 —— ListWatch 机制,但 k8s 只知道此硬件,不明白其中原理 —— 所以需要该硬件自行开发一个 ListWatch 接口,来满足 k8s 的 ListWatch 机制
  3. k8s 如何分配此硬件资源给 Pod 等使用呢 —— 因为k8s 只知道此硬件,不明白其中原理 —— 所以同样有该硬件自行开发一个资源分配管理接口 Allocate,到时候进行资源的分配

注意上述其实都是,该硬件和 kebelet 打交道,因为 kubelet 负责容器的生命周期(创建、删除等)还有资源的监控,所以该硬件资源通过与 kubelet 交互,才能完成接入到 k8s 中

而 device plugin 就是 中间商,帮忙将硬件设备注册到 kubelet 中

在这里插入图片描述

概述总流程

  • Kubernetes开发知识–device-plugin的实现

  • [Gaia Scheduler] gpu-manager 启动流程分析

  • [Gaia Scheduler] gpu-manager 的虚拟化 gpu 分配流程

以 gpu-manager 项目为例,关注了两种资源 vcore 和 vmemory(我为其命名为 dfy.com/vgpu-core 和 dfy.com/vgpu-memory,项目中不是这个名字哈)

所以考虑一下几个方面,以 vcore 资源为例

  1. 首先为我们资源命名,符合 k8s 格式,取名为 dfy.com/vgpu-core
  2. 如何与 kubelet 通信呢?
    • 需要为该资源创建个 vcore.sock,将其名字 dfy.com/vgpu-core 和 vcore.sock 注册到 kubelet(就是收到此资源请求,就发给 vcore.sock)
  3. vcore.sock 收到 kubelet 发来的资源请求后怎么处理呢?
    • 需要为创建个 grpc server,监听 vcore.sock 传来的消息,之后 grpc.server 进行处理
  4. 那 vcore.sock 传来的消息是什么呢?(也就是 kubelet 会调用什么函数吗?)
    • 这个已经是规定好的
    • 就是是下面四个函数
      • Allocate 分配资源
      • ListAndWatch 监听资源变化
      • PreStartContainer 每次启动容器前做的操作(在 Allocate 之后执行)
      • GetDevicePluginOptions 可选功能,目前只能开始 PreStartContainer
  5. 之后就要考虑这些方法的实现,并注册到 vcore grpc server 上
    • 这里就是 NvidiaTopoAllocator 结构体进行实现的

总结,我们就是要串起上面的流程

  1. 为资源定义名称,创建 socket,注册到 kubelet (作用:让 kubelet 知道这种资源,并且知道通信方式)
  2. 创建一个结构体,实现 device plugin 定义的接口(Allocate、ListAndWatch 重要,其他两个可选)
  3. 创建 grpc server,监听此 socket,并将上面结构体绑定到该 grpc server 上(作用:kubelet 对此资源发起请求,会通过 socket 到达 grpc server,调用此机构体的处理函数进行处理)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWL1mMqZ-1672912229659)(/Users/dufengyang/Library/Application Support/typora-user-images/image-20230105173004871.png)]

注册设备插件

kubelet 提供了一个 Registration 的 gRPC 服务:

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

device plugin(设备插件) 可以通过此 gRPC 服务在 kubelet 进行注册。在注册期间,设备插件需要发送下面几样内容:

  • 设备插件 的 Unix socket(套接字)。
  • 设备插件 的 API 版本。
  • ResourceName 是需要公布的。这里 ResourceName 需要遵循扩展资源命名方案, 类似于 vendor-domain/resourcetype。(比如 NVIDIA GPU 就被公布为 nvidia.com/gpu。)

成功注册后,设备插件就向 kubelet 发送它所管理的设备列表,然后 kubelet 负责将这些资源发布到 API 服务器,作为 kubelet 节点状态更新的一部分。

// 举例子  来自 https://github.com/oceanweave/cola-device-plugin/blob/master/pkg/server/server.go#L117:22 项目
// pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
// RegisterToKubelet 向kubelet注册device plugin
func (s *ColaServer) RegisterToKubelet() error {
	socketFile := filepath.Join(DevicePluginPath + KubeletSocket)	// kubelet 的 unix 套接字

	conn, err := s.dial(socketFile, 5*time.Second)						// 每 5s 访问一次 kubelet
	if err != nil {
		return err
	}
	defer conn.Close()

	client := pluginapi.NewRegistrationClient(conn)
	req := &pluginapi.RegisterRequest{
		Version:      pluginapi.Version,												// 该硬件设备插件的 API 版本
		Endpoint:     path.Base(DevicePluginPath + colaSocket), // 该硬件设备插件的 unix socket
		ResourceName: resourceName,															// 该硬件设备插件定义的 资源名称
	}
	log.Infof("Register to kubelet with endpoint %s", req.Endpoint)
	_, err = client.Register(context.Background(), req)				// 将该设备插件 注册到 kubelet 中
	if err != nil {
		return err
	}

	return nil
}

设备插件的实现

主要就是实现两个函数

  • ListAndWatch —— 监听该硬件设备的变化
  • Allocate —— 分配该硬件设备资源

承接上面项目例子 https://github.com/oceanweave/cola-device-plugin/blob/master/pkg/server/server.go#L117:22

可以看到该项目实现了下面定义的大部分函数规定

设备插件的常规工作流程包括以下几个步骤:

  • 初始化。在这个阶段,设备插件将执行供应商特定的初始化和设置, 以确保设备处于就绪状态。

  • 插件使用主机路径 /var/lib/kubelet/device-plugins/ 下的 Unix 套接字启动一个 gRPC 服务,该服务实现以下接口:

    // k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1/api.proto
    service DevicePlugin {
        // GetDevicePluginOptions 返回与设备管理器沟通的选项。
        rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
        // ListAndWatch 返回 Device 列表构成的数据流。
        // 当 Device 状态发生变化或者 Device 消失时,ListAndWatch
        // 会返回新的列表。
        rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
        // Allocate 在容器创建期间调用,这样设备插件可以运行一些特定于设备的操作,
        // 并告诉 kubelet 如何令 Device 可在容器中访问的所需执行的具体步骤
        rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
        // GetPreferredAllocation 从一组可用的设备中返回一些优选的设备用来分配,
        // 所返回的优选分配结果不一定会是设备管理器的最终分配方案。
        // 此接口的设计仅是为了让设备管理器能够在可能的情况下做出更有意义的决定。
        rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
        // PreStartContainer 在设备插件注册阶段根据需要被调用,调用发生在容器启动之前。
        // 在将设备提供给容器使用之前,设备插件可以运行一些诸如重置设备之类的特定于
        // 具体设备的操作,
        rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    

    说明:

    插件并非必须为 GetPreferredAllocation()PreStartContainer() 提供有用的实现逻辑, 调用 GetDevicePluginOptions() 时所返回的 DevicePluginOptions 消息中应该设置这些调用是否可用。kubelet 在真正调用这些函数之前,总会调用 GetDevicePluginOptions() 来查看是否存在这些可选的函数。

  • 插件通过 Unix socket 在主机路径 /var/lib/kubelet/device-plugins/kubelet.sock 处向 kubelet 注册自身。

  • 成功注册自身后,设备插件将以服务模式运行,在此期间,它将持续监控设备运行状况, 并在设备状态发生任何变化时向 kubelet 报告。它还负责响应 Allocate gRPC 请求。 在 Allocate 期间,设备插件可能还会做一些设备特定的准备;例如 GPU 清理或 QRNG 初始化。 如果操作成功,则设备插件将返回 AllocateResponse,其中包含用于访问被分配的设备容器运行时的配置。 kubelet 将此信息传递到容器运行时。

异常处理(socket 被恶意删除怎么办?)

  • 参考:KUBERNETES如何通过DEVICE PLUGINS来使用NVIDIA GPU

设备插件应能监测到 kubelet 重启,并且向新的 kubelet 实例来重新注册自己。 在当前实现中,当 kubelet 重启的时候,新的 kubelet 实例会删除 /var/lib/kubelet/device-plugins 下所有已经存在的 Unix 套接字。 设备插件需要能够监控到它的 Unix 套接字被删除,并且当发生此类事件时重新注册自己。

  • 就是监控到 kubelet 重启后,重新将自身的 socket 通信文件,放入到 /var/lib/kubelet/device-plugins 目录下

kubelet socket 的异常处理

  • 每次 kubelet 启动 (重启) 时,都会将 /var/lib/kubelet/device-plugins 下的所有 sockets 文件删除。
  • Device Plugin 要负责监测自己的 socket 被删除,然后进行重新注册,重新生成自己的 socket。
  • 当 plugin socket 被误删,Device Plugin 该怎么办?

我们看看 Nvidia Device Plugin 是怎么处理的,相关的代码如下:

github.com/NVIDIA/k8s-device-plugin/main.go:15

func main() {
	...
	
	log.Println("Starting FS watcher.")
	watcher, err := newFSWatcher(pluginapi.DevicePluginPath)
	
    ...

	restart := true
	var devicePlugin *NvidiaDevicePlugin

L:
	for {
		if restart {
			if devicePlugin != nil {
				devicePlugin.Stop()
			}

			devicePlugin = NewNvidiaDevicePlugin()
			if err := devicePlugin.Serve(); err != nil {
				log.Println("Could not contact Kubelet, retrying. Did you enable the device plugin feature gate?")
				log.Printf("You can check the prerequisites at: https://github.com/NVIDIA/k8s-device-plugin#prerequisites")
				log.Printf("You can learn how to set the runtime at: https://github.com/NVIDIA/k8s-device-plugin#quick-start")
			} else {
				restart = false
			}
		}

		select {
		case event := <-watcher.Events:
			if event.Name == pluginapi.KubeletSocket && event.Op&fsnotify.Create == fsnotify.Create {
				log.Printf("inotify: %s created, restarting.", pluginapi.KubeletSocket)
				restart = true
			}

		case err := <-watcher.Errors:
			log.Printf("inotify: %s", err)

		case s := <-sigs:
			switch s {
			case syscall.SIGHUP:
				log.Println("Received SIGHUP, restarting.")
				restart = true
			default:
				log.Printf("Received signal \"%v\", shutting down.", s)
				devicePlugin.Stop()
				break L
			}
		}
	}
}	
  • 通过 fsnotify.Watcher 监控 /var/lib/kubelet/device-plugins/ 目录。
  • 如果 fsnotify.Watcher 的 Events Channel 收到 Create kubelet.sock 事件(说明 kubelet 发生重启),则会触发 Nvidia Device Plugin 的重启。
  • Nvidia Device Plugin 重启的逻辑是:先检查 devicePlugin 对象是否为空(说明完成了 Nvidia Device Plugin 的初始化):
    • 如果不为空,则先停止 Nvidia Device Plugin 的 gRPC Server。
    • 然后调用 NewNvidiaDevicePlugin () 重建一个新的 DevicePlugin 实例。
    • 调用 Serve () 启动 gRPC Server,并先 kubelet 注册自己。

device plugin socket 的异常处理

因此,这其中只监控了 kubelet.sock 的 Create 事件,能很好处理 kubelet 重启的问题,但是并没有监控自己的 socket 是否被删除的事件。所以,如果 Nvidia Device Plugin 的 socket 被误删了,那么将会导致 kubelet 无法与该节点的 Nvidia Device Plugin 进行 socket 通信,则意味着 Device Plugin 的 gRPC 接口都无法调通:

  • 无法 ListAndWatch 该节点上的 Device 列表、健康状态,Devices 信息无法同步。
  • 无法 Allocate Device,导致容器创建失败。

因此,建议加上对自己 device plugin socket 的删除事件的监控,一旦监控到删除,则应该触发 restart。

select {
    case event := <-watcher.Events:
    	if event.Name == pluginapi.KubeletSocket && event.Op&fsnotify.Create == fsnotify.Create {
    		log.Printf("inotify: %s created, restarting.", pluginapi.KubeletSocket)
    		restart = true
    	}
    	
    	// 增加对nvidia.sock的删除事件监控
    	if event.Name == serverSocket && event.Op&fsnotify.Delete == fsnotify.Delete {
    		log.Printf("inotify: %s deleted, restarting.", serverSocket)
    		restart = true
    	}
    	
    	...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141471.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot的智慧物业管理系统的设计与实现(前后端分离)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

mysql 中间件 mycat2 的详细安装及配置步骤

下载 首先打开mycat官网&#xff1a;MyCat2 右上角下载里面有个文件下载服务&#xff0c;点进去发现无法访问 这里需要配置一下host&#xff0c;把下面内容复制到host文件中。host文件位置在C:\Windows\System32\drivers\etc 210.51.26.184 mycat.org.cn www.mycat.org.cn …

第五篇 - 数组的劫持

一&#xff0c;前言 上篇&#xff0c;主要介绍了 Vue 数据初始化流程中&#xff0c;对象属性的深层劫持是如何实现的 核心思路就是递归&#xff0c;主要流程如下&#xff1b; 1.通过 data isFunction(data) ? data.call(vm) : data;处理后的 data 一定是对象类型 2.通过 d…

如何定位Bug——Qt

1. 前言 在写程序的过程中&#xff0c;不可避免出现各种Bug&#xff0c;如何快速的定位到Bug的位置&#xff0c;是程序员必备的技能之一。 2. 几种方法 2.1. 逻辑分析 根据程序所出的问题&#xff0c;分析问题可能所在的几个位置&#xff0c;通过逻辑分析找出Bug&…

从 Nginx Ingress 窥探云原生网关选型

作者&#xff1a; 魁予 现今有越来越多的企业开始采纳云原生理念进行应用架构转型。而 K8s 和微服务是云原生的两大支柱&#xff0c;随着云原生浪潮而被广泛应用。 对多数应用而言&#xff0c;提供对外服务的使命并不会改变&#xff0c;相比于原来的单体应用&#xff0c;微服…

VUE3中,使用.env.development和.env.production

在实际项目开发过程中&#xff0c;可能开发环境和生产&#xff08;测试&#xff09;环境不一样&#xff0c;经常需要修改配置常量&#xff0c;才能满足对应的环境&#xff0c;才能使软件运行起来。在vue3中可以使用2个文件进行区分。 .env.development&#xff1a;开发环境 .…

Mysql存储引擎 初级(自用笔记)

内容来自于(https://www.bilibili.com/video/BV1Kr4y1i7ru?p58&vd_source3cf72bb393b8cc11b96c6d4bfbcbd890) 1.存储引擎 1.1Mysql体系结构 1.2什么是存储引擎呢? 存储引擎就是存储数据,建立索引,更新,查询数据等技术的实现方式. 存储引擎是基于表的,而不是基于数据库的…

魔兽世界开服教程wow服务器框架Trinirycore构建

首先明杰先给各位普及一下TrinityCore是什么TrinityCore 是c实现MMORPG框架来自MaNGOS&#xff0c;大型网络对象服务&#xff0c;随着时间的推移&#xff0c;该项目代码广泛的优化&#xff0c;改善和清理代码。需要准备的架设工具&#xff1a;端&#xff08;版本&#xff09;、…

皮尔逊相关性分析一文详解+python实例代码

目录 前言 一、数值类型 二、皮尔逊系数使用场景 二、皮尔逊相关系数(Pearson correlation) 1.定义 2.线性关系判定 3.正态检验 1.KS检验 4.计算代码 点关注&#xff0c;防走丢&#xff0c;如有纰漏之处&#xff0c;请留言指教&#xff0c;非常感谢 参阅 前言 相关性…

MySQL中这10个小玩意,让人眼前一亮

我最近几年用MYSQL数据库挺多的&#xff0c;发现了一些非常有用的小玩意&#xff0c;今天拿出来分享到大家&#xff0c;希望对你会有所帮助。 1.group_concat 在我们平常的工作中&#xff0c;使用group by进行分组的场景&#xff0c;是非常多的。 比如想统计出用户表中&…

给Kylin iso中添加自定义rpm包

前言 在日常产品交付中会有如下需求&#xff1a; 客户自己安装centos / ubuntu / kylin iso&#xff0c;然后我们把rpm包给到客户 我们直接将rpm包封装到iso&#xff0c;给客户交付整体iso&#xff0c;这样不管是对于客户还是对于公司都是最省心的&#xff0c;会避免很多因为系…

疯狂2023:科技迎来爆发之年,关注云计算这些方向

临近年末&#xff0c;熬过艰难苦恨繁霜鬓的2022&#xff0c;打工人已默默扛起来年的OKR 。根据市场研究机构Garner预测&#xff0c;2023将迎来科技爆发之年&#xff0c;最值得关注的云计算趋势是AI和ML、多云和混合云解决方案、物联网、云安全等。 及时了解不断变化的技术格局…

汇聚数据库创新力量 GBASE携手openGauss助企业数字化转型

12月29日&#xff0c;openGauss Summit 2022于线上举行。本次大会是由openGauss开源数据库社区联合行业组织、伙伴及客户共同举办的一场年度数据库产业界交流与分享峰会。天津南大通用数据技术股份有限公司&#xff08;简称&#xff1a;GBASE&#xff09;首次以openGauss社区理…

PDF转换成JPG图片怎么转换?这两招轻松搞定

PDF转换成JPG图片怎么转换&#xff1f;PDF文件是我们常用的一类文件&#xff0c;在现在的办公环境中&#xff0c;PDF真的用途很广&#xff0c;不仅是日常发送文件&#xff0c;还有学习工作中都会用到&#xff0c;不过有时候我们也需要将PDF文件转换成其他的格式&#xff0c;比如…

JUC并发编程学习笔记(五)读写锁和阻塞队列

8 读写锁 8.1 悲观锁和乐观锁介绍 回顾悲观锁和乐观锁的概念 悲观锁&#xff1a;单独每个人完成事情的时候&#xff0c;执行上锁解锁。解决并发中的问题&#xff0c;不支持并发操作&#xff0c;只能一个一个操作&#xff0c;效率低 顾名思义&#xff0c;就是比较悲观的锁&am…

配置文件中命名空间的作用

命名空间 实际上就是一个由程序设计者命名的内存区域&#xff0c;程序设计者可以根据需要指定一些有名字的空间域&#xff0c;把一些全局实体分别放在各个命名空间中&#xff0c;从而与其他全局实体分隔开来。 命名空间的作用 是建立一些互相分隔的作用域&#xff0c;把一些…

11_3、Java集合之迭代器Iterator接口

一、引入Iterator对象称为迭代器(设计模式的一种)&#xff0c;主要用于遍历 Collection 集合中的元素。 GOF给迭代器模式的定义为&#xff1a;提供一种方法访问一个容器(container)对象中各个元 素&#xff0c;而又不需暴露该对象的内部细节。迭代器模式&#xff0c;就是为容器…

华脉智联铁路巡检及指挥调度系统

技术方案 整套系统基于云通讯平台和智能终端的结合&#xff0c;实现指挥中心和现场人员的实时互动&#xff0c;同时保证现场取证材料的实时上报以及指挥中心实时将决策及命令下发到现场人员。提高巡检效率及巡检准确性。 方案拓扑如下&#xff1a; 在指挥中心通信机房部署指挥…

k8s集群监控cadvisor+prometheus+grafana部署

目录 1.新建命名空间monitor 2.部署 2.1部署cadvisor 2.2部署node_exporter 2.3部署prometheus资源 2.4部署rbac权限 2.5.部署 metrics 2.6部署grafana 3.测试效果 参考文章&#xff1a; k8s集群部署cadvisornode-exporterprometheusgrafana监控系统 - cyh00001 - 博客园 …

JDBC --- 数据库连接池 C3P0

目录 1、基本定义 2、使用C3P0&#xff08;数据库连接池&#xff09;的必要性 2.1.JDBC传统模式开发存在的主要问题 2.1.1时间和内存资源消耗巨大 2.1.2 有内存泄漏的风险 3、数据库连接池的详细说明 4、使用连接池的明显优势 4.1.资源的高效利用 4.2.更快的系统反应速…