Kubernetes Custom Controllerの所感と使いどころ

こんにちは、varu3です。年の瀬なので久しぶりに技術ポエムです。

 11月に行われた、CloudNative Days Tokyo 2021 で行われた「Kubernetesオペレータのアンチパターン&ベストプラクティス」というセッションを見てから、Kubernetesオペレータに興味を持ち、自分でも調べながら簡単なオペレータをいくつか作ってみたのでKubernetesオペレータへ感じた所感と使いどころを自分なりに整理してみようと思います。

Kubernetesオペレータとは何か

 あえて一言で表すと「自身で定義できるカスタムリソースが作成された際に、冪等性のある処理(Reconcile Loop)を実行することで、そのカスタムリソースで定義された状態に近づけるプログラム」のことを言います。

超雑に絵を描くとこんな感じ

 オペレータを用いることの利点としては、そのカスタムリソースの状態を宣言的に定義することができるという点です。コントローラーが実行するReconcile Loopでその定義した状態になるため、運用を自動化することができます。ここではKuberenetes内のリソースに変化を与えるように描いてますが、クラスタ外のリソースも管理することができます。

 有名なプロダクトとしては、alb-ingress-controller(AWS ALBをKubernetes内からingressとして作成してくれるコントローラ)や、cert-manager(証明書を自動で発行してくれるコントローラ)などがありますね。

 正直、はじめにKubernetesオペレータの概念を知ったときは、Kuberenetesクラスタ内に状態を持つということに抵抗感があったため非常に懐疑的でした。例えば、「マスターノードのバージョンアップのためKubernetesクラスタ自体を作り直す」というオペレーションが発生した際に、オペレータによって作成された外部リソースの取り扱いが非常に難しくなるポイントだと感じました。

 とはいえ、宣言的にリソースの状態を定義するというコンセプト自体には惹かれるものがあったので、手を動かさずに文句を言うのは老害の始まりなのでつべこべいわずに手を動かしてみたって感じです :)

Kuberenetesオペレータと他ツールとの比較

 私がオペレータを作成する際にはフレームワークとして、kubebuilderを用いています。実際触ってみた印象としよくあるツールと比較して気づいた点などを挙げてみようと思います。

AWS Lambda Function 等との比較

 ここでは自分がよく使うのでAWS Lambda Functionとしましたが、Kubernetes外部からリソースを操作するツール全般との比較です。何かKubernetesの処理をしたいな〜と思った時にpythonのコードやkubectlコマンドをFaaSに乗っけて実行したいと思うことがあります。(自分もそのようにしていました。)それらと比較すると、コントローラを用いた場合には、以下のような利点がありました。

Kubernetesに設定されたRBACやSecretを扱うことができる

 kubernetesのAPIを外部から実行する場合には、サービスアカウントを発行してRBACを設定して…という操作が必要だったり、外部からKubernetesのSecretsを使いたい場合にはSecretをとってきて環境変数にセットして…というような初期化が必要になります。結構めんどくさいですよね。

 コントローラの場合には、前者のRBACの設定はkubebuilderのcontroller-toolsの機能によって簡単に設定することができますし、後者もコントローラー自身がPodとして動作するので、Secretをマウントするだけなので必要ありません。

リソースを監視して処理を実行することができる

 事前にKubernetesリソースの監視対象を設定することで、そのリソースが作成、更新、削除された時にReconcile Loopを実行することができます。例えば、監視対象をPodやDeploymentに設定した場合にはそれらのイベントにフックさせた処理を走らせることができるので、これがめっちゃ便利だと感じました。

 「リソースの更新などのイベント契機で処理を実行する」ことを考える場合は、オペレータが大変便利そうでした。

Terraform との比較

 冪等性というならterraformでいいのでは、というterraform大好きっ子の声が聞こえてきそうです。実際、terarformでKuberenetesリソースを扱うのは定義された状態がtfstateファイルに保存されるので、manifestで管理するよりも状態を管理しやすいですし。

 terraformと比較してオペレータの場合、原理上、定義した状態との解離が発生しづらいという利点があります。Reconcile Loopの処理は先述したイベント契機に加えて、ReconcileLoop処理の失敗時やコントローラーの起動時など様々なタイミングで実行されます。そのためコントローラで作成されたリソースが手動で変更されたとしても、すぐにReconcileLoopがはたらきます。

 そのため(Reconcile Loop自体に問題がない限り)ドリフトが発生しません。もう一ついえばGolangで実装できるというのもHCLに苦労している一部の人にとっても良い点かもしれないです。

Kubernetesオペレータの使いどころ

 とまあ、Kubernetesオペレータを随分とヨイショしてみました。というより、ほとんどがkubebuilderというフレームワークの利点でもあり、Reconcile Loopに集中して実装するためのライフサイクルがとても良くできているなという印象でした。

 とはいえ、微妙な点もいくつかあります。一番の問題点は、肝心なReconcile Loopで冪等になるよう自分で実装しなければいけないところです。おそらくこれが一番テクニックが必要な難しいポイントになりそう。後はCRD(カスタムリソースの定義)を自分で設計しなければいけないところや、テストを書くのが難しい点などなど、難点がいくつかありちゃんとやろうとするには経験が必要であるのではないかと感じました。

 使いどころとしては、Kubernetesのリソースを何かしら自動化したい場合には大変有用なツールだと感じました。シンプルにKubernetes内のイベントをフックさせたり、watchしたりする際にはオペレータを使って実装するのが最適であると思います。

 一方、Kubernetes内から外部のリソースを制御できるようにする処理についてはまだまだ慎重にやるべきだという気持ちがあります。kubebuidlerでFinallizerなどの仕組みが用意されてはいるものの、それらの制御をオペレータ単体で実装するとなると、難易度が跳ね上がります。

 この抜け道として、コントローラをKubernetesイベントをwatchすることに専念して、イベントを契機にLambdaなどの外部のリソースを発火させることに専念するという方法があります。この方法だとコントローラ自体は状態を持つことはなくなるので簡単に処理を実装できるのではないかと思いました

 何にせよ、慣れないうちはコントローラ自体を薄くもつのが良いのではないかと感じました。

感想

 使い所さえ間違えずにシンプルに使うのであれば、大変有用なツールであると感じました(まあなんでもそうなんですが…)。一方で、複数リソースや外部のリソースを取り扱うためにはそれなりの覚悟が必要だと感じました。個人的なことをいえばゴリゴリ処理をGolangで書くのはとてもエキサイティングなので、もうちょっとコントローラを作って自作Kubernetesコントローラおじさん力を高めていきたいと思っています。

参考になった資料など

この記事が気に入ったらサポートをしてみませんか?