」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 如何將 Kubernetes 支援的領導者選舉添加到您的 Go 應用程式中

如何將 Kubernetes 支援的領導者選舉添加到您的 Go 應用程式中

發佈於2024-07-30
瀏覽:971

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]刪除
最新教學 更多>
  • FCS API 與 Insight Ease:比特幣 API 服務的簡單比較
    FCS API 與 Insight Ease:比特幣 API 服務的簡單比較
    如果您熱衷於比特幣 API,那麼選擇正確的 API 非常重要。特別是如果您是開發人員、金融分析師或經營金融科技公司。您會聽到的兩個流行名稱是 FCS API 和 Insight Ease。但哪一個比較好呢?讓我們仔細觀察它們的比較,特別是當涉及加密貨幣即時匯率 API、加密貨幣 API 交易,甚至...
    程式設計 發佈於2024-11-02
  • 如何在不修改HTML的情況下用JavaScript監聽表單提交事件?
    如何在不修改HTML的情況下用JavaScript監聽表單提交事件?
    在JavaScript 中監聽表單提交事件而不修改HTML在本文中,我們解決了在不修改HTML 的情況下監聽表單提交事件的常見挑戰必須修改HTML 程式碼。我們不依賴 HTML 中的 onClick 或 onSubmit 屬性,而是提供純 JavaScript 解決方案。 為了實現這一點,我們利用 ...
    程式設計 發佈於2024-11-02
  • Document.getElementById 與 jQuery $():主要差異是什麼?
    Document.getElementById 與 jQuery $():主要差異是什麼?
    Document.getElementById vs jQuery $():比較分析深入研究Web 開發領域時,了解一般版本之間的細微差別JavaScript 和jQuery 可能至關重要。本文研究了兩個看似相同的程式碼片段之間的細微差別:var contents = document.getEle...
    程式設計 發佈於2024-11-02
  • 在 Java 中使用方法和變數句柄進行運行時物件存取和操作
    在 Java 中使用方法和變數句柄進行運行時物件存取和操作
    反射和方法/var 句柄是 Java 中的兩個強大功能,允許開發人員在運行時存取和操作物件。然而,它們在存取和處理物件的方式上有所不同。 讓我們來看一個如何使用反射來存取類別中方法的範例。我們將從一個名為「MyClass」的簡單類別開始,它有一個私有字串變數和該變數的 getter 方法。為了創建...
    程式設計 發佈於2024-11-02
  • 如何在 Python 中使用內建函數驗證 IP 位址?
    如何在 Python 中使用內建函數驗證 IP 位址?
    Python 中的 IP 位址驗證驗證 IP 位址的有效性是程式設計中的常見任務。從使用者接收字串形式的 IP 位址時,必須對其進行驗證,以確保它們符合正確的格式和結構。 要在 Python 中有效驗證 IP 位址,請考慮以下方法:無需手動解析 IP 位址,而是利用套接字模組中的內建 inet_at...
    程式設計 發佈於2024-11-02
  • 我需要學習程式設計的幫助
    我需要學習程式設計的幫助
    您好,我是系統工程專業的學生,我覺得我在課程中學到的程式設計知識不多。我想自學,因為我對這個主題非常感興趣。這就是我在這個網站上向了解程式設計的人尋求幫助的原因。如果有人知道學習程式設計的最佳課程,從基礎開始並進步到更專業的水平,那將會有很大的幫助。 我感興趣的語言: Java JavaScrip...
    程式設計 發佈於2024-11-02
  • 如何將 gorm.Model 整合到具有日期時間支援的 Protocol Buffer 定義中?
    如何將 gorm.Model 整合到具有日期時間支援的 Protocol Buffer 定義中?
    將gorm.Model 整合到Protocol Buffer 定義中將gorm.Model 整合到Protocol Buffer 定義中將gorm 的gorm.Model 欄位整合到protobuf 定義時,由於proto3 中缺乏日期時間支持,出現了挑戰。本文探討了此問題的解決方案。 ProtoB...
    程式設計 發佈於2024-11-02
  • 修補您的 Discord 活動的網路請求,以實現順利的 CSP 合規性
    修補您的 Discord 活動的網路請求,以實現順利的 CSP 合規性
    透過Discord運行Discord活動時,您可能會遇到內容安全策略(CSP)問題。您可以透過確保網路請求遵循 Discord 代理 規則來修復這些問題。 這可以手動完成...或者你可以讓@robojs/patch處理它。 什麼是CSP? 內容安全策略 (CSP) 是一種安全標準...
    程式設計 發佈於2024-11-02
  • 推薦項目:刪除課程表查看數據
    推薦項目:刪除課程表查看數據
    LabEx 的這個專案釋放了資料庫管理的力量,提供了在資料庫中建立和操作視圖的全面學習體驗。無論您是嶄露頭角的資料庫管理員還是經驗豐富的開發人員,該專案都提供了寶貴的機會來增強您的技能並獲得對資料管理世界的實際見解。 深入了解基礎知識 在這個專案中,您將踏上了解資料庫中視圖的核心概...
    程式設計 發佈於2024-11-02
  • 模擬網路請求變得容易:整合 Jest 和 MSW
    模擬網路請求變得容易:整合 Jest 和 MSW
    Writing unit tests that involve mocking or stubbing API calls can feel overwhelming—I’ve been there myself. In this article, I’ll guide you through a ...
    程式設計 發佈於2024-11-02
  • 使用 Javascript 的哈希映射
    使用 Javascript 的哈希映射
    介紹 哈希映射(Hash Map),也稱為哈希表(Hash Table),是實現關聯數組抽象資料類型的資料結構,是可以將鍵映射到值的結構。 它使用雜湊函數來計算儲存桶或槽數組的索引,從中可以找到所需的值。 哈希映射的主要優點是它的效率。插入新的鍵值對、刪除鍵值對以及查找給定鍵...
    程式設計 發佈於2024-11-02
  • HTPX 簡介:適用於 JavaScript 和 Node.js 的輕量級多功能 HTTP 用戶端
    HTPX 簡介:適用於 JavaScript 和 Node.js 的輕量級多功能 HTTP 用戶端
    作為開發人員,我們的Web 應用程式通常需要一個可靠且高效的HTTP 用戶端,無論我們是在瀏覽器中使用JavaScript 還是在伺服器端使用Node.js 進行構建。這就是我創建 HTPX 的原因——一個強大的、輕量級的解決方案,旨在簡化 HTTP 請求,同時為現代開發提供一系列功能。 在本文中...
    程式設計 發佈於2024-11-02
  • 使用自然語言透過法學碩士產生簡單的 Python GUI .... 在不到幾分鐘的時間內
    使用自然語言透過法學碩士產生簡單的 Python GUI .... 在不到幾分鐘的時間內
    Thought that building Python GUIs took hours of tedious coding? Welcome to an exciting new era! Not only can tools like Github Copilot help with code ...
    程式設計 發佈於2024-11-02
  • Dev、Oops 和 WEBAPP 故事
    Dev、Oops 和 WEBAPP 故事
    作為 DevOps 專業人員開發桌面 Web 應用程式感覺就像在廣闊而複雜的海洋中航行。隨著技術融合,Web、桌面和基於雲端的應用程式之間的界限變得模糊,迫使 DevOps 深入傳統上由前端佔據的領域 終端開發商。選擇正確的框架變得至關重要,但挑戰往往在於篩選當今可用的眾多選項。例如,Vite、Re...
    程式設計 發佈於2024-11-02
  • 釋放您的 Django 潛力:適合 4 人的專案創意和資源
    釋放您的 Django 潛力:適合 4 人的專案創意和資源
    Django 電子報 - 2024 年 10 月 Django 簡介與專案想法 如果您希望開始使用 Django 或提高自己的技能,請考慮以下一些寶貴的資源和專案想法: Django 專案想法 對於想要嘗試或建立自己的作品集的人來說,Django 提...
    程式設計 發佈於2024-11-02

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3