0

0

如何将 Kubernetes 支持的领导者选举添加到您的 Go 应用程序中

PHPz

PHPz

发布时间:2024-07-20 09:20:19

|

1062人浏览过

|

来源于dev.to

转载

如何将 kubernetes 支持的领导者选举添加到您的 go 应用程序中

最初由博客发布

kubernetes 标准库充满了宝石,隐藏在生态系统中的许多不同的子包中。我最近发现了一个这样的例子 k8s.io/client-go/tools/leaderelection,它可用于向 kubernetes 集群内运行的任何应用程序添加领导者选举协议。本文将讨论什么是领导者选举,它是如何在这个 kubernetes 包中实现的,并提供一个示例来说明如何在我们自己的应用程序中使用这个库。

领导人选举

领导者选举是一个分布式系统概念,是高可用软件的核心构建块。它允许多个并发进程相互协调并选举一个“领导者”进程,然后该进程负责执行同步操作,例如写入数据存储。

这在分布式数据库或缓存等系统中非常有用,其中多个进程正在运行以针对硬件或网络故障创建冗余,但无法同时写入存储以确保数据一致性。如果领导者进程在未来某个时刻变得无响应,剩余进程将启动新的领导者选举,最终选择一个新进程作为领导者。

利用这个概念,我们可以创建具有单个领导者和多个备用副本的高可用软件。

在 kubernetes 中,controller-runtime 包使用领导者选举来使控制器具有高可用性。在控制器部署中,仅当进程是领导者并且其他副本处于等待状态时才会发生资源协调。如果leader pod没有响应,剩余的副本将选举一个新的leader来执行后续的协调并恢复正常运行。

kubernetes 租赁

这个库使用 kubernetes lease,或者分布式锁,可以由进程获取。租约是由单一身份在给定期限内持有的原生 kubernetes 资源,并具有续订选项。 这是文档中的示例规范:

apiversion: coordination.k8s.io/v1
kind: lease
metadata:
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
spec:
  holderidentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leasedurationseconds: 3600
  renewtime: "2023-07-04t21:58:48.065888z"

k8s 生态系统通过三种方式使用租约:

  1. 节点心跳:每个节点都有对应的lease资源,并不断更新其renewtime字段。如果 lease 的 renewtime 一段时间没有更新,该 node 将被污染为不可用,并且不会再为其调度 pod。
  2. leader election:在这种情况下,lease 用于通过让 leader 更新 lease 的holderidentity 来协调多个进程。具有不同身份的备用副本陷入等待租约到期的状态。如果租约确实到期,并且领导者没有续订,则会进行新的选举,其中剩余的副本尝试通过用自己的持有人身份更新其持有者身份来获得租约的所有权。由于 kubernetes api 服务器不允许更新过时的对象,因此只有一个备用节点能够成功更新租约,此时它将作为新的领导者继续执行。
  3. api 服务器身份:从 v1.26 开始,作为测试功能,每个 kube-apiserver 副本将通过创建专用租约来发布其身份。由于这是一个相对较小的新功能,因此除了运行的 api 服务器数量之外,无法从 lease 对象派生出太多其他内容。但这确实为未来的 k8s 版本中的这些 lease 添加更多元数据留下了空间。

现在让我们通过编写示例程序来探索租赁的第二个用例,以演示如何在领导者选举场景中使用它们。

示例程序

在此代码示例中,我们使用 leaderelection 包来处理领导者选举和租约操作细节。

package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "k8s.io/client-go/tools/leaderelection"
    rl "k8s.io/client-go/tools/leaderelection/resourcelock"
    ctrl "sigs.k8s.io/controller-runtime"
)

var (
    // lockname and locknamespace need to be shared across all running instances
    lockname      = "my-lock"
    locknamespace = "default"

    // identity is unique to the individual process. this will not work for anything,
    // outside of a toy example, since processes running in different containers or
    // computers can share the same pid.
    identity      = fmt.sprintf("%d", os.getpid())
)

func main() {
    // get the active kubernetes context
    cfg, err := ctrl.getconfig()
    if err != nil {
        panic(err.error())
    }

    // create a new lock. this will be used to create a lease resource in the cluster.
    l, err := rl.newfromkubeconfig(
        rl.leasesresourcelock,
        locknamespace,
        lockname,
        rl.resourcelockconfig{
            identity: identity,
        },
        cfg,
        time.second*10,
    )
    if err != nil {
        panic(err)
    }

    // create a new leader election configuration with a 15 second lease duration.
    // visit https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#leaderelectionconfig
    // for more information on the leaderelectionconfig struct fields
    el, err := leaderelection.newleaderelector(leaderelection.leaderelectionconfig{
        lock:          l,
        leaseduration: time.second * 15,
        renewdeadline: time.second * 10,
        retryperiod:   time.second * 2,
        name:          lockname,
        callbacks: leaderelection.leadercallbacks{
            onstartedleading: func(ctx context.context) { println("i am the leader!") },
            onstoppedleading: func() { println("i am not the leader anymore!") },
            onnewleader:      func(identity string) { fmt.printf("the leader is %s\n", identity) },
        },
    })
    if err != nil {
        panic(err)
    }

    // begin the leader election process. this will block.
    el.run(context.background())

}

leaderelection 包的优点在于它提供了一个基于回调的框架来处理领导者选举。这样,您可以以精细的方式对特定的状态变化采取行动,并在选举新领导者时适当地释放资源。通过在单独的 goroutine 中运行这些回调,该包利用 go 强大的并发支持来有效地利用机器资源。

测试一下

为了测试这一点,让我们使用 kind 启动一个测试集群。

$ kind create cluster

将示例代码复制到 main.go 中,创建一个新模块(go mod init leaderelectiontest)并整理它(go mod tidy)以安装其依赖项。运行 go run main.go 后,您应该看到如下输出:

$ go run main.go
i0716 11:43:50.337947     138 leaderelection.go:250] attempting to acquire leader lease default/my-lock...
i0716 11:43:50.351264     138 leaderelection.go:260] successfully acquired lease default/my-lock
the leader is 138
i am the leader!

确切的领导者身份将与示例(138)中的不同,因为这只是撰写本文时在我的计算机上运行的进程的 pid。

知了zKnown
知了zKnown

知了zKnown:致力于信息降噪 / 阅读提效的个人知识助手。

下载

这是在测试集群中创建的租约:

$ kubectl describe lease/my-lock
name:         my-lock
namespace:    default
labels:       
annotations:  
api version:  coordination.k8s.io/v1
kind:         lease
metadata:
  creation timestamp:  2024-07-16t15:43:50z
  resource version:    613
  uid:                 1d978362-69c5-43e9-af13-7b319dd452a6
spec:
  acquire time:            2024-07-16t15:43:50.338049z
  holder identity:         138
  lease duration seconds:  15
  lease transitions:       0
  renew time:              2024-07-16t15:45:31.122956z
events:                    

看到“holder identity”与进程的pid相同,138。

现在,让我们打开另一个终端并在单独的进程中运行相同的 main.go 文件:

$ go run main.go
i0716 11:48:34.489953     604 leaderelection.go:250] attempting to acquire leader lease default/my-lock...
the leader is 138

第二个进程将永远等待,直到第一个进程没有响应。让我们终止第一个进程并等待大约 15 秒。现在,第一个进程不再更新其对租约的声明,因此 .spec.renewtime 字段将不再更新。这最终将导致第二个进程触发新的领导者选举,因为租约的更新时间早于其持续时间。因为这个进程是唯一正在运行的进程,所以它将选举自己作为新的领导者。

the leader is 604
I0716 11:48:51.904732     604 leaderelection.go:260] successfully acquired lease default/my-lock
I am the leader!

如果初始 leader 退出后还有多个进程仍在运行,则第一个获得 lease 的进程将成为新的 leader,其余进程继续处于待命状态。

没有单一领导者的保证

这个包并不是万无一失的,因为它“不能保证只有一个客户端充当领导者(又名击剑)”。例如,如果领导者暂停并让其租约到期,则另一个备用副本将获取租约。然后,一旦原来的领导者恢复执行,它就会认为自己仍然是领导者,并继续与新当选的领导者一起工作。这样,你最终可以有两个领导者同时运行。

要解决此问题,需要在向服务器发出的每个请求中包含引用租约的隔离令牌。隔离令牌实际上是一个整数,每次租约易手时该整数就会增加 1。因此,具有旧防护令牌的客户端的请求将被服务器拒绝。在这种情况下,如果旧领导者从睡眠中醒来,并且新领导者已经增加了防护令牌,则旧领导者的所有请求都将被拒绝,因为它发送的令牌比服务器从服务器看到的令牌更旧(更小)。新领导者。

如果不修改核心 api 服务器来考虑每个 lease 的相应 fencing 令牌,那么在 kubernetes 中实现 fencing 将会很困难。然而,k8s api 服务器本身在一定程度上减轻了拥有多个领导者控制器的风险。由于对过时对象的更新会被拒绝,因此只有拥有最新版本对象的控制器才能修改它。因此,虽然我们可以运行多个控制器领导者,但如果一个控制器错过了另一个领导者所做的更改,资源的状态将永远不会回归到旧版本。相反,协调时间将会增加,因为两位领导者都需要刷新自己的内部资源状态,以确保他们正在执行最新版本。

不过,如果您使用此包使用不同的数据存储来实现领导者选举,这是一个需要注意的重要警告。

结论

领导者选举和分布式锁定是分布式系统的关键构建块。当尝试构建容错和高可用性的应用程序时,拥有此类工具至关重要。 kubernetes 标准库为我们提供了一个经过实战检验的原语包装器,允许应用程序开发人员轻松地将领导者选举构建到他们自己的应用程序中。

虽然这个特定库的使用确实限制了您在 kubernetes 上部署应用程序,但这似乎是最近世界的发展方向。如果事实上这是一个破坏者,您当然可以分叉该库并修改它以适用于任何符合 acid 且高度可用的数据存储。

请继续关注更多 k8s 源码深入研究!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

329

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

235

2023.10.07

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

358

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2082

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

349

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

256

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

326

2023.10.09

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

412

2023.10.16

Golang 网络安全与加密实战
Golang 网络安全与加密实战

本专题系统讲解 Golang 在网络安全与加密技术中的应用,包括对称加密与非对称加密(AES、RSA)、哈希与数字签名、JWT身份认证、SSL/TLS 安全通信、常见网络攻击防范(如SQL注入、XSS、CSRF)及其防护措施。通过实战案例,帮助学习者掌握 如何使用 Go 语言保障网络通信的安全性,保护用户数据与隐私。

0

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL 教程
SQL 教程

共61课时 | 3.6万人学习

PHP面向对象基础课程(更新中)
PHP面向对象基础课程(更新中)

共12课时 | 0.7万人学习

FastAPI框架精讲课程
FastAPI框架精讲课程

共38课时 | 2.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号