コントローラーランタイムのキャッシュの仕組みと、なぜあなたの... ノート

コントローラーランタイムのキャッシュの仕組みと、なぜあなたのコントローラーがAPIサーバーをクラッシュさせないのか

Kubernetesコントローラーは、一般的にGo言語でkubebuilderとcontroller-runtimeを使用して記述され、分散ワークロードの効率的な開発を提供します。しかし、負荷が増加するにつれて、本番環境の問題を回避するためにcontroller-runtimeの内部動作を理解することが重要になります。中心的な概念は、リコンサイラー内のr.Get()r.List()がAPIサーバーに直接クエリするのではなく、ローカルのインメモリキャッシュにアクセスすることです。このキャッシュは、リスト操作によって最初に設定され、その後ウォッチによって維持されます。この設計により、ローカル読み取りは非常に高速になり、コントロールプレーンへの負荷を回避できます。しかし、トレードオフとして、ローカルキャッシュはかなりのメモリを消費する可能性があり、古いデータを返す可能性があります。対照的に、書き込みは常にキャッシュをバイパスしてAPIサーバーに向けられます。このローカルキャッシュのサイズとインデックス作成戦略は、メモリ使用量に直接影響します。非効率的なList()操作は、多数のオブジェクトの線形スキャンを遅くする可能性があります。リコンサイルループは、望ましい状態と実際の状態を継続的に比較して、それらを一致させます。イベントはキューに入れられ、現在の状態を読み取り、必要なアクションを決定し、新しいイベントを生成する可能性のあるリコンサイル関数につながります。キャッシュの主な目的は、これらのリコンサイルループのためにオブジェクトの状態の迅速で最新のビューを提供することです。このウォッチベースのモデルは、client-goに由来し、更新のための単一の長寿命接続を維持することにより、継続的なAPIポーリングを防ぎます。controller-runtimeは、Reflector、DeltaFIFO、Indexerなどの低レベルのclient-goコンポーネントの複雑さを抽象化します。Managerは、共有キャッシュ、コントローラー、その他のサービスをオーケストレーションします。Informerは特定のGVKを監視し、ローカルストア(Indexer)を維持し、イベントをサブスクライバーにディスパッチします。ResourceEventHandlersはこれらのイベントを処理し、ストアを更新し、それがコントローラーのビューに反映されます。workqueueは、ワーカーによる処理のためにオブジェクトキーを管理し、Predicatesはエンキュー前にイベントをフィルタリングします。ReflectorはAPIサーバーと直接やり取りする唯一のコンポーネントであり、初期リストを実行してからウォッチを確立します。resourceVersionを利用してウォッチを再開し、イベントの見落としがないことを保証します。ウォッチ接続が失われた場合、またはAPIサーバーが古いresourceVersionを示した場合、再リストが実行されます。DeltaFIFOは変更の順序付けられたバッファとして機能し、オブジェクトキーごとにデルタをグループ化します。最後のPop()呼び出し以降のオブジェクトのすべての累積変更を提供し、順序付けられたバッチ処理を保証します。ただし、連続する「追加」または「更新」イベントを折りたたまないため、急速に変化するオブジェクトに対して複数の処理ラウンドが発生する可能性があります。