リーダー選出
Spring Cloud Kubernetes リーダー選出メカニズムは、Kubernetes ConfigMap を使用して Spring Integration のリーダー選出 API を実装します。
複数のアプリケーションインスタンスがリーダーシップをめぐって競合しますが、リーダーシップが与えられるのは 1 つだけです。リーダーシップが付与されると、リーダーアプリケーションはリーダーシップ Context を持つ OnGrantedEvent アプリケーションイベントを受け取ります。アプリケーションは定期的にリーダーシップを獲得しようとし、最初の呼び出し元にリーダーシップが与えられます。リーダーは、クラスターから削除されるか、リーダーシップを放棄するまで、リーダーであり続けます。リーダーの削除が発生すると、前のリーダーは OnRevokedEvent アプリケーションイベントを受け取ります。削除後は、古いリーダーを含め、クラスター内のすべてのインスタンスが新しいリーダーになる可能性があります。
これをプロジェクトに含めるには、次の依存関係を追加します。Fabric8 リーダーの実装
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-fabric8-leader</artifactId>
</dependency>リーダーの選択に使用される構成マップの名前を指定するには、次のプロパティを使用します。
spring.cloud.kubernetes.leader.config-map-name=leaderリーダー選出情報コントリビューター
Spring Cloud Kubernetes Leader には、Spring Boot の /actuator/info エンドポイントにリーダー選出情報を追加する InfoContributor が含まれています。このコントリビューターは、リーダー ID、ロール、現在のアプリケーションインスタンスがリーダーであるかどうかなど、現在のリーダーに関する情報を提供します。
出力例:
{
"leaderElection": {
"leaderId": "my-app-pod-1",
"role": "my-role",
"isLeader": true
}
}application.[properties | yaml] で management.info.leader.enabled を false に設定することで、この InfoContributor を無効にすることができます。
management.info.leader.enabled=falseリーダー選出を設定する別の方法があり、これは Fabric8 と Kubernetes クライアントの両方でネイティブサポートされています。長期的には、これがリーダー選出を設定するデフォルトの方法となり、以前の方法は廃止される予定です。この方法は、JDK の「プレビュー」機能と同様に扱うことができます。
これを使用するには、プロパティを設定する必要があります。
spring.cloud.kubernetes.leader.election.enabled=true 以前の実装とは異なり、この実装ではクラスタのバージョンに応じて、Lease または ConfigMap のいずれかをロックとして使用します。リースがサポートされている場合でも、以下のコマンドで configMap の使用を強制できます。
spring.cloud.kubernetes.leader.election.use-config-map-as-lock=trueLease または ConfigMap の名前は、プロパティを使用して定義できます (デフォルト値は spring-k8s-leader-election-lock です)。
spring.cloud.kubernetes.leader.election.lockName=other-name ロックが作成される名前空間 (明示的なものが存在しない場合は default が設定される) も設定できます。
spring.cloud.kubernetes.leader.election.lockNamespace=other-namespaceリーダー選出プロセスが開始される前に、pod が準備完了(準備チェックによる)になるまで待つことができます。これはデフォルトで有効になっていますが、必要に応じて無効にすることができます。
spring.cloud.kubernetes.leader.election.waitForPodReady=false以前の実装と同様に、デフォルトでイベントを公開しますが、これを無効にすることもできます。
spring.cloud.kubernetes.leader.election.publishEvents=false リーダー選出プロセスを制御するパラメーターがいくつかあります。説明するには、このプロセスの高レベル実装を確認する必要があります。すべての pods 候補は、リーダーになろうとするか、ロックの取得を試みます。ロックがすでに取得されている場合は、spring.cloud.kubernetes.leader.election.retryPeriod (値は java.time.Duration で指定され、デフォルトでは 2 秒)ごとにロック取得を再試行します。
ロックが取得されない場合、現在の pod がリーダーになります。これは、いわゆる「レコード」(Lease または ConfigMap)をロックに挿入することによって行われます。この「レコード」に含まれるものの中には、leaseDuration があります(これは spring.cloud.kubernetes.leader.election.leaseDuration で指定できます。デフォルトでは 15 秒で、java.time.Duration 型です)。これはロックの TTL のように機能します。つまり、この期間(最終更新時刻から)が経過しない限り、他の候補はロックを取得できません。
特定の pod が(ロックを取得して)リーダーとしての地位を確立すると、その pod は(すべての spring.cloud.kubernetes.leader.election.retryPeriod に対して)継続的にリースの更新を試みます。言い換えれば、リーダーシップの拡大を試みます。更新が発生すると、ロック内に保存されている「レコード」が更新されます。たとえば、renewTime はレコード内で更新され、前回の更新がいつ行われたかを示します。(これらのフィールドの内容は、たとえば kubectl describe lease… を使用することでいつでも確認できます)
更新は、spring.cloud.kubernetes.leader.election.renewDeadline で指定された一定の間隔内に行われなければなりません。デフォルトでは 10 秒に設定されており、リーダーである pod は最大 10 秒以内にリーダーシップを更新する必要があります。この時間が経過しない場合、この pod はリーダーシップを失い、リーダー選出が再度開始されます。他の pods は 2 秒ごとにリーダーになろうとするため(デフォルト)、リーダーシップを失った pod が再びリーダーになる可能性があります。他の pods がリーダーになる可能性を高めたい場合は、プロパティ(秒単位で指定、デフォルトは 3)を設定できます。
spring.cloud.kubernetes.leader.election.wait-after-renewal-failure=3これは、リースを更新できずリーダーシップを失った pod が、再びリーダーになろうとする前に、この秒数待機することを意味します。
これらの設定を例に基づいて説明してみましょう。リーダー選出に参加する 2 つの pods があります。説明を簡単にするために、これらを podA と podB と呼びます。これらは同時に 12:00:00 で開始されますが、podA がリーダーとして選出されます。つまり、2 秒ごと(retryPeriod)、podB は新しいリーダーになろうとします。つまり、12:00:02、12:00:04 …と、基本的に「リーダーになってもいいですか?」と尋ねていることになります。この簡略化された例では、その質問への答えは podA のアクティビティに基づいて得られます。
podA がリーダーになった後、2 秒ごとにリーダーシップの「延長」、つまり更新を試みます。そのため、12:00:02、12:00:04 などでは、podA がロックにアクセスし、自身のレコードを更新して、依然としてリーダーであることを反映させます。最後の正常な更新から次の更新までの間は、ちょうど 10 秒です (renewalDeadline)。この 10 秒以内にリーダーシップの更新に失敗した場合 (接続の問題や GC の大きな一時停止など)、podA はリーダーとしての立場を停止し、podB がリーダーシップを獲得できるようになります。podA がリーダーとしてのロールを正常に終了すると、ロックレコードは「クリア」されます。これは基本的に、podB がすぐにリーダーシップを獲得できることを意味します。
たとえば、podA が OutOfMemory でロックレコードを適切に更新できずに終了した場合、状況は変わります。このとき、leaseDuration 引数が重要になります。最も簡単な説明は、例を挙げることです。
podA has renewed its leadership at 12:00:04, but at 12:00:05 it has been killed by the OOMKiller. At 12:00:06, podB will try to become the leader. It will check if "now" (12:00:06) is after last renewal + lease duration, essentially it will check:
12:00:06 > (12:00:04 + 00:00:10)The condition is not fulfilled, so it can’t become the leader. Same result will be at 12:00:08, 12:00:10 and so on, until 12:00:16 and this is where the TTL (leaseDuration) of the lock will expire and podB can acquire it. As such, a lower value of leaseDuration will mean a faster acquiring of leadership by other pods.
You might have to give proper RBAC to be able to use this functionality, for example:
- apiGroups: [ "coordination.k8s.io" ]
resources: [ "leases" ]
resourceNames: [ "spring-k8s-leader-election-lock" ]
verbs: [ "get", "update", "create" ]
- apiGroups: [ "" ]
resources: [ "configmaps" ]
resourceNames: [ "spring-k8s-leader-election-lock" ]
verbs: [ "get", "update", "create" ]