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

【kubernetes】kubernetes中的Controller

1 什么是Controller?

kubernetes采用了声明式API,与声明式API相对应的是命令式API:

  • 声明式API:用户只需要告诉期望达到的结果,系统自动去完成用户的期望
  • 命令式API:用户需要关注过程,通过命令一步一步完成用户的需求

因此,用户向k8s提交的yaml文件中最重要的部分就是spec,相当于就是用户期望的结果,而使用-o yaml选项查看时,还有一个很重要的部分就是status,它表示的就是当前状态,因此,k8s主要任务就是完成status->spec的转变。这项工作就是Controller(控制器)完成的。

对于不同的资源,控制逻辑是不一样的,因此,就有很多Controller,例如,DeploymentController负责将Deployment的status向spec进行转变,ReplicaSetController负责将ReplicaSet的status向spec进行转变。

从上面可以看出,Controller的工作方式如下:

  • 监听资源变化
  • 得到资源的当前状态status和期望状态spec
  • 执行逻辑使得status->spec

下面以Deployment的创建操作为例说明整个流程:
请添加图片描述

通过上图重新复习下各组件的工作方式:

  • apiserver:为其他组件提供接口,并且所有的组件都通过apiserver进行交互
  • etcd:存储集群的资源对象
  • Controller Manager:管理控制器,Watch -> Analyze -> Act,监听资源的变化,分析出spec和status的差别,执行操作使得status向spec转变
  • Scheduler:监听资源的变化,如果发现未调度的Pod,通过一定的策略选择出Node,设置Pod的Node字段
  • Kubelet:监听调度给当前Node的Pod,并执行对应的操作

可以发现,除了apiserver和etcd,其他组件都可以称为Controller。

2 Controller的实现

知道了Controller的工作方式,如果是我们自己实现Controller,可以会这样来实现:

请添加图片描述

Controller直接通过Apiserver的接口监控对应资源的变化,当资源发生变化时,直接执行对应的业务逻辑,也就是调协循环。

这样会有啥问题呢?

当集群中Node很多时,就会有很多kubelet监控Pod的状态变化,而所有的监听操作都需要通过apiserver,那么apiserver的压力就会很大,就会造成集群的不稳定。

当然,其他资源(例如,Pod或者服务)很多时,同样会造成集群不稳定。

因此,k8s的client-go(client-go)库采用了另外的设计:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

client-go components:

  • Reflector:对特定类型的资源执行ListAndWatch,当监听到资源变更时,通过API获取最新的资源对象,然后将它放到Delta Fifo queue队列中
  • Informer:从Delta Fifo queue队列中弹出对象,然后调用Indexer放到Store里面,同时调用用户提交的回调函数(ResourceEventHandler)
  • Indexer:用于操作Store中的对象

Custom Controller components:

  • Informer Reference和Indexer Reference都是对client-go中对象的引用,用户控制器可以通过cache库直接创建或者使用Factory工厂函数创建
  • ResourceEventHandler:用户控制器接收对象的回调函数,一般来说,里面的逻辑就是,获取对象的key,然后将key写入WorkQueue
  • WorkQueue:用户控制器创建的队列,负责存储用户控制器需要处理的对象的key
  • Process Item:从WorkQueue中读取key,通过key获取对应的对象

上图是通常会给出的关于Controller的实际实现的逻辑,初看还是挺复杂的,大致的模块和功能如下:

请添加图片描述

于是,Controller实现的步骤如下:

  • 获取Informer和Indexer的引用,指定要监控变更的资源类型,注册ResourceEventHandler,并创建WorkQueue,用上述的3个对象初始化我们自己的Controller
  • 编写Process Item Loop,从WorkQueue中读取key,然后执行我们自己的业务逻辑

因此,整个Controller我们需要注入的逻辑只有2个部分,其他都是相对固定的:

  • ResourceEventHandler
  • Process Item

3 Controller的使用

上面介绍了k8s中的Controller的实现,而要使用

下面对client-go中的workqueue的例子进行分析:

workqueue example by client-go

type Controller struct {indexer  cache.Indexer // Indexer,缓存的索引queue    workqueue.RateLimitingInterface // 带限速功能的WorkQueueinformer cache.Controller // Informer
}// 创建控制器
func NewController(queue workqueue.RateLimitingInterface, indexer cache.Indexer, informer cache.Controller) *Controller {return &Controller{informer: informer,indexer:  indexer,queue:    queue,}
}// worker的具体执行逻辑
func (c *Controller) processNextItem() bool {// 从workqueue中获取keykey, quit := c.queue.Get()if quit {return false}// 告诉队列已经处理完毕defer c.queue.Done(key)err := c.syncToStdout(key.(string))// 错误处理c.handleErr(err, key)return true
}// 控制器的业务逻辑,这里就执行status->spec的转变
func (c *Controller) syncToStdout(key string) error {obj, exists, err := c.indexer.GetByKey(key)if err != nil {klog.Errorf("Fetching object with key %s from store failed with %v", key, err)return err}if !exists {// Pod已经不存在fmt.Printf("Pod %s does not exist anymore\n", key)} else {// 这里执行status->spec的转变逻辑fmt.Printf("Sync/Add/Update for Pod %s\n", obj.(*v1.Pod).GetName())}return nil
}// 错误处理,包含重试处理
func (c *Controller) handleErr(err error, key interface{}) {if err == nil {// 处理完毕c.queue.Forget(key)return}// 如果出现问题,会进行重试,也就是重新入workqueue// 但是,入workqueue不超过5次if c.queue.NumRequeues(key) < 5 {klog.Infof("Error syncing pod %v: %v", key, err)// 重新入workqueuec.queue.AddRateLimited(key)return}c.queue.Forget(key)runtime.HandleError(err)klog.Infof("Dropping pod %q out of the queue: %v", key, err)
}// 启动我们自己的控制器
func (c *Controller) Run(workers int, stopCh chan struct{}) {defer runtime.HandleCrash()defer c.queue.ShutDown()// 启动Informer开始监听资源变化go c.informer.Run(stopCh)// 等待cache同步if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))return}// 运行若干个worker,// wait.Until(),每隔1秒执行runWorker()函数,直到stopCh收到结束信号for i := 0; i < workers; i++ {go wait.Until(c.runWorker, time.Second, stopCh)}// 读取结束信号,结束控制器<-stopChklog.Info("Stopping Pod controller")
}func (c *Controller) runWorker() {for c.processNextItem() {}
}func main() {var kubeconfig stringvar master stringflag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")flag.StringVar(&master, "master", "", "master url")flag.Parse()// 通过master和kubeconfig生成配置对象config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)if err != nil {klog.Fatal(err)}// 根据配置对象生成clientset,用于连接k8sclientset, err := kubernetes.NewForConfig(config)if err != nil {klog.Fatal(err)}// 创建Pod的watcherpodListWatcher := cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", v1.NamespaceDefault, fields.Everything())// 创建workqueuequeue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())// 创建Indexer和Informer,其中重要的是两个参数,Pod的watcher和回调函数// 告知Informer,我们只监听Pod的资源变化,并且,给Infomer注册回调函数indexer, informer := cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{AddFunc: func(obj interface{}) {key, err := cache.MetaNamespaceKeyFunc(obj)if err == nil {queue.Add(key)}},UpdateFunc: func(old interface{}, new interface{}) {key, err := cache.MetaNamespaceKeyFunc(new)if err == nil {queue.Add(key)}},DeleteFunc: func(obj interface{}) {key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)if err == nil {queue.Add(key)}},}, cache.Indexers{})// 创建我们自己的控制器controller := NewController(queue, indexer, informer)// 启动控制器stop := make(chan struct{})defer close(stop)go controller.Run(1, stop)// Wait foreverselect {}
}

参考资料:

1 client-go under the hood

2 client-go Examples

3 k8s-client-go demo

4 writing controllers

相关文章:

【kubernetes】kubernetes中的Controller

1 什么是Controller&#xff1f; kubernetes采用了声明式API&#xff0c;与声明式API相对应的是命令式API&#xff1a; 声明式API&#xff1a;用户只需要告诉期望达到的结果&#xff0c;系统自动去完成用户的期望命令式API&#xff1a;用户需要关注过程&#xff0c;通过命令一…...

RabbitMQ-死信队列

接上文 RabbitMQ-java使用消息队列 1 死信队列简介 死信队列模式实际上本质是一个死信交换机绑定的死信队列&#xff0c;当正常队列的消息被判定为死信时&#xff0c;会被发送到对应的死信交换机&#xff0c;然后再通过交换机发送到死信队列中&#xff0c;死信队列也有对应的消…...

ElasticSearch - 基于 DSL 、JavaRestClient 实现数据聚合

目录 一、数据聚合 1.1、基本概念 1.1.1、聚合分类 1.1.2、特点 1.2、DSL 实现 Bucket 聚合 1.2.1、Bucket 聚合基础语法 1.2.2、Bucket 聚合结果排序 1.2.3、Bucket 聚合限定范围 1.3、DSL 实现 Metrics 聚合 1.4、基于 JavaRestClient 实现聚合 1.4.1、组装请求 …...

什么是数学建模(mooc笔记)

什么是数学建模 前提&#xff1a;我们数学建模国赛计划选择C题&#xff0c;故希望老师的教学中侧重与C题相关性大的模型及其思想进行培训。之后的学习内容中希望涉及以下知识点&#xff1a; logistic回归相关知识点。如&#xff1a;用法、适用、限制范围等。精学数学建模中常…...

基于SpringBoot的流浪动物管理系

基于SpringBoot的流浪动物管理系的设计与实现&#xff0c;前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 首页 后台登陆界面 管理员界面 摘要 基于Spring Boot的…...

fcpx插件:82种复古电影胶卷框架和效果mFilm Matte

无论您是在制作音乐剪辑、私人假期视频还是大型广告活动&#xff0c;这个专业的插件都将帮助您为您的镜头赋予真正的电影角色。 复古效果在任何视频中都能立即识别出来&#xff0c;增添了感伤的复古氛围&#xff0c;并使镜头更具说服力。使用 mFilm Matte 轻松实现这些特征&…...

【LeetCode热题100】--98.验证二叉搜索树

98.验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 由于二…...

wxpython:wx.grid 表格显示 Excel xlsx文件

pip install xlrd xlrd-1.2.0-py2.py3-none-any.whl (103 kB) 摘要: Library for developers to extract data from Microsoft Excel (tm) spreadsheet files pip install wxpython4.2 wxPython-4.2.0-cp37-cp37m-win_amd64.whl (18.0 MB) Successfully installed wxpython-4.…...

事件循环机制

eventLoop 事件循环&#xff08;Event Loop&#xff09;是用于管理和调度异步任务执行的一种机制&#xff0c;通常在浏览器中&#xff0c;也在其他 JavaScript 运行环境中存在。事件循环确保 JavaScript 单线程的执行模型下能够处理非阻塞的异步任务&#xff0c;以避免程序阻塞…...

苹果曾考虑基于定位控制AirPods Pro自适应音频

在一次最近的采访中&#xff0c;苹果公司的高管Ron Huang和Eric Treski透露&#xff0c;他们在开发AirPods Pro自适应音频功能时&#xff0c;曾考虑使用GPS信号来控制音频级别。这个有趣的细节打破了我们对AirPods Pro的固有认知&#xff0c;让我们对苹果的创新思维有了更深的…...

【代码阅读笔记】yolov5 rknn模型部署

一、main函数思路 二、值得学习的地方 1、关注yolov5检测流程 2、其中几个重要的结构体 typedef struct {int left;int right;int top;int bottom; } YOLOV5_BOX_RECT; // box坐标信息typedef struct {char name[YOLOV5_NAME_MAX_SIZE];int class_index;YOLOV5_BOX_RECT box…...

【多线程】进程与线程 并发编程 面试题总结

进程和线程 进程是程序执行时的一个实例&#xff0c;即它是程序已经执行到何种程度的数据结构的汇集。从内核的观点看&#xff0c;进程的目的就是担当分配系统资源&#xff08;CPU时间、内存等&#xff09;的基本单位。线程是进程的一个执行流&#xff0c;是CPU调度和分派的基…...

C++算法 —— 动态规划(10)二维费用背包

文章目录 1、动规思路简介2、一和零3、盈利计划 背包问题需要读者先明白动态规划是什么&#xff0c;理解动规的思路&#xff0c;并不能给刚接触动规的人学习。所以最好是看了之前的动规博客&#xff0c;以及两个背包博客&#xff0c;或者你本人就已经懂得动规了。 1、动规思路简…...

MySQL数据库正在耗用大量CPU的问题排查

这是一篇实战性的文章&#xff0c;如何处理正在发生的MYSQL服务器CPU飙升的问题&#xff0c;一般情况下&#xff0c;MySQL是不会耗用这么高的CPU的&#xff0c;要么是不走索引的查询&#xff0c;要么是同一时间出现了大量比较耗用资源的查询&#xff0c;不管出现的是哪一种情况…...

php替换字符串里的a变为b

$tempstrstr_replace("\\","/",$tempstr); //把$tempstr中的a替换成b $tempstrstr_replace("a","b",$tempstr);...

黑豹程序员-架构师学习路线图-百科:CSS-网页三剑客

文章目录 1、为什么需要CSS2、发展历史3、什么是CSS4、什么是SASS、SCSS 1、为什么需要CSS 作为网页三剑客的第二&#xff0c;CSS为何需要它&#xff0c;非常简单HTML只能完成页面的展现&#xff0c;但其做出来的页面奇丑无比。 随着网络的普及&#xff0c;人们的要求更高&…...

NUWA论文阅读

论文链接&#xff1a;NUWA: Visual Synthesis Pre-training for Neural visUal World creAtion 文章目录 摘要引言相关工作视觉自回归模型视觉稀疏自注意 方法3D数据表征3D Nearby Self-Attention3D编码器-解码器训练目标 实验实现细节与SOTA比较T2I微调T2V微调V2V微调Sketch-t…...

4.Tensors For Beginners-Vector Definition

在上一节&#xff0c;已经了解了前向和后向转换。 什么是向量&#xff1f; 定义1&#xff1a;向量是一个数字列表 这很简洁&#xff0c;也通俗易懂。 现有两个向量&#xff1a; 如果要把这两个向量给加起来&#xff0c;只需把对应位置的元素(组件)给加起来。 而要缩放向量&…...

vertx学习总结5

这章我们讲回调&#xff0c;英文名&#xff1a;Beyond callbacks 一、章节覆盖&#xff1a; 回调函数及其限制&#xff0c;如网关/边缘服务示例所示 未来和承诺——链接异步操作的简单模型 响应式扩展——一个更强大的模型&#xff0c;特别适合组合异步事件流 Kotlin协程——…...

Go,从命名开始!Go的关键字和标识符全列表手册和代码示例!

目录 一、Go的关键字列表和分类介绍关键字在Go中的定位语言的基石简洁与高效可扩展性和灵活性 关键字分类声明各种代码元素组合类型的字面表示基本流程控制语法协程和延迟函数调用 二、Go的关键字全代码示例关键字全代码示例 三、Go的标识符定义基础定义特殊规定关键字与标识符…...

MiGPT终极指南:如何将小爱音箱改造成AI语音助手

MiGPT终极指南&#xff1a;如何将小爱音箱改造成AI语音助手 【免费下载链接】mi-gpt &#x1f3e0; 将小爱音箱接入 ChatGPT 和豆包&#xff0c;改造成你的专属语音助手。 项目地址: https://gitcode.com/GitHub_Trending/mi/mi-gpt 在智能家居日益普及的今天&#xff0…...

搜极星破局:拆解企业 “看不见、控不住、比不过” 困局

引言&#xff1a;AI 时代&#xff0c;企业陷入三重信息绝境2026 年&#xff0c;生成式 AI 全面主导用户决策链路&#xff0c;品牌竞争从搜索排名转向 AI 认知权重。但多数企业正深陷看不见、控不住、比不过的三重困局&#xff1a;看不见自身在 AI 平台的真实曝光状态&#xff0…...

Ubuntu 20.04虚拟机重启后断网?别慌,用Netplan配置静态IP一劳永逸(附避坑指南)

Ubuntu 20.04虚拟机网络配置终极指南&#xff1a;Netplan静态IP与持久化方案 当你兴奋地启动Ubuntu 20.04虚拟机准备大展身手时&#xff0c;突然发现网络连接消失了——这不是个别现象。许多开发者在本地虚拟化环境或云平台中都遭遇过类似困扰。本文将彻底解决这个"幽灵断…...

技能包管理器:开发者工具链标准化与版本隔离解决方案

1. 项目概述&#xff1a;一个为开发者赋能的技能包管理器在软件开发的世界里&#xff0c;我们每天都在与各种工具、库和依赖项打交道。从构建工具到代码格式化器&#xff0c;从静态分析器到部署脚本&#xff0c;一个现代项目的开发环境往往由数十个、甚至上百个独立的命令行工具…...

sdd-riper:专业磁盘镜像工具在数据恢复中的原理与实践

1. 项目概述与核心价值最近在整理一些老旧存储设备时&#xff0c;遇到了一个挺典型的问题&#xff1a;手头有几块年代久远的硬盘&#xff0c;里面可能还存着一些早年间的照片、文档&#xff0c;但硬盘本身已经不太稳定&#xff0c;系统里能识别&#xff0c;但拷贝文件时动不动就…...

硅应变计与Σ-Δ ADC协同设计及温度补偿技术

1. 硅应变计与Σ-Δ ADC的协同优势解析硅基应变计在现代传感器领域占据重要地位&#xff0c;其核心原理基于压阻效应——当硅材料发生机械形变时&#xff0c;晶格结构变化导致载流子迁移率改变&#xff0c;从而引起电阻值变化。与传统金属箔应变计相比&#xff0c;硅应变计的灵…...

光伏并网系统谐波抑制控制策略【附程序】

✨ 长期致力于锁相环、谐波电流检测、二阶广义积分器、LMS滤波器研究工作&#xff0c;擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流&#xff0c;点击《获取方式》 &#xff08;1&#xff09;基于双二阶广义积分器-锁频环的自适应…...

告别esptool失败!用乐鑫官方Flash工具给ESP8266刷MicroPython固件(保姆级图文)

ESP8266刷机新选择&#xff1a;乐鑫官方Flash工具全流程指南 为什么选择官方工具替代esptool&#xff1f; 每次看到命令行里跳出的红色报错信息&#xff0c;是不是有种想把开发板扔出窗外的冲动&#xff1f;"端口不存在"、"擦除失败"、"权限不足"…...

基于Vue的纯前端的库存销售系统

&#x1f680;【开源】 基于Vue的纯前端的库存销售系统 项目地址&#xff1a;https://github.com/cuiyunhao-2026/warhouse-sales-management-system 这是基于art design pro模板的二次开发 模板地址&#xff1a;https://github.com/Daymychen/art-design-pro 你是否&#x…...

GD32F303硬件I2C实战:手把手教你用AT24C02 EEPROM存储和读取设备配置参数

GD32F303硬件I2C实战&#xff1a;构建工业级参数存储系统 在嵌入式设备开发中&#xff0c;系统参数的持久化存储是个看似简单却暗藏玄机的需求。想象一下&#xff0c;当你的智能温控器经历突然断电后&#xff0c;所有用户设置的日程和偏好全部归零——这种体验足以让产品口碑崩…...