k8sのコントローラはどのようにリソースの変更を検知しているのか
はじめに
本記事はKubernetesのコントローラの解説記事のPart2になる。
解説記事では、下記3パートに分けてKubernetesのコントローラについて解説している。
- 各コントローラはどのようにリソースを更新しているのか
- どのようにリソースの変更を検知しているのか
- どのようにリソースの変更を各コントローラに送信しているのか
Part1では、「各コントローラはどのようにリソースを更新しているのか」を解説している。もしPart1をまだ読んでいない&コントローラの全体像を把握したい方がいれば、そちらを先に読むことをお勧めする。
それでは、「どのようにリソースの変更を検知しているのか」について解説していく。
※解説している処理・コードはKubernetes v1.27.0のものとなる。
リソースの変更検知を行うコンポーネント
イベントに関わる処理を担当するコンポーネントとしてSharedIndexInformerがある。
主に下記を行うコンポーネントとなっている。
- 各リソースの状態変更検知
- 状態のキャッシュ
- 各コントローラへイベントを送信
本記事ではリソースの状態変更検知について解説する。ほかの機能については次回の記事で解説する。
このコンポーネントはkube-controller-managerによって生成・起動される。これは各種コントローラの起動時に埋め込まれる。
SharedIndexInformerの埋め込みから起動までの流れ
SharedIndexInformerFactoryをフィールドに持つControllerContextを生成。
この時点ではSharedIndexInformerFactoryは内部に各種コントローラ用のSharedIndexInformerを持っていない。
次に各種コントローラを起動する。
渡されたControllerContextを用いて、Deployment、ReplicaSet、Pod用のSharedIndexInformerを取得し、Deploymentコントローラに埋め込む。
(controllerContext.InformerFactory.Apps().V1().Deployments()
はDeployment用のSharedIndexInformerを返す)
これらはコントローラがイベントハンドラを登録されるときに利用される。
ここでInformer()
が呼ばれるとSharedIndexInformerFactoryに対象のSharedIndexInformerが登録される。
ここまでの処理により、各リソースに対応するSharedIndexInformerの登録が完了する。
その後、kube-controller-managerはSharedIndexInformerFactoryを起動する。
この起動処理により、各種Controllerの起動後に登録されたSharedIndexInformerが一括で並列起動される。
下記はDeploymentのSharedIndexInformerの生成処理。Deploymentの状態を監視するための関数を登録し、生成している。
これら監視用関数が実際のリソースの変更検知処理にて利用されている。これら関数を用いて変更があった場合は変更イベントが生成されている。
ここまでの処理により生成されたSharedIndexInformerは、起動時に下記コンポーネントを実行する。
- リソースの変更を検知するためのcontrollerの起動
- イベントを各種コントローラへ伝搬するためのsharedProcessorの起動
本記事では、controllerの検知処理について解説していく。(sharedProcessorについては次回の記事で解説する)
リソースの変更検知のしくみ
リソースの状態を監視し、変更を検知するのはcontrollerの責務となっている。
controllerは内部で2つのコンポーネントに分かれており、Reflector(r.Run()
)とcontroller.processLoop()
を起動する。
- Reflector: 対象のリソースの変更を監視し、変更イベントをイベント用のキューへ送信する。
-
processLoop()
: Reflectorが送信したイベントに応じて、sharedProcessorへ変更イベントを送信する(次回の記事で解説する)
Reflectorについて
Reflectorはリソースの変更を監視するコンポーネントであり、起動後に下記処理を行う。
- 現状のリソース状態を取得する
- キューの中身を最新状態に置き換える
- 現状のリソース状態を取得する
- 過去の状態と比較し、変更内容を生成する
- キューへ変更イベントを送信する
- 3~6を繰り返す
Reflectorは起動時にReflector.ListAndWatch()
を起動する。これがReflectorの処理を行っている。
このメソッドは、まず始めにReflector.watchList()
, Reflector.list()
を呼び出すことにより、現在のリソース状態を取得し、キューの中身を最新の状態に置き換える。
たとえば、Reflector.watchList()
はlisterWatcher.Watch()
を呼び出し、リソース状態の監視を開始する。
このWatch()
は事前に登録されたWatchFunc()
を呼び出している。
変更監視結果はwatchHandler()
で取得され、その変更タイプに応じたイベントをtemporaryStore
へ送信する。
ここで利用されているtemporaryStore
は名前の通り一時的なリソースであり、下記のキュー(store
)に入っている状態を最新の状態で置き換えるのに利用されている。
ここまでの処理により、起動時のリソース状態の取得とキューの初期化が完了する。
なお、ここで利用されているキューはSharedIndexInformerの起動時に登録されている。
その後はReflector.ListAndWatch()
がReflector.watch()
を起動し、リソースの状態監視を開始する。
事前にReflector.watchList()
を実行していた場合は、そこで起動したlisterWatcher.Watch()
の監視処理を渡して利用している(w
が監視処理)。
変更があれば、Reflector.watchList()
と同様にwatchHandler()
がその情報を取得し、適切なイベントに変換してキューへ送信する。ここでは一時的なリソースは利用せずに、直接キューにイベントを送信している。
ここまでの処理によりリソースの状態が監視され、変更があった場合にはイベントが生成される。そのイベントはキューとsharedProcessorを経由して各コントローラへ送信され、リソース更新に利用される。このイベント送信周りについては次回の記事で解説する。
おわりに
本記事では、リソースの変更検知処理について解説した。
次回の記事では、「どのようにリソースの変更を各コントローラに送信しているのか」について解説する。
Discussion