連載: Kubernetesでカスタムコントローラを作ろう! ~第5回 宣言的な管理とReconciliation Loop~
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
前回まででKubernetesのリソースやAPIについて学びました。 今回はやっとこの連載の本題であるコントローラについて説明していきます。 Kubernetesのコントローラは、大まかにどのように実装されているのか触れていきます。
第1回で紹介した通り、Kubernetes のコントローラは現在の状態をあるべき状態へ収束させるように動作するものです。 あるべき状態はDeploymentやStatefulSet、前回定義したカスタムリソースであるSimplePodなどKubernetesに作成されたオブジェクトです。 コントローラはクラスタや外部リソースなどを操作し、マニフェストとして定義したオブジェクトの状態となるように動作しなければなりません。
宣言的な管理
Kubernetesのマニフェストを書いたことがある方ならご存じだと思いますが、マニフェストには何かしらの操作を実施する時の手順は書きません。 簡単な例としてDeploymentのレプリカ数の変更する場合を考えてみましょう。 Kubernetesでは、レプリカ数を増やす場合も減らす場合も、ユーザが気にする必要ありません。 レプリカ数を増やす時も減らす時も、.spec.replicasフィールドの数値を変えるだけです。今のレプリカ数が全く分からない状態であっても、ユーザが望むレプリカ数を指定すればコントローラが調整してくれます。 ノードの障害等で管理されていたPodが削除されてしまった場合も、コントローラがそれを検知し、定義してあるレプリカ数のPodがきちんと起動するように調整してくれます。 もちろんこのように処理するようなフローをマニフェストには定義していません。 あるべき状態を定義するだけで、コントローラが良しなに対応してくれるのです。 この"良しなに対応してくれる"処理をReconcile処理と呼びます。 コントローラではこのReconcile処理をループで実行しており、それをReconciliation Loopと呼びます。
Reconciliation Loop
コントローラ内ではReconciliation Loopと呼ばれるあるべき状態へ収束させる(Reconcile)ループ処理が実行されています。 このReconciliation Loopはイベントを起因に実行されます。 コントローラにより異なりますが、オブジェクトに変更があった場合やポーリング、クラスタ外部のイベントなどによりReconcile処理が実行されます。 Reconcile処理の特徴として、イベントの種類によって処理が分岐することはなく、常に同じReconcile関数が呼び出されます。 そのためReconcile処理は冪等に実装しなければなりません。 このような実装方法としている理由を詳しく知りたい方はLevel Triggering and Reconciliation in Kubernetes | HackerNoonをご参照ください。
Reconciliation Loopを冪等にするために、おおよそ以下の図の流れとなるように実装することとなります。
現状の状態をを確認し(Observe)、あるべき状態と現状の差分を計算し(Diff)、差分に対する処理を実施(Act)します。 ReplicaSet ControllerがあるReplicaSetをReconcileするシンプルな例を用いて、少し具体的に考えてみましょう。 最初のObserveではReplicaSetのオブジェクトとクラスタ上の状態を確認します。
この例ではクラスタ上にはReplicaSetにより管理されているPodが1Pod存在することがわかりました。 そしてオブジェクトには.spec.replicas=3となっており、3Pod作成されていることがあるべき状態でした。
次にDiffで差分を計算します。この場合、2Podクラスタ上に足りないことは明らかですね。
最後にActで、この2Pod足りない分のPodを作成します。 このように実装することで、発火したイベントは関係なくReconcile処理を実行することができます。
ちなみに、無駄なReconcile Loopを実施しなくて済むように、kubebuilderで利用されているcontroller-runtimeなどのライブラリを利用すれば、複数のイベントが同一のオブジェクトを対象にしていた場合、重複排除された状態でワークキューに積まれます。 そしてReconciliation Loopでは、そのキューからイベントを取り出し、本当に必要なReconcile処理だけを実行します。
Reconcileはシンプルに
Reconciliation Loopはスムーズに回らなければなりません。 あるオブジェクトに対するReconcile処理で詰まってしまうと、コントローラ全体の処理が進まなくなってしまうためです。 Reconciliation Loopの並列数を上げることで、ある程度緩和できます。 しかしそれにも限界があったり、コントローラによっては並列数を上げることができない実装のものもあります。 そのような場合は、時間がかかる処理で一度イベントをRequeueし再度順番待ちにすることで、全体の処理を止めない方法が考えられます。 またRequeueはkube-apiserverへ負荷をかけすぎないようにするためなどにも有用です。 一度のReconcile処理であるべき状態にせず、何度かReconcile処理を実行することで、徐々にあるべき状態となるような実装をしているコントローラもあります。 例えばReplicaSet Controllerでは作成すべきPodが多い時は、一度のReconcileで作成されるPod数に上限を設けていたりします。
あるReconcile関数は、基本的に1つのリソースを対象とします。 複数リソースを対象としないことで、コントローラの責務をはっきりさせることができ、コードの可読性にも繋がります。
最後に
今回は宣言的な管理とReconciliation Loopについての説明でした。 次回は実際のコントローラの実装を見ながら、コントローラ開発のイメージをより具体的なものにしていきましょう。
クリエーションラインではKubernetesに関する様々なトレーニングを提供しております。 詳しくはこちらをご参照ください。
また、この連載の更新通知を受け取りたい方は、Twitterアカウントのフォローや、このページ下部にリンクのあるCL LAB MAIL MAGAZINEへの登録をぜひお願いします!