【kubernetes】k8s中的选主机制
leader-election选主机制
1 为什么需要leader-election?
在集群中存在某种业务场景,一批相同功能的进程同时运行,但是同一时刻,只能有一个工作,只有当正在工作的进程异常时,才会由另一个进程进行接管。这种业务逻辑通常用于实现一主多从。
如果有人认为,传统应用需要部署多个通常是为了容灾,而在k8s上运行的Pod受控制器管理,如果Pod异常或者Pod所在宿主机宕机,Pod是可以漂移到其他节点的,所以,不需要部署多个Pod,只需要部署一个Pod就行。k8s上的Pod确实可以漂移,但是,如果宿主机宕机,k8s认为Pod异常,并在其他节点重建Pod是有周期的,不能在查询不到Pod状态时立刻就将Pod驱逐掉,也许节点只是临时不可用呢?例如,负载很高,因此,判断宿主机宕机需要有个时间短。
k8s节点故障时,工作负载的调度周期
因此,在k8s中运行一主多从是为了能够实现主的快速切换。
2 kubernetes中的leader-election
k8s中也有这种业务场景,在多master场景下,只能有一个master上的进程工作,例如,scheduler和controller-manager。以scheduler来说,它的工作是给Pod分配合适的宿主机,如果有多个scheduler同时运行,就会出现竞争,因此,如果允许这种场景存在的话,就又需要实现一种调度逻辑:某个Pod由哪个scheduler进行调度,这相当于又要实现一层调度。但是,实际上调度工作是相对比较简单的,不需要多个scheduler进行负载,只需要一个scheduler进行调度就行。因此,k8s提供了leader-election的能力。
leader-election的具体工作方式是:各候选者将自身的信息写入某一个资源,如果写成功,某个后选择就称为了主,其他就是备,同时,在之后主会定期更新资源的时间,如果超过一段时间未更新时间,其他候选者发现资源的最后更新时间超过一定值,就会认为主挂掉,然后会向资源写入自身信息,从而成为新的主。
基于该原理,有一个现成的镜像可以使用:instana/leader-elector。
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: leadername: leader
spec:replicas: 3selector:matchLabels:app: leadertemplate:metadata:labels:app: leaderspec:containers:- image: instana/leader-elector:0.5.13name: leadercommand: ["/app/server","--id=$(POD_NAME)","--election=leader-election","--http=0.0.0.0:4040"]env:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name
上面的yaml有两个需要注意的地方:
- /app/server是二进制程序,id参数是候选者的唯一标识,election是资源名称,http是应用监听的IP和端口号
- 将pod的名称作为id参数,也就是候选者的唯一标识
创建deploy后,会启动三个Pod,通过kubectl logs可以看到只有一个Pod成为主,也就是向资源名称为leader-election的Endpoint写入了自身的Pod名称。然后通过代理(kubectl proxy)访问:http://localhost:8001/api/v1/namespaces/default/pods/:4040/proxy/,就会看到主的Pod名称。
知道了leader-election的大概原理,也知道了上面的镜像可以直接实现主的选举,那么如何使用呢?
2.1 Sidecar
直接将上面的leader-elector镜像作为Sidecar,将Pod名称作为候选者的唯一标识,然后将Pod名称也注入到环境变量,在业务进程起来后,定时调用http://localhost:4040就可以获取主,如果发现主的名称与自身的Pod的名称一致,就执行业务逻辑,否则一直等待。
2.2 SDK
使用Sidecar的好处是比较方便,开发成本低,不便的地方就是,适用场景有限,只能写入Endpoint资源。因此,在某些场景下,可以使用SDK,直接基于leader-election库开发。
k8s-leader-election
创建一个Lease类型的锁(当然,也可以是其他类型,但是lease更加轻量),创建资源时需要指定资源的命名空间、名称、标识(这一批Pod都会该命名空间的资源写入自身的唯一标识)。然后调用leaderelection库中的RunOrDie()函数,此时会指定:
- Lock:资源锁,将前面创建的Lease类型锁填入
- ReleaseOnCancel:
- LeaseDuration:租约时间
- RenewDeadline:leader刷新超时
- RetryPeriod:刷新租约的时间间隔
- Callbacks:指定成为leader时要执行的业务逻辑(OnStartedLeading),从leader变成非leader时要执行的逻辑(OnStoppedLeading),leader变更时要执行的逻辑(OnNewLeader)。
3 具体实现机制
// leaderelection/leaderelection.go
func (le *LeaderElector) Run(ctx context.Context) {defer runtime.HandleCrash()defer func() {le.config.Callbacks.OnStoppedLeading()}()// 申请资源锁,有三种情形:// 1 出错,则返回false,Run()直接退出// 2 获取到锁了,则返回true,执行回调函数// 3 未获取到锁,acquire()函数不会返回if !le.acquire(ctx) {return // ctx signalled done}ctx, cancel := context.WithCancel(ctx)defer cancel()// 申请成功后,执行回调函数go le.config.Callbacks.OnStartedLeading(ctx)// 定时刷新租约le.renew(ctx)
}// 申请资源锁
func (le *LeaderElector) acquire(ctx context.Context) bool {ctx, cancel := context.WithCancel(ctx)defer cancel()succeeded := falsedesc := le.config.Lock.Describe()klog.Infof("attempting to acquire leader lease %v...", desc)// 每隔RetryPeriod去申请资源锁,或者更新wait.JitterUntil(func() {succeeded = le.tryAcquireOrRenew(ctx)le.maybeReportTransition()if !succeeded {// 没有获取到锁,下一次再尝试klog.V(4).Infof("failed to acquire lease %v", desc)return}// 成功获取到锁,则退出le.config.Lock.RecordEvent("became leader")le.metrics.leaderOn(le.config.Name)klog.Infof("successfully acquired lease %v", desc)cancel()}, le.config.RetryPeriod, JitterFactor, true, ctx.Done())return succeeded
}func (le *LeaderElector) renew(ctx context.Context) {ctx, cancel := context.WithCancel(ctx)defer cancel()// 每隔RetryPeriod尝试更新租约wait.Until(func() {timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)defer timeoutCancel()err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) {return le.tryAcquireOrRenew(timeoutCtx), nil}, timeoutCtx.Done())le.maybeReportTransition()desc := le.config.Lock.Describe()if err == nil {klog.V(5).Infof("successfully renewed lease %v", desc)return}le.config.Lock.RecordEvent("stopped leading")le.metrics.leaderOff(le.config.Name)klog.Infof("failed to renew lease %v: %v", desc, err)cancel()}, le.config.RetryPeriod, ctx.Done())// if we hold the lease, give it upif le.config.ReleaseOnCancel {le.release()}
}// 尝试获取或者更新资源锁
func (le *LeaderElector) tryAcquireOrRenew(ctx context.Context) bool {now := metav1.Now()leaderElectionRecord := rl.LeaderElectionRecord{HolderIdentity: le.config.Lock.Identity(),LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second),RenewTime: now,AcquireTime: now,}// 1 获取资源锁记录oldLeaderElectionRecord, oldLeaderElectionRawRecord, err := le.config.Lock.Get(ctx)if err != nil {if !errors.IsNotFound(err) {klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err)return false}// 创建资源锁if err = le.config.Lock.Create(ctx, leaderElectionRecord); err != nil {klog.Errorf("error initially creating leader election record: %v", err)return false}le.setObservedRecord(&leaderElectionRecord)return true}// 2 将资源锁记录与缓存的上一次的值进行对比// 如果当前不是leader,并且资源锁没有过期,则退出if !bytes.Equal(le.observedRawRecord, oldLeaderElectionRawRecord) {le.setObservedRecord(oldLeaderElectionRecord)le.observedRawRecord = oldLeaderElectionRawRecord}if len(oldLeaderElectionRecord.HolderIdentity) > 0 &&le.observedTime.Add(le.config.LeaseDuration).After(now.Time) &&!le.IsLeader() {klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity)return false}// 3. We're going to try to update. The leaderElectionRecord is set to it's default// here. Let's correct it before updating.if le.IsLeader() {// 当前是leader,锁资源未过期,将之前的资源锁的数据填充到新的资源锁中(申请锁时间,切换次数)leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTimeleaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions} else {// 当前不是leaderleaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1}// 更新资源锁if err = le.config.Lock.Update(ctx, leaderElectionRecord); err != nil {klog.Errorf("Failed to update lock: %v", err)return false}le.setObservedRecord(&leaderElectionRecord)return true
}
相关文章:
【kubernetes】k8s中的选主机制
leader-election选主机制 1 为什么需要leader-election? 在集群中存在某种业务场景,一批相同功能的进程同时运行,但是同一时刻,只能有一个工作,只有当正在工作的进程异常时,才会由另一个进程进行接管。这…...
学生选课系统基础版
第四章java中的集合框架 4.1:java中的集合框架概述 1.java概念与作用 现实中很多事物凑在一起都是集合 如购物车是商品的集合 军队呢 是军人的集合 学校是学生的结合 数学中的集合: 具有共同属性的事物的总体 java中的集合类呢 跟数学的集…...
redis no-appendfsync-on-rewrite
no-appendfsync-on-rewriteyes 当用户请求写入redis的时候,这部分数据只是保存在内存中,主线程并不会马上对此数据进行 aof刷盘(而是根据aof刷盘的频率由子线程进行同步),这样子不会阻塞但是会导致数据丢失no-appendfs…...
Spring Cloud Gateway2之路由详解
Spring Cloud Gateway路由 文章目录 1. 前言2. Gateway路由的基本概念3. 三种路由1. 静态路由2. 动态路由1. 利用外部存储2. API动态路由 3. 服务发现路由(自动路由)3.1. 配置方式3.2 自动路由(服务发现)原理核心源码GatewayDiscoveryClientAutoConfigur…...
阿里云RDS关系型数据库详细介绍_多版本数据库说明
阿里云RDS关系型数据库大全,关系型数据库包括MySQL版、PolarDB、PostgreSQL、SQL Server和MariaDB等,NoSQL数据库如Redis、Tair、Lindorm和MongoDB,阿里云百科分享阿里云RDS关系型数据库大全: 目录 阿里云RDS关系型数据库大全 …...
Vue中的数据绑定
一、v-bind单向数据绑定 单向数据绑定中,数据只能由data流向页面。 v-bind:属性名"data变量" 或简写为 :属性名"data变量" 我们修改data中的iptvalue值,页面input框中的value值改变。 而我们修改input框中的value值࿰…...
前后端分离计算机毕设项目之基于SpringBoot的旅游网站的设计与实现《内含源码+文档+部署教程》
博主介绍:✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久,选择我们就是选择放心、选择安心毕业✌ 🍅由于篇幅限制,想要获取完整文章或者源码,或者代做&am…...
[JAVAee]Spring拦截器
适用场景 像是页面的登录验证处理,权限校验,登录日志的处理. 实现步骤 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理⽅法.将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法中. 下面以登录验证为例,实现拦…...
【nvm】Node Version Manager(NVM)安装配置以及使用(WIN版)
NVM 包管理工具 安装 访问NVM-Windows的GitHub页面:点击nvm-setup.exe。 根据提示进行下一步,文件位置选择自定义位置 验证安装是否成功 nvm version 。如果成功,它将显示NVM的版本号。 使用 nvm list available查看所有的可以被下载…...
【微服务】七. http客户端Feign
7.1 基于Feign远程调用 RestTimeplate方式调用存在的问题 先来看以前利用RestTemplate发起远程调用的代码: String url "http://userservice/user"order.getUserId(); User user restTemplate.getForObject(url,User.class);存在下面的问题…...
【Spring Boot 源码学习】OnWebApplicationCondition 详解
Spring Boot 源码学习系列 OnWebApplicationCondition 详解 引言往期内容主要内容1. getOutcomes 方法2. getMatchOutcome 方法3. isWebApplication 方法3.1 isServletWebApplication 方法3.2 isReactiveWebApplication 方法3.3 isAnyWebApplication 方法 总结 引言 上篇博文带…...
力扣之二分法
今天,学习了二分法,详细内容见代码随想录 (programmercarl.com),讲得十分好。 力扣之35. 搜索插入位置 - 力扣(LeetCode)。 class Solution { public:int searchInsert(vector<int>& nums, int target) {in…...
css图形化理解--扭曲函数skew()
transform: skewX(30deg);transform: skewY(45deg);transform: skew(30deg,45deg);transform: skewX(angleX);transform: skewY(angleY);transform: skew(angleX,angleY); 是CSS中的一个2D变换方法,它用于对元素沿X轴、Y轴进行倾斜变换。其中,angle表示倾…...
八、互联网技术——物联网
文章目录 一、智慧物联案例分析二、M2M技术三、数据保护综合案例分析一、智慧物联案例分析 智能物流是一种典型的物联网应用。一个物流仓储管理系统架构如下图所示: [问题1] 图中的三层功能:仓库物品识别、网络接入、物流管理中心,分别可对应到物联网基本架构中的哪一层? …...
聊聊MySQL的聚簇索引和非聚簇索引
文章目录 1. 索引的分类1. 存储结构维度2. 功能维度3. 列数维度4. 存储方式维度5. 更新方式维度 2. 聚簇索引2.1 什么是聚簇索引2.2 聚簇索引的工作原理 3. 非聚簇索引(MySQL官方文档称为Secondary Indexes)3.1 什么是非聚簇索引3.2 非聚簇索引的工作原理…...
python之subprocess模块详解
介绍 subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码。 这个模块的目的在于替换几个旧的模块和方法。 那么我们到底该用哪个模块、哪个…...
第10讲:Vue组件的定义与注册
定义组件 1. 在程序的 components 目录下新建一个名为 Child.vue 的文件 2. 在文件内键入如下代码 <template><div>Child</div> </template> <script> export default {name: Child } </script>新建的 Child .vue 文件即为我们定义的组件…...
Pycharm操作git仓库 合并等
菜单 Git CommitPushUpdate ProjectPullFetchMergreRebase 查询 查询分支 查询本地所有分支 # 查询本地分支 git branch# 查询远程分支 git branch -rPycharm查看当前分支 步骤: Git->Branches 哈喽,大家好,我是[有勇气的牛排]&…...
Flink+Doris 实时数仓
Flink+Doris 实时数仓 Doris基本原理 Doris基本架构非常简单,只有FE(Frontend)、BE(Backend)两种角色,不依赖任何外部组件,对部署和运维非常友好。架构图如下 可以 看到Doris 的数仓架构十分简洁,不依赖 Hadoop 生态组件,构建及运维成本较低。 FE(Frontend)以 Java 语…...
windows 任务计划自动提交 笔记到github 、gitee
一、必须有个git仓库托管到git上。 这个就不用说了,自己在github或者码云上新建一个仓库就行了。 二、创建自动提交脚本 这个bat脚本是在windows环境下使用的。 注意:windows定时任务下 调用自动提交git前,必须先进入该git仓库目录&#x…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
