11.6. リーダー選択の設定

Operator のライフサイクル中は、いずれかの時点で複数のインスタンスが実行される可能性があります。たとえば、Operator のアップグレードをロールアウトしている場合などがこれに含まれます。この場合、リーダー選択を使用して複数の Operator 間の競合を避ける必要があります。 これにより、1 つのリーダーインスタンスのみが調整を行い、他のインスタンスは非アクティブな状態であるものの、リーダーがその役割を実行しなくなる場合に引き継げる状態にできます。

2 種類のリーダー選択の実装を選択できますが、それぞれに考慮すべきトレードオフがあります。

  • Leader-for-life: リーダー Pod は削除される場合のみリーダーシップを放棄します (ガべージコレクションを使用)。この実装は 2 つのインスタンスが誤ってリーダーとして実行されるのを防ぎます (スプリットブレイン)。しかし、この方法では、新規リーダーの選択に遅延が生じる可能性があります。たとえば、リーダー Pod が応答しないノードまたはパーティション化されたノードにある場合、pod-eviction-timeout はリーダー Pod がノードから削除され、リーダーシップを中止するまでの時間を判別します(デフォルトは 5m)。詳細は、Leader-for-life Go ドキュメントを参照してください。
  • Leader-with-lease: リーダー Pod は定期的にリーダーリースを更新し、リースを更新できない場合にリーダーシップを放棄します。この実装により、既存リーダーが分離される場合に新規リーダーへの迅速な移行が可能になりますが、スピリットブレインが特定の状況で生じる場合があります。詳細は、Leader-with-lease Go ドキュメントを参照してください。

デフォルトで、Operator SDK は Leader-for-life 実装を有効にします。実際のユースケースに適した選択ができるように両方のアプローチのトレードオフについて、関連する Go ドキュメントを参照してください。

以下の例は、これらの 2 つのオプションを使用する方法について説明しています。

11.6.1. Leader-for-life 選択の使用

Leader-for-life 選択の実装の場合、leader.Become() の呼び出しは、memcached-operator-lock という名前の ConfigMap を作成して、リーダー選択までの再試行中に Operator をブロックします。

import (
  ...
  "github.com/operator-framework/operator-sdk/pkg/leader"
)

func main() {
  ...
  err = leader.Become(context.TODO(), "memcached-operator-lock")
  if err != nil {
    log.Error(err, "Failed to retry for leader lock")
    os.Exit(1)
  }
  ...
}

Operator がクラスター内で実行されていない場合、 leader.Become() はエラーなしに返し、Operator の namespace を検出できないことからリーダー選択をスキップします。

11.6.2. Leader-with-lease 選択の使用

Leader-with-lease 実装は、リーダー選択について Manager オプションを使用して有効にできます。

import (
  ...
  "sigs.k8s.io/controller-runtime/pkg/manager"
)

func main() {
  ...
  opts := manager.Options{
    ...
    LeaderElection: true,
    LeaderElectionID: "memcached-operator-lock"
  }
  mgr, err := manager.New(cfg, opts)
  ...
}

Operator がクラスターで実行されていない場合、Manager はリーダー選択用の ConfigMap を作成するための Operator の namespace を検出できないことから開始時にエラーを返します。Manager の LeaderElectionNamespace オプションを設定してこの namespace を上書きできます。