当前位置: 首页 > news >正文

如何实现一个K8S DevicePlugin?

什么是device plugin

k8s允许限制容器对资源的使用,比如CPU内存,并以此作为调度的依据。

当其他非官方支持的设备类型需要参与到k8s的工作流程中时,就需要实现一个device plugin

Kubernetes提供了一个设备插件框架,你可以用它来将系统硬件资源发布到Kubelet

供应商可以实现设备插件,由你手动部署或作为 DaemonSet 来部署,而不必定制 Kubernetes 本身的代码。

目标设备包括 GPU、高性能 NIC、FPGA、 InfiniBand 适配器以及其他类似的、可能需要特定于供应商的初始化和设置的计算资源。

更多云原生、K8S相关文章请点击【专栏】查看!

发现插件

一个新的device plugin是如何被kubelet发现的?

device plugin通过gRPC的方式与kubelet通信,kubelet实现了Register接口,用于注册插件。

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

通过这个接口, 向kubelet提交当前插件的信息,包括插件的名称、版本、socket路径等。

已注册的插件信息并不会被持久化下来, 也就是说当kubelet重启后,插件需要重新调用Register方法。

kuelet重启时会删除插件的socket文件, 插件通过监听socket文件的方式来感知kubelet的重启并重新注册。

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

当插件注册成功后, 根据插件中的配置与定义, 可能会有类似下面的pod配置以使用插件中的资源。

apiVersion: v1
kind: Pod
metadata:name: demo-pod
spec:containers:- name: demo-container-1image: registry.k8s.io/pause:2.0resources:limits:hardware-vendor.example/foo: 2
#
# 这个 pod 需要两个 hardware-vendor.example/foo 设备
# 而且只能够调度到满足需求的节点上
#
# 如果该节点中有 2 个以上的设备可用,其余的可供其他 Pod 使用

在这里插入图片描述

AMD GPU插件源码解析

插件的实现并不复杂, 只需要实现几个接口函数即可。

service DevicePlugin {// GetDevicePluginOptions 返回与设备管理器沟通的选项。// kuelet 在每次方法调用前都会调用这个方法,来获取可用的设备插件选项。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) {}
}

以下源码解析以AMD GPU插件为例。

代码版本 0.12.0

仓库地址 https://github.com/ROCm/k8s-device-plugin

源码解析

插件启动流程

AMD GPU插件的框架,是使用的"github.com/kubevirt/device-plugin-manager/pkg/dpm"这个包。

AMD的插件确实实现的很粗糙, 这里我们只用它分析实现一个插件需要做什么。

程序启动时实例化Manager对象, 并调用Run方法。

func main() {// ...// Lister用于传递心跳与资源更新l := Lister{ResUpdateChan: make(chan dpm.PluginNameList),Heartbeat:     make(chan bool),}manager := dpm.NewManager(&l)// ...// 启动管理器manager.Run()
}

Run方法中启动了gRPC服务, 并注册了AMD GPU插件。

func (dpm *Manager) Run() {// ...// 监听socket文件变化(kubelet会在重启时删除)fsWatcher, _ := fsnotify.NewWatcher()defer fsWatcher.Close()// DevicePluginPath = "/var/lib/kubelet/device-plugins/"fsWatcher.Add(pluginapi.DevicePluginPath)// 启动插件监听方法, // 实际是将上面传入Liste.ResUpdateChan的数据转发到这个chan中pluginsCh := make(chan PluginNameList)defer close(pluginsCh)go dpm.lister.Discover(pluginsCh)
HandleSignals:for {select {case newPluginsList := <-pluginsCh:// 创建新的插件服务, 并启动服务dpm.handleNewPlugins(pluginMap, newPluginsList)case event := <-fsWatcher.Events:if event.Name == pluginapi.KubeletSocket {// kubelet重启时, 重新注册插件if event.Op&fsnotify.Create == fsnotify.Create {dpm.startPluginServers(pluginMap)}if event.Op&fsnotify.Remove == fsnotify.Remove {dpm.stopPluginServers(pluginMap)}}case s := <-signalCh:switch s {case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT:// 优雅退出dpm.stopPlugins(pluginMap)break HandleSignals}}}
}

创建插件服务会返回一个devicePlugin对象:

// dpm.handleNewPlugins(pluginMap, newPluginsList) 最终会调用这个方法
func newDevicePlugin(resourceNamespace string, pluginName string, devicePluginImpl PluginInterface) devicePlugin {return devicePlugin{DevicePluginImpl: devicePluginImpl,// DevicePluginPath = "/var/lib/kubelet/device-plugins/"// resourceNamespace = "amd.com"Socket:           pluginapi.DevicePluginPath + resourceNamespace + "_" + pluginName,ResourceName:     resourceNamespace + "/" + pluginName,Name:             pluginName,Starting:         &sync.Mutex{},}
}
type devicePlugin struct {// 实现的deviceplugin serverDevicePluginImpl PluginInterfaceResourceName     stringName             string// socket文件路径Socket           stringServer           *grpc.ServerRunning          boolStarting         *sync.Mutex
}

启动服务最终会由StartServer这个方法来完成。

func (dpi *devicePlugin) StartServer() error {// ...if dpi.Running {return nil}// 启动grpc服务err := dpi.serve()if err != nil {return err}// 调用Register方法向kubelet注册插件err = dpi.register()if err != nil {dpi.StopServer()return err}dpi.Running = truereturn nil
}
func (dpi *devicePlugin) serve() error {// ...// 可以看见是以socket文件启动的grpc服务sock, err := net.Listen("unix", dpi.Socket)if err != nil {glog.Errorf("%s: Failed to setup a DPI gRPC server: %s", dpi.Name, err)return err}dpi.Server = grpc.NewServer([]grpc.ServerOption{}...)pluginapi.RegisterDevicePluginServer(dpi.Server, dpi.DevicePluginImpl)go dpi.Server.Serve(sock)// ...return nil
}
func (dpi *devicePlugin) register() error {// KubeletSocket = DevicePluginPath + "kubelet.sock"// "/var/lib/kubelet/device-plugins/kubelet.sock"// 与kubelet通信conn, err := grpc.Dial(pluginapi.KubeletSocket, grpc.WithInsecure(),grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {return net.DialTimeout("unix", addr, timeout)}))defer conn.Close()client := pluginapi.NewRegistrationClient(conn)// 向kubelet注册插件reqt := &pluginapi.RegisterRequest{Version:      pluginapi.Version,Endpoint:     path.Base(dpi.Socket),ResourceName: dpi.ResourceName,Options:      options,}_, err = client.Register(context.Background(), reqt)// ...return nil
}

socket文件默认会放在/var/lib/kubelet/device-plugins目录下, 所以当以daemonset的方式部署插件时,需要将这个目录挂载到容器中。

服务实现

AMD GPU插件只实现了两个关键方法(因为不同设备插件的实现都不一样,所以这里不展开):

  • ListAndWatch
  • Allocate

所以它的GetDevicePluginOptions方法返回的是一个空结构体

func (p *Plugin) GetDevicePluginOptions(ctx context.Context, e *pluginapi.Empty) (*pluginapi.DevicePluginOptions, error) {return &pluginapi.DevicePluginOptions{}, nil
}
type DevicePluginOptions struct {// 是否需要调用 PreStartContainer 方法PreStartRequired bool `protobuf:"varint,1,opt,name=pre_start_required,json=preStartRequired,proto3" json:"pre_start_required,omitempty"`// 是否需要调用 GetPreferredAllocation 方法GetPreferredAllocationAvailable bool     `protobuf:"varint,2,opt,name=get_preferred_allocation_available,json=getPreferredAllocationAvailable,proto3" json:"get_preferred_allocation_available,omitempty"`
}

服务部署

设备插件可以作为节点操作系统的软件包来部署、作为 DaemonSet 来部署或者手动部署。

如果你将设备插件部署为 DaemonSet, /var/lib/kubelet/device-plugins 目录必须要在插件的 PodSpec 中声明作为 卷(Volume)被挂载到插件中。

实现一个设备插件

  1. 实现一个虚假设备, 用于测试插件。(可选)
  2. 实现DevicePlugin接口。 我们可以仅实现ListAndWatchAllocate两个关键方法。
  3. 注册gRPC服务, 并向kubelet注册插件。
  4. 监听kubelet的socket文件变化, 重新注册插件。

代码实现

待补充…

相关文章:

如何实现一个K8S DevicePlugin?

什么是device plugin k8s允许限制容器对资源的使用&#xff0c;比如CPU和内存&#xff0c;并以此作为调度的依据。 当其他非官方支持的设备类型需要参与到k8s的工作流程中时&#xff0c;就需要实现一个device plugin。 Kubernetes提供了一个设备插件框架&#xff0c;你可以用…...

Android LruCache源码分析

文章目录 Android LruCache源码分析概述LruCache和LinkedHashMap关系源码分析属性写入数据读取数据删除缓存 Android LruCache源码分析 概述 LruCache&#xff08;Least Recently Used Cache&#xff0c;最近最少使用缓存&#xff09;是 Android 中的一种缓存机制。 根据数据…...

如何使用Inno Setup制作Unity构建程序的Windows安装程序

1. 准备 &#xff08;1&#xff09;准备好Unity构建的程序集合 必须包括&#xff1a; Data文件夹&#xff08;xxx_Data&#xff09; Mono文件夹&#xff08;MonoBleedingEdge&#xff09; 打包的应用程序文件&#xff08;xxx.exe&#xff09; Unity播放器dll文件&#xff…...

linux 面试题

1.linux操作系统的常用指令可以详细说下吗,平常哪些用的比较多 文件目录操作命令: ls cd more cat tail mkdir touch rm rmdir 拷贝复制: cp mv 打包解包压缩解压: tar -z 解亚压缩 -c 打包 -x 解包 -v 显示过程 -f 指定文件名 文本编辑: vi vim 查找: find 查找文件 gre…...

嵌入式中逻辑分析仪基本操作方法

前期准备 1.一块能触摸的屏对应的主板机 2.逻辑分析仪对应的软件工具 3.对应的拓展板 4.确定拓展板的引脚分布情况 第一步&#xff1a;逻辑分析仪j基本操作 1.数据捕捉需要先进行对应软件安装,并按照需求进行配置 2.这里以A20为例:此手机使用显示驱动芯片CST148,触摸屏分辨…...

ONLYOFFICE 桌面编辑器 v8.0 更新内容详细攻略

文章目录 引言PDF 表单RTL 支持电子表格中的新增功能Moodle 集成用密码保护 PDF 文件从“开始”菜单快速创建文档本地界面主题下载安装桌面编辑工具总结 引言 官网链接&#xff1a; ONLYOFFICE 官方网址 ONLYOFFICE 桌面编辑器是一款免费的文档处理软件&#xff0c;适用于 Li…...

2024-2-22 作业

作业要求&#xff1a; 复习前面知识点(指针、结构体、函数)整理思维导图顺序表(按位置插入、按位置删除和去重、重新写)理解链表的代码&#xff0c;尝试写一下链表的尾插和输出 1.复习前面知识点(指针、结构体、函数) 2.整理思维导图 3.顺序表(按位置插入、按位置删除和去重、…...

2.1 RK3399项目开发实录-升级固件介绍(物联技术666)

1. 介绍 1.1. 前言 AIO-3399J 出厂默认安装Android操作系统&#xff0c;如果用户要运行其他操作系统&#xff0c;需要使用对应的固件烧写到主板。 AIO-3399J 有灵活的启动方式。一般情况下&#xff0c;除非硬件损坏&#xff0c;AIO-3399J 开发板是不会变砖的。 如果在升级过…...

Uniapp + VUE3.0 实现双向滑块视频裁剪效果

效果图 <template><view v-if"info" class"all"><video:src"info.videoUrl"class"video" id"video" :controls"true" object-fit"fill" :show-fullscreen-btn"false"play-btn…...

【算法小讲堂】#1 贪心算法

引入——关于贪心算法 我们先来做一个小游戏——现在假设自己是一个小偷&#xff0c;桌上有一些物品&#xff0c;包括一台iPhone15、一个充电宝、一个眼罩和一个溜溜梅。此时&#xff0c;你听说警察即将到来&#xff0c;那么你会先带走哪个东西呢&#xff1f; 一般来讲&#xf…...

判断当前shell版本

查看$SHELL环境变量&#xff1a; echo $SHELL输出的结果将是当前使用的shell的路径。例如&#xff0c;如果输出为 /bin/bash&#xff0c;则表示当前使用的是Bash shell。 查看ps命令输出&#xff1a; ps -p $$上述命令将显示当前终端进程的信息&#xff0c;其中 $$ 代表当前进…...

如何实现两个电脑之间通过以太网(网线)实现文件互传

如何实现两个电脑之间通过以太网&#xff08;网线&#xff09;实现文件互传 本帖目的&#xff1a;介绍如何通过以太网&#xff08;网线&#xff09;连接两台电脑&#xff0c;通过文件夹共享的方式&#xff0c;实现两台电脑之间的文件互传。 本帖以笔者实际工作上遇到的场景为例…...

Jenkins 中部署Nodejs插件并使用,并构建前端项目(3)

遇到多个版本nodeJS需要构建的时候 1、第一种就是一个配置安装&#xff0c;然后进行选中配置 2、第二种就是插件&#xff1a;nvm-wrapper&#xff0c;我们还是选用NodeJS插件&#xff1a; &#xff08;1&#xff09;可以加载任意npmrc文件&#xff1b; &#xff08;2&#x…...

VUE为什么有的属性要加冒号

<el-menu-item :index "/item.menuClick" v-for"(item,i) in menu"><i class"item.menuIcon" ></i><span slot"title">{{item.menuName}}</span></el-menu-item>不加不行 加了好像是吧整体作为…...

微信小程序 --- wx.request网络请求封装

网络请求封装 网络请求模块难度较大&#xff0c;如果学习起来感觉吃力&#xff0c;可以直接学习 [请求封装-使用 npm 包发送请求] 以后的模块 01. 为什么要封装 wx.request 小程序大多数 API 都是异步 API&#xff0c;如 wx.request()&#xff0c;wx.login() 等。这类 API 接口…...

通义千问Qwen-7B-Chat Windows本地部署教程-详细认真版

通义千问本地部署教程&#x1f680; 本专栏的第四弹&#xff0c;在实现了联网调用通义千问模型进行多轮对话&#xff0c;流式输出&#xff0c;以及结合LangChain实现自建知识库之后&#xff0c;开始准备考虑实现对大模型进行本地部署&#xff0c;网上找不到看着比较舒服的教程&…...

探索C语言位段的秘密

位段 1. 什么是位段2. 位段的内存分配3. 位段的跨平台问题4. 位段的应用4. 使用位段的注意事项 1. 什么是位段 我们使用结构体实现位段&#xff0c;位段的声明和结构体是类似的&#xff0c;有两个不同&#xff1a; 位段的成员必须是int&#xff0c;unsigned int&#xff0c;或…...

数据库-数据库设计-社交关系

佛 每有一个新方案&#xff0c;就要考虑有什么影响增删改查可扩展性 MySQL 根据ER图设计表 create table follow(id bigint unsigned not null auto_increment comment 主键,gmt_create datetime null default current_timestamp,gmt_modified null default current_timest…...

YOLO算法改进Backbone系列之:EfficientViT

EfficientViT: Memory Effificient Vision Transformer with Cascaded Group Attention 摘要&#xff1a;视觉transformer由于其高模型能力而取得了巨大的成功。然而&#xff0c;它们卓越的性能伴随着沉重的计算成本&#xff0c;这使得它们不适合实时应用。在这篇论文中&#x…...

JANGOW: 1.0.1

kali:192.168.223.128 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -p- 192.168.223.154 开启了21 80端口 web看一下&#xff0c;有个busque.php参数是buscar,但是不知道输入什么&#xff0c;尝试文件包含失败 扫描目录 dirsearch -u http://192.168.223.154 dirse…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...

鸿蒙HarmonyOS 5军旗小游戏实现指南

1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;采用DevEco Studio实现&#xff0c;包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...