「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > Kubernetes を利用したリーダー選挙を Go アプリに追加する方法

Kubernetes を利用したリーダー選挙を Go アプリに追加する方法

2024 年 7 月 30 日に公開
ブラウズ:668

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

元々はブログで公開されました

Kubernetes 標準ライブラリには、エコシステムの一部であるさまざまなサブパッケージの多くに隠された宝石がたくさんあります。そのような例の 1 つは、私が最近発見した k8s.io/client-go/tools/leaderelection です。これは、Kubernetes クラスター内で実行されているアプリケーションにリーダー選出プロトコルを追加するために使用できます。この記事では、リーダー選挙とは何か、それがこの Kubernetes パッケージでどのように実装されるかについて説明し、独自のアプリケーションでこのライブラリを使用する方法の例を示します。

リーダー選挙

リーダー選挙は、高可用性ソフトウェアの中核となる構成要素である分散システムの概念です。これにより、複数の同時プロセスが相互に調整し、単一の「リーダー」プロセスを選択できるようになり、そのプロセスがデータ ストアへの書き込みなどの同期アクションの実行を担当します。

これは、ハードウェアやネットワーク障害に対する冗長性を確保するために複数のプロセスが実行されているが、データの一貫性を確保するためにストレージに同時に書き込むことができない分散データベースやキャッシュなどのシステムで役立ちます。将来のある時点でリーダー プロセスが応答しなくなった場合、残りのプロセスによって新しいリーダーの選挙が開始され、最終的にリーダーとして機能する新しいプロセスが選択されます。

この概念を使用すると、単一のリーダーと複数のスタンバイ レプリカを備えた高可用性ソフトウェアを作成できます。

Kubernetes では、コントローラー ランタイム パッケージはリーダー選挙を使用してコントローラーの高可用性を実現します。コントローラーのデプロイメントでは、リソースの調整は、プロセスがリーダーであり、他のレプリカがスタンバイで待機している場合にのみ発生します。リーダー ポッドが応答しなくなった場合、残りのレプリカは新しいリーダーを選択して後続の調整を実行し、通常の操作を再開します。

Kubernetes リース

このライブラリは、プロセスによって取得できる Kubernetes リース (分散ロック) を使用します。リースは、単一の ID によって一定期間保持されるネイティブ 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 エコシステムによって 3 つの方法で使用されます:

  1. ノード ハートビート: すべてのノードには対応するリース リソースがあり、その renewTime フィールドを継続的に更新します。リースの renewTime がしばらく更新されなかった場合、ノードは利用不可として汚染され、それ以上のポッドはスケジュールされなくなります。
  2. リーダー選挙: この場合、リースは、リーダーにリースのholderIdentityを更新させることで、複数のプロセス間を調整するために使用されます。異なる ID を持つスタンバイ レプリカは、リースの期限が切れるのを待ったままになります。リースの有効期限が切れ、リーダーによって更新されない場合は、新しい選挙が行われ、残りのレプリカが、そのholderIdentity を自分のもので更新することによって、リースの所有権を取得しようとします。 Kubernetes API サーバーは古いオブジェクトの更新を禁止しているため、リースを正常に更新できるのは 1 つのスタンバイ ノードだけであり、その時点で新しいリーダーとして実行を継続します。
  3. API サーバー ID: v1.26 以降、ベータ機能として、各 kube-apiserver レプリカは専用のリースを作成することでその ID を公開します。これは比較的スリムな新しい機能であるため、実行されている API サーバーの数以外に Lease オブジェクトから得られるものはあまりありません。ただし、これにより、将来の k8s バージョンでこれらのリースにさらにメタデータを追加する余地が残ります。

次に、リーダー選出シナリオでリースを使用する方法を示すサンプル プログラムを作成して、リースの 2 番目の使用例を調べてみましょう。

サンプルプログラム

このコード例では、リーダー選挙とリース操作の詳細を処理するためにリーダー選挙パッケージを使用しています。

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 tiny) 依存関係をインストールします。 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!

これは、この記事を書いている時点で私のコンピュータ上で実行されていたプロセスの PID にすぎないため、正確なリーダー ID は例 (138) のものとは異なります。

テスト クラスターで作成されたリースは次のとおりです:

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

「ホルダー ID」がプロセスの 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

この 2 番目のプロセスは、最初のプロセスが応答しなくなるまで永遠に待機します。最初のプロセスを強制終了して、15 秒ほど待ちます。最初のプロセスがリースの請求を更新していないため、.spec.renewTime フィールドは更新されなくなります。リースの更新時期がその期間よりも古いため、最終的には 2 番目のプロセスで新しいリーダーの選挙がトリガーされます。このプロセスは現在実行されている唯一のプロセスであるため、それ自体が新しいリーダーとして選出されます。

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

最初のリーダーが終了した後も複数のプロセスが実行中の場合、リースを取得する最初のプロセスが新しいリーダーとなり、残りは引き続きスタンバイ状態になります。

単一リーダーの保証はない

このパッケージは、「1 つのクライアントだけがリーダー (別名フェンシング) として機能することを保証しない」という点で、絶対確実というわけではありません。たとえば、リーダーが一時停止され、そのリースが期限切れになると、別のスタンバイ レプリカがリースを取得します。その後、元のリーダーが実行を再開すると、自分がまだリーダーであると考え、新しく選出されたリーダーと一緒に作業を続行します。このようにして、最終的に 2 つのリーダーを同時に実行することができます。

これを修正するには、サーバーへの各リクエストにリースを参照するフェンシング トークンを含める必要があります。フェンシング トークンは事実上、リースの所有者が変わるたびに 1 ずつ増加する整数です。したがって、古いフェンシング トークンを持つクライアントは、そのリクエストをサーバーによって拒否されます。このシナリオでは、古いリーダーがスリープから目覚め、新しいリーダーがすでにフェンシング トークンをインクリメントしている場合、古いリーダーのリクエストはすべて拒否されます。これは、サーバーがサーバーから認識したものよりも古い (小さい) トークンを送信しているためです。新しいリーダー。

各リースに対応するフェンシング トークンを考慮してコア API サーバーを変更しない限り、Kubernetes にフェンシングを実装することは困難です。ただし、複数のリーダー コントローラーを持つリスクは、k8s API サーバー自体によってある程度軽減されます。古いオブジェクトへの更新は拒否されるため、オブジェクトの最新バージョンを持つコントローラーのみがそのオブジェクトを変更できます。したがって、複数のコントローラー リーダーを実行できますが、コントローラーが別のリーダーによる変更を見逃しても、リソースの状態が古いバージョンに戻ることはありません。代わりに、両方のリーダーが最新バージョンで動作していることを確認するためにリソースの内部状態を更新する必要があるため、調整時間が増加します。

ただし、このパッケージを使用して、別のデータ ストアを使用してリーダー選出を実装している場合、これは注意すべき重要な注意事項です。

結論

リーダーの選出と分散ロックは、分散システムの重要な構成要素です。フォールトトレラントで可用性の高いアプリケーションを構築しようとする場合、このようなツールを自由に使えることが重要です。 Kubernetes 標準ライブラリは、そのプリミティブを中心に実戦テストされたラッパーを提供し、アプリケーション開発者が独自のアプリケーションにリーダー選挙を簡単に組み込めるようにします。

この特定のライブラリを使用すると、アプリケーションを Kubernetes 上にデプロイできるように制限されますが、最近は世界がそのようになっているようです。実際にそれが問題である場合は、もちろん、ライブラリをフォークして、ACID 準拠の高可用性データストアに対して動作するように変更することができます。

今後も k8s ソースの詳細をご覧ください!

リリースステートメント この記事は次の場所に転載されています: https://dev.to/sklarsa/how-to-add-kubernetes-powered-leader-election-to-your-go-apps-57jh?1 侵害がある場合は、study_golang にご連絡ください。 @163.com 削除
最新のチュートリアル もっと>
  • もうプレプテンバーですか?
    もうプレプテンバーですか?
    皆さんおはようございます、MonDEV おめでとうございます ☕ 私たちは本格的に戻ってきており、9 月に PC に戻るのが良い理由の 1 つはプレプテンバーです! 初期の頃からニュースレターをフォローしている人や、オープンソースの世界をフォローしている人はすでに知っていると思いますが、10 月には...
    プログラミング 2024 年 11 月 2 日に公開
  • 間違って配置された return ステートメントが for ループ内でプログラムの早期終了を引き起こすのはなぜですか?
    間違って配置された return ステートメントが for ループ内でプログラムの早期終了を引き起こすのはなぜですか?
    for ループの Return ステートメントが間違っているこのプログラミングの問題では、ユーザーは 3 匹の動物に対するユーザー入力を可能にするプログラムを作成する際に困難に遭遇します。 。このプログラムは、名前、動物の種類、年齢を含むペット オブジェクトをリストに追加するように設計されています。...
    プログラミング 2024 年 11 月 2 日に公開
  • 項目 他の型の方が適している場合は文字列を避ける
    項目 他の型の方が適している場合は文字列を避ける
    1.他のデータ型の代わりに文字列を使用することは避けてください: 文字列はテキストを表すように設計されていますが、数値、列挙型、または集合構造を表すために誤用されることがよくあります。 データが本質的に数値である場合は、String. ではなく、int、float、BigInteger などの型を...
    プログラミング 2024 年 11 月 2 日に公開
  • sync.WaitGroup を使用して Go 同時実行でデッドロックを防ぐ方法
    sync.WaitGroup を使用して Go 同時実行でデッドロックを防ぐ方法
    ゴルーチンのデッドロックの解決このシナリオでは、Go 同時実行コードでデッドロック エラーが発生しました。問題を詳しく調べて、効率的な解決策を提供しましょう。このエラーは、プロデューサとコンシューマの動作の不一致が原因で発生します。プロデューサー関数に実装されたプロデューサーは、限られた期間、チャネ...
    プログラミング 2024 年 11 月 2 日に公開
  • テキスト ファイル内の Unicode テキストを処理する方法: エラーのない書き込みのための完全ガイド
    テキスト ファイル内の Unicode テキストを処理する方法: エラーのない書き込みのための完全ガイド
    テキスト ファイル内の Unicode テキスト: エラーのない記述のための包括的なガイドGoogle ドキュメントから抽出されたデータのコーディングは、特に困難な場合があります。 HTML で使用するために変換する必要がある非 ASCII シンボルが見つかった場合。このガイドでは、Unicode ...
    プログラミング 2024 年 11 月 2 日に公開
  • EchoAPI と不眠症: 実践例による包括的な比較
    EchoAPI と不眠症: 実践例による包括的な比較
    フルスタック開発者として、私は API のデバッグ、テスト、文書化のための一流のツールを用意することがいかに重要であるかを知っています。 EchoAPI と Insomnia は 2 つの傑出したオプションであり、それぞれに独自の特徴と機能があります。これらのツールについて説明し、その機能と利点を比...
    プログラミング 2024 年 11 月 2 日に公開
  • 移動時間と所要時間 |プログラミングチュートリアル
    移動時間と所要時間 |プログラミングチュートリアル
    導入 このラボは、Go の時間と期間のサポートについての理解をテストすることを目的としています。 時間 以下のコードには、Go で時間と期間を操作する方法の例が含まれています。ただし、コードの一部が欠落しています。あなたの仕事は、コードを完成させて期待通りに動作させ...
    プログラミング 2024 年 11 月 2 日に公開
  • ホイスティングにおける面接の質問と回答
    ホイスティングにおける面接の質問と回答
    1. JavaScript におけるホイスティングとは何ですか? 答え: ホイスティングは、変数や関数にメモリが割り当てられる実行コンテキストの作成フェーズ中のプロセスです。このプロセス中に、変数用のメモリが割り当てられ、変数には未定義の値が割り当てられます。関数の場合、関数定義全...
    プログラミング 2024 年 11 月 2 日に公開
  • JavaScript のドキュメント オブジェクト モデル (DOM) を理解する
    JavaScript のドキュメント オブジェクト モデル (DOM) を理解する
    こんにちは、素晴らしい JavaScript 開発者の皆さん? ブラウザは、スクリプト (特に JavaScript) が Web ページのレイアウトと対話できるようにするドキュメント オブジェクト モデル (DOM) と呼ばれるプログラミング インターフェイスを提供します。 We...
    プログラミング 2024 年 11 月 2 日に公開
  • SPRING BATCH でプログラミングを始める
    SPRING BATCH でプログラミングを始める
    Introduction Dans vos projets personnels ou professionnels, Il vous arrive de faire des traitements sur de gros volumes de données. Le traite...
    プログラミング 2024 年 11 月 2 日に公開
  • CSS で Github プロフィールを目立たせる
    CSS で Github プロフィールを目立たせる
    これまで、Github プロフィールをカスタマイズできる唯一の方法は、写真を更新するか名前を変更することでした。これは、すべての Github プロファイルが同じに見え、カスタマイズしたり目立たせるためのオプションが最小限であることを意味しました。 それ以来、Markdown を使用してカスタム セ...
    プログラミング 2024 年 11 月 2 日に公開
  • TypeScript ユーティリティの種類: コードの再利用性の向上
    TypeScript ユーティリティの種類: コードの再利用性の向上
    TypeScript は、開発者が型を効果的に変換および再利用できるようにする組み込みのユーティリティ型を提供し、コードをより柔軟で ​​DRY にします。この記事では、TypeScript スキルを次のレベルに引き上げるのに役立つ、Partial、Pick、Omit、Record などの主要なユー...
    プログラミング 2024 年 11 月 2 日に公開
  • 電報 window.open(url, &#_blank&#); iOSでは動作がおかしい
    電報 window.open(url, &#_blank&#); iOSでは動作がおかしい
    電報ボットを作成していて、ミニアプリからチャットに情報を転送するオプションを追加したいと考えています。 window.open(url, '_blank'); を使用することにしました。 iPhone で試してみるまでは問題なく動作していました。転送の代わりに、Share を取得しま...
    プログラミング 2024 年 11 月 2 日に公開
  • フロントエンド開発者とは誰ですか?
    フロントエンド開発者とは誰ですか?
    今日のインターネット上のすべての Web サイトやプラットフォームのユーザー インターフェイス部分は、フロントエンド開発者の仕事の成果です。彼らはユーザーフレンドリーなインターフェイスの作成に携わり、サイトの外観と機能を保証します。しかし、フロントエンド開発者とはいったい誰なのでしょうか?簡単に説明...
    プログラミング 2024 年 11 月 2 日に公開
  • CSS スタイルを保持したまま HTML コンテンツを PDF として保存するにはどうすればよいですか?
    CSS スタイルを保持したまま HTML コンテンツを PDF として保存するにはどうすればよいですか?
    CSS を含む HTML コンテンツを PDF として保存するWeb 開発では、コンテンツを別の形式にエクスポートする場合でも、見た目の美しさを維持することが非常に重要です。変換プロセス中に CSS スタイルが失われる可能性があるため、HTML 要素を PDF として保存しようとするときに問題が発生...
    プログラミング 2024 年 11 月 2 日に公開

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3