🐸

K8s client-goのLeader Electionを読む

に公開

はじめに

sample-controllerのコードリーディングではリーダ選出には触れていませんでした。
client-goにはリーダ選出の仕組みが備わっているため、サンプルコードを読みつつ理解してみようというのが趣旨になります。

コードリーディング

https://github.com/kubernetes/client-go/blob/d033c497ffef47be9b4f81abde5c3d94dd78089a/examples/leader-election/main.go#L1-L170

細かく読み込む前に、client-goのリーダ選出がどのように実装されているのかをコメントからみてみます。

Lease Resourceを使ったリーダ選出

https://github.com/kubernetes/client-go/blob/d033c497ffef47be9b4f81abde5c3d94dd78089a/examples/leader-election/main.go#L76-L80

コメントにあるように、リーダ選出にはKubernetesのLease Resourceを利用します。

https://kubernetes.io/docs/concepts/architecture/leases/

このLease Resourceですが、Nodeの死活監視やkube-controller-managerkube-schedulerのリーダ選出でも使われているResourceです。

中身は下記のようになっています。

apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2023-07-02T13:16:48Z"
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
  resourceVersion: "334899"
  uid: 90870ab5-1ba9-4523-b215-e4d4e662acb1
spec:
  holderIdentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leaseDurationSeconds: 3600
  renewTime: "2023-07-04T21:58:48.065888Z"

https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#leasespec-v1-coordination-k8s-io

ざっくりとですが「誰がいつからLease Resourceを所有しているか」という情報が含まれています。

公式ドキュメントのWorkloadsにもあるように、このLease Resourceを使ったリーダ選出がclient-goで実装されています。

それでは読んでいきましょう。

リーダとして実行する処理

https://github.com/kubernetes/client-go/blob/d033c497ffef47be9b4f81abde5c3d94dd78089a/examples/leader-election/main.go#L87-L92

モックです。実際はこの部分にリーダとして実行したい処理を書く必要があります。

シグナル時の安全な停止

https://github.com/kubernetes/client-go/blob/d033c497ffef47be9b4f81abde5c3d94dd78089a/examples/leader-election/main.go#L102-L108

シグナル発生時の処理が記載されています。sample-controllerの時にも似たような処理がありました。

Lease Resourceの設定

https://github.com/kubernetes/client-go/blob/d033c497ffef47be9b4f81abde5c3d94dd78089a/examples/leader-election/main.go#L112-L121

ここでリーダ選出に利用するLease Resourceの設定がされています。
特定のNamespaceにLease Resourceを作成するので、実行環境のアイデンティティにはRole/RoleBindingが必要になります。

リーダ選出を含む処理の開始

https://github.com/kubernetes/client-go/blob/d033c497ffef47be9b4f81abde5c3d94dd78089a/examples/leader-election/main.go#L123-L170

実際の処理がこのRunOrDieで行われています。各引数を見ていきましょう。

https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#RunOrDie

https://pkg.go.dev/k8s.io/client-go/tools/leaderelection#LeaderElectionConfig

項目 内容 実装例
Lock rl.Interface ロック用のLease Resource。(ConfigMap Resource等も利用可)
LeaseDuration time.Duration リーダ以外がLeaseを強制取得開始する時間
RenewDeadline time.Duration リーダがLeaseを更新するまでのタイムリミット
RetryPeriod time.Duration 各インスタンスがリーダ選出を試す間隔
Callbacks LeaderCallbacks 後述
WatchDog *HealthzAdaptor ヘルスチェック用の機能
ReleaseOnCancel bool contextの終了時にLeaseを手放すかどうかのフラグ。trueにする場合はcontextの終了前にリーダ処理を正しく終了させること
Name string デバッグ用の名前。ログに表示される
Coordinated bool Coordinated Leader Electionを利用するかどうかのフラグ。通常のLeader Electionと何が違うの?という件についてはこのQiita記事を参照

Callbacks LeaderCallbacksは下記になります。

項目 内容
OnStartleading 自身がリーダとなったときに呼び出される。リーダとして実行したい処理を実装する。
OnStoppedLeading リーダではなくなった場合や、一度もリーダにならずに終了した場合に呼び出される
OnNewLeader 自身を含むいずれかのインスタンスが新しいリーダとして選出されたときに呼び出される。

今回の設定だと、

  • 前回のLease更新から15秒以内にリーダがLeaseを更新できなければ、リーダから降りてLeaseを開放する
  • 前回のLease更新から60秒経過していたら、リーダではないインスタンスによって強制的にリーダ選出がされる
  • 5秒毎にリーダ選出の処理が行われる(LeaseDuration以内であればリーダの強制選出はされない)

となります。

おわりに

今回はclient-goに実装されているリーダ選出の機能をみていきました。
分散システムであるKubernetesでは、Lease Resourceのような仕組みがもともと含まれており、それが使いやすい形にライブラリ化されているのは良いですね。

宿題

  • WatchDog *HealthzAdaptorのサンプル作成と疎通
  • sample-controllerへのリーダ選出機能の追加

おまけ

kubernetes/ingress-nginxのLeader Election

https://github.com/kubernetes/ingress-nginx/blob/57e6899d86751519e9526eb9f68b296bbc29d9b0/internal/ingress/controller/status.go#L111-L118

HealthzAdaptorの設定あるかなと思って調べてみましたがなさそうです。

oracle/oci-native-ingress-controllerのLeader Election

https://github.com/oracle/oci-native-ingress-controller/blob/2eb08b97402ae8435e6f4e5deb5ad30f7a5d7af6/main.go#L205-L236

こっちもなさそう。

Discussion