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

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

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

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]刪除
最新教學 更多>
  • C++ 函式宣告中「const」的真正意義是什麼?
    C++ 函式宣告中「const」的真正意義是什麼?
    解密返回類型、函數參數和成員函數中的Const 關鍵字C 程式碼片段中:const int* const Method3(const int* const&) const;the術語“const”出現多次,每次都有特定含義。 1.傳回型別中的 Const(指向 Int Const 的...
    程式設計 發佈於2024-12-20
  • 大批
    大批
    方法是可以在物件上呼叫的 fns 數組是對象,因此它們在 JS 中也有方法。 slice(begin):將陣列的一部分提取到新數組中,而不改變原始數組。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index ...
    程式設計 發佈於2024-12-20
  • 儘管程式碼有效,為什麼 POST 請求無法擷取 PHP 中的輸入?
    儘管程式碼有效,為什麼 POST 請求無法擷取 PHP 中的輸入?
    解決PHP 中的POST 請求故障在提供的程式碼片段:action=''而非:action="<?php echo $_SERVER['PHP_SELF'];?>";?>"檢查$_POST陣列:表單提交後使用 var_dump 檢查 $_POST 陣列的內容...
    程式設計 發佈於2024-12-20
  • 如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    在 PHP 中組合關聯數組在 PHP 中,將兩個關聯數組組合成一個數組是常見任務。考慮以下請求:問題描述:提供的代碼定義了兩個關聯數組,$array1和$array2。目標是建立一個新陣列 $array3,它合併兩個陣列中的所有鍵值對。 此外,提供的陣列具有唯一的 ID,而名稱可能重疊。要求是建構一...
    程式設計 發佈於2024-12-20
  • 插入資料時如何修復「常規錯誤:2006 MySQL 伺服器已消失」?
    插入資料時如何修復「常規錯誤:2006 MySQL 伺服器已消失」?
    插入記錄時如何解決「一般錯誤:2006 MySQL 伺服器已消失」介紹:將資料插入MySQL 資料庫有時會導致錯誤「一般錯誤:2006 MySQL 伺服器已消失」。當與伺服器的連線遺失時會出現此錯誤,通常是由於 MySQL 配置中的兩個變數之一所致。 解決方案:解決此錯誤的關鍵是調整wait_tim...
    程式設計 發佈於2024-12-20
  • 在 Go 中使用 WebSocket 進行即時通信
    在 Go 中使用 WebSocket 進行即時通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    程式設計 發佈於2024-12-20
  • Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta 中的列偏移發生了什麼事?
    Bootstrap 4 Beta:列偏移的刪除和恢復Bootstrap 4 在其Beta 1 版本中引入了重大更改柱子偏移了。然而,隨著 Beta 2 的後續發布,這些變化已經逆轉。 從 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    程式設計 發佈於2024-12-20
  • 使用“list.List”是建立帶有字串鍵和列表值的 Go 映射的最佳方法嗎?
    使用“list.List”是建立帶有字串鍵和列表值的 Go 映射的最佳方法嗎?
    建立字串到清單的對應問題:您想要建立一個有字串型別鍵的映射和列表類型的值。以下程式碼片段是否是正確的方法:package main import ( "fmt" "container/list" ) func main() { x :=...
    程式設計 發佈於2024-12-19
  • 使用 html css 和 javascript 幻覺的 Tic-Tac-Toe 遊戲 https://www.instagram.com/webstreet_code/
    使用 html css 和 javascript 幻覺的 Tic-Tac-Toe 遊戲 https://www.instagram.com/webstreet_code/
    在 Instagram 上關注我們:https://www.instagram.com/webstreet_code/ ? ✨ 有玻璃效果的井字遊戲! ✨? 我剛剛使用 HTML、CSS 和 JavaScript 構建了一款經典的 Tic-Tac-Toe 遊戲,具有時尚的玻璃態設計。觀看視頻,看看...
    程式設計 發佈於2024-12-19
  • TB 級資料庫的 MySQL 與 NoSQL:聚集索引何時是正確的解決方案?
    TB 級資料庫的 MySQL 與 NoSQL:聚集索引何時是正確的解決方案?
    MySQL:探索資料庫設計迷宮優化大型資料庫時,必須考慮資料庫設計策略以提高效能。在給定的場景中,包含執行緒的 TB 級資料庫由於其龐大的規模而面臨效能挑戰。本文探討了 MySQL 和 NoSQL 之間的選擇,重點介紹了 MySQL 的 innodb 引擎及其聚集索引的優點。 了解 MySQL 的 ...
    程式設計 發佈於2024-12-19
  • 為什麼我的 Spring Boot 應用程式不自動建立資料庫架構?
    為什麼我的 Spring Boot 應用程式不自動建立資料庫架構?
    在 Spring Boot 中自動建立資料庫架構啟動 Spring Boot 應用程式時,可能會遇到自動建立資料庫架構的問題。以下故障排除步驟旨在解決此問題:1.實體類別包:確保實體類別位於使用@EnableAutoConfiguration註解的類別的同一個套件或子包中。否則,Spring 將不會...
    程式設計 發佈於2024-12-18
  • CSS3 轉場是否提供事件來偵測起點和終點?
    CSS3 轉場是否提供事件來偵測起點和終點?
    了解 CSS3 過渡事件CSS3 過渡允許在 Web 元素上實現流暢的動畫和視覺效果。為了增強使用者體驗並使操作與這些轉換同步,監控其進度非常重要。本文解決了 CSS3 是否提供事件來檢查過渡何時開始或結束的問題。 W3C CSS 過渡草案W3C CSS 過渡草案規定CSS 轉換會觸發對應的 DOM...
    程式設計 發佈於2024-12-18
  • Java 中可以手動釋放記憶體嗎?
    Java 中可以手動釋放記憶體嗎?
    Java 中的手動內存釋放與垃圾回收與C 不同,Java 採用託管內存框架來處理內存分配和釋放由垃圾收集器(GC) 自動執行。這種自動化方法可以提高記憶體利用率並防止困擾 C 程式的記憶體洩漏。 Java 中可以手動釋放記憶體嗎? 由於 Java 的記憶體管理是由GC,它沒有提供像 C 中的 fre...
    程式設計 發佈於2024-12-18
  • Java 1.6 中如何可靠地確定檔案是否為符號連結?
    Java 1.6 中如何可靠地確定檔案是否為符號連結?
    在 Java 1.6 中驗證符號連結確定符號連結的存在對於各種文件處理操作至關重要。在 Java 中,識別符號連結時需要考慮一些潛在問題,特別是在目錄遍歷的上下文中。 檢查符號連結的常見方法是比較文件的絕對路徑和規範路徑。規範路徑表示檔案的標準化路徑,而絕對路徑可能包括符號連結。傳統上,概念是如果這...
    程式設計 發佈於2024-12-17
  • 如何使背景顏色透明,同時保持文字不透明?
    如何使背景顏色透明,同時保持文字不透明?
    背景顏色的不透明度而不影響文本在Web 開發領域,實現透明度通常對於增強視覺吸引力和網站元素的功能。常見的要求是對 div 背景套用透明度,同時保留所包含文字的不透明度。這可能會帶來挑戰,特別是在確保跨瀏覽器相容性方面。 rgba 解決方案最有效且廣泛支持的解決方案是利用「RGBA」(紅、綠、藍、A...
    程式設計 發佈於2024-12-17

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

Copyright© 2022 湘ICP备2022001581号-3