”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 如何将 Kubernetes 支持的领导者选举添加到您的 Go 应用程序中

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

发布于2024-07-30
浏览:871

How to add Kubernetes-powered leader election to your Go apps

最初由博客发布

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。

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

$ 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:                    

看到“持有者身份”与进程的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 源码深入研究!

版本声明 本文转载于:https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    您可以使用CSS在Chrome和Firefox中染色控制台输出吗?
    在javascript console 中显示颜色是可以使用chrome的控制台显示彩色文本,例如红色的redors,for for for for错误消息?回答是的,可以使用CSS将颜色添加到Chrome和Firefox中的控制台显示的消息(版本31或更高版本)中。要实现这一目标,请使用以下模...
    编程 发布于2025-06-19
  • 使用jQuery如何有效修改":after"伪元素的CSS属性?
    使用jQuery如何有效修改":after"伪元素的CSS属性?
    在jquery中了解伪元素的限制:访问“ selector 尝试修改“:”选择器的CSS属性时,您可能会遇到困难。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    编程 发布于2025-06-19
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中可能会遇到一个冲突,其中3派对软件包将另一个带有导入套件的path package the Imptioned package the Imptioned package the Imported tocted pac...
    编程 发布于2025-06-19
  • 如何使用Regex在PHP中有效地提取括号内的文本
    如何使用Regex在PHP中有效地提取括号内的文本
    php:在括号内提取文本在处理括号内的文本时,找到最有效的解决方案是必不可少的。一种方法是利用PHP的字符串操作函数,如下所示: 作为替代 $ text ='忽略除此之外的一切(text)'; preg_match('#((。 &&& [Regex使用模式来搜索特...
    编程 发布于2025-06-19
  • 如何在无序集合中为元组实现通用哈希功能?
    如何在无序集合中为元组实现通用哈希功能?
    在未订购的集合中的元素要纠正此问题,一种方法是手动为特定元组类型定义哈希函数,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    编程 发布于2025-06-19
  • 为什么使用Firefox后退按钮时JavaScript执行停止?
    为什么使用Firefox后退按钮时JavaScript执行停止?
    导航历史记录问题:JavaScript使用Firefox Back Back 此行为是由浏览器缓存JavaScript资源引起的。要解决此问题并确保在后续页面访问中执行脚本,Firefox用户应设置一个空功能。 警报'); }; alert('inline Alert')...
    编程 发布于2025-06-19
  • 在细胞编辑后,如何维护自定义的JTable细胞渲染?
    在细胞编辑后,如何维护自定义的JTable细胞渲染?
    在JTable中维护jtable单元格渲染后,在JTable中,在JTable中实现自定义单元格渲染和编辑功能可以增强用户体验。但是,至关重要的是要确保即使在编辑操作后也保留所需的格式。在设置用于格式化“价格”列的“价格”列,用户遇到的数字格式丢失的“价格”列的“价格”之后,问题在设置自定义单元格...
    编程 发布于2025-06-19
  • 图片在Chrome中为何仍有边框?`border: none;`无效解决方案
    图片在Chrome中为何仍有边框?`border: none;`无效解决方案
    在chrome 在使用Chrome and IE9中的图像时遇到的一个频繁的问题是围绕图像的持续薄薄边框,尽管指定了图像,尽管指定了;和“边境:无;”在CSS中。要解决此问题,请考虑以下方法: Chrome具有忽略“ border:none; none;”的已知错误,风格。要解决此问题,请使用以下...
    编程 发布于2025-06-19
  • 您如何在Laravel Blade模板中定义变量?
    您如何在Laravel Blade模板中定义变量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配变量对于存储以后使用的数据至关重要。在使用“ {{}}”分配变量的同时,它可能并不总是最优雅的解决方案。幸运的是,Blade通过@php Directive提供了更优雅的方法: $ old_section =“...
    编程 发布于2025-06-19
  • Android如何向PHP服务器发送POST数据?
    Android如何向PHP服务器发送POST数据?
    在android apache httpclient(已弃用) httpclient httpclient = new defaulthttpclient(); httppost httppost = new httppost(“ http://www.yoursite.com/script.p...
    编程 发布于2025-06-19
  • 如何使用Python有效地以相反顺序读取大型文件?
    如何使用Python有效地以相反顺序读取大型文件?
    在python 中,如果您使用一个大文件,并且需要从最后一行读取其内容,则在第一行到第一行,Python的内置功能可能不合适。这是解决此任务的有效解决方案:反向行读取器生成器 == ord('\ n'): 缓冲区=缓冲区[:-1] ...
    编程 发布于2025-06-19
  • PHP与C++函数重载处理的区别
    PHP与C++函数重载处理的区别
    作为经验丰富的C开发人员脱离谜题,您可能会遇到功能超载的概念。这个概念虽然在C中普遍,但在PHP中构成了独特的挑战。让我们深入研究PHP功能过载的复杂性,并探索其提供的可能性。在PHP中理解php的方法在PHP中,函数超载的概念(如C等语言)不存在。函数签名仅由其名称定义,而与他们的参数列表无关。...
    编程 发布于2025-06-19
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将其...
    编程 发布于2025-06-19
  • Java字符串非空且非null的有效检查方法
    Java字符串非空且非null的有效检查方法
    检查字符串是否不是null而不是空的 if(str!= null && str.isementy())二手: if(str!= null && str.length()== 0) option 3:trim()。isement(Isement() trim whitespace whitesp...
    编程 发布于2025-06-19
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    The Mystery of "Broken" Two-Phase Template Instantiation in Microsoft Visual C Problem Statement:Users commonly express concerns that Micro...
    编程 发布于2025-06-19

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3