Operator FrameworkでKubernetesのAPIを拡張する方法

**Kubebuilder**について説明し、**Operator Framework**を通じて**Kubernetes API**を拡張する方法を3つの主要なパートで紹介します。


著者：アリババのシニアエンジニアSuxing氏

この記事では、現在のプラクティスとユースケースに基づいて、Operator Frameworkを介して[Kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.56016056cyOGOW) APIを拡張する方法を紹介します。(1)Operatorの基礎、(2)Operator Frameworkの基礎と開発プロセスの事例、(3)Operatorの事例の3つのパートで構成されています。

#1、オペレーターの概要
###基本コンセプト
はじめに、今回の記事に関わる基本的な概念をご紹介します。

- CustomResourceDefinition（CRD）：Kubernetesのカスタムリソースの一種です。
- Custom Resource（CR）：特定のCRDインスタンス。
- Webhook：Kubernetes APIサーバーに登録されたHTTPコールバックの一種。特定のイベントが発生すると、Kubernetes APIサーバーは登録されているWebhookを照会し、メッセージを転送します。

Webhookはmutating webhookとvalidation webhookに分けられます。Mutating webhookは入力オブジェクトを変更し、validating webhookは入力オブジェクトを読み取るだけです。


- ワークキュー：コントローラの重要な構成要素。ワークキューは、クラスター内のリソースの変更を監視し、関連するオブジェクトをイベントとして、そのアクションとキー（ポッドのCreateアクションなど）を含めて保存します。
- コントローラ:コントローラは、ワークキューを周期的に処理し、それぞれのロジックに従ってクラスタのステータスを期待されるステータスにプッシュします。コントローラの種類によって、実行される処理アクションが異なります。たとえば、ReplicaSetコントローラは、レプリカの数を監視し、Pod関連のイベントを処理します。
- オペレーター：Kubernetesアプリケーションの記述、展開、管理に使用されるメカニズム。実装面では、ユーザーのビジネスロジックを実装するためのCRD、Webhook、コントローラを組み合わせたものがオペレーターとなります。

###一般的なオペレーターモード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/1cc24cfd-1ac6-1e30-3d87-4761d46a779f.png)


ワークフロー:

1、CRDを作成します。
2、Kubernetes APIサーバは、登録されたパスリストに基づいて、CRDリクエストをwebhookに転送します。
3、webhookはCRDのデフォルトパラメータ設定とパラメータチェックを完了します。その後、関連するCRがデータベースに書き込まれ、返却されます。
4、コントローラは、バックグラウンドでCRを監視し、ビジネスロジックに基づいてCR関連の特殊操作を処理します。
5、先行する処理により、クラスタのステータスが変化し、コントローラが監視し、CRDのステータスデータとして記録します。

Operatorのワークフローは高レベルで説明されていますが、後ほどケーススタディを通して詳細に説明します。

#2、 Operator Frameworkの実践
###Operator Frameworkの概要
まず初めに、Operator Frameworkの基本を説明します。Operator Frameworkは、Webhookとコントローラを提供し、一般的な基礎的な詳細を開発者からシールドすることで、メッセージ通知のトリガーや失敗時の再キューなどを実装することなく、マネージドアプリケーションのO&Mロジックの実装に集中することができます。

主流のOperator FrameworkにはKubebuilderとOperator SDKがあり、どちらもcontroller-toolsとcontroller-runtimeを使用しています。Kubebuilderは包括的なテスト、デプロイメント、コードの足場を提供し、一方Operator SDKはAnsible Operatorsと上位層のオペレーションをよりよくサポートします。

###Kubebuilderの実践
このセクションでは、Alibaba CloudのオープンソースプロジェクトKruiseでSidercarSetsを介してKubebuilderを使用する方法を説明します。

SidercarSetsは、Sidecarコンテナ（セカンダリコンテナとも呼ばれる）をPodに挿入します。例えば、モニタリングやログ収集に使用するコンテナを挿入することで、Podの機能を充実させることができます。SidercarSetsは、挿入の状態とPodの状態に基づいて自身を更新し、セカンダリコンテナの状態を記録します。

###ステップ1：初期化
手順は以下の通りです。GitLabプロジェクトを作成し、`kubebuilder init --domain=kruise.io`コマンドを実行します。

パラメータの説明です。domainパラメータは、CRDオブジェクトの登録に使用するグループドメイン名を示します。

結果 依存関係にあるコードライブラリが引き出され、MakefileやDockerfileなどのツールファイルとともにコードフレームワークが作成されます。

以下は、作成された内容を簡略化したものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/73a30319-a056-4a72-8113-d34c063ac472.png)


詳細は実際に確認してみてください。

###ステップ2：APIの作成
手順:`kubebuilder create api --group apps --version v1alpha1 --kind SidecarSet --namespace=false`コマンドを実行します。

このコマンドを実行すると、API（またはCRD）とともにコントローラフレームワークが作成されます。

パラメータの説明:

- CRDの`Group: apps.kruise.io`は、groupパラメータとdomainパラメータで構成されています。
- バージョンパラメータには、コミュニティスタンダードで指定された3つの値があります。

- v1alpha1：このAPIは不安定です。CRDは廃棄される可能性があり、フィールドは調整される可能性があり、依存関係は必要ありません。
- v1beta1: この API は安定しており、下位互換性があり、機能の調整が可能です。
- v1: API と機能は安定しています。
- kindパラメータは、コミュニティのネイティブサービスタイプと同様に、CRDタイプを示します。
- namespacedパラメータは、CRDがグローバルに一意であるか、名前空間内で一意であるかを示し、これはnodeやpodの概念に似ています。

パラメータは大きく2種類に分けられます。group、version、kindの各パラメータは、CRDのメタデータの3つの重要な要素に対応しています。パラメータを設定する際には、こちらの一般的な基準を参考にしてください。namespacedパラメータは、CRDがグローバルにユニークであるか（nodeと同様）、名前空間内でユニークであるか（podと同様）を示しています。ここでは、namespaced を false に設定し、SidecarSet がグローバルにユニークであることを示しています。

結果:

CRDとコントローラフレームワークが作成された後、コードを記入します。

結果は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/5a9dee59-6830-3774-0293-1620cbffde9c.png)


青字の内容に注目してください。CRDはsidecarset_types.goで定義されており、これは手動で記入する必要があります。コントローラーはsidecarset_controller.goで定義されており、これも記入する必要があります。

###ステップ3：CRD関連コードの入力
1、作成したCRDは、pkg/apis/apps/v1alpha1/sidecarset_types.goにあります。以下の2つの作業を行います。

(1)コメントの調整

コードジェネレータはコメントに依存してコードを生成するため、コメントを調整する必要がある場合があります。この例では、SidecarSetの以下のコメントを調整します。

+genclient:nonNamespaced: 非名前空間オブジェクトを作成します。

+kubebuilder:subresource:status: ステータスのサブリソースを作成します。

+kubebuilder:printcolumn:name=`MATCHED`,type=`integer`,JSONPath=`.status.matchedPods`,description=xxx: kubectl get sidecarset: 後ほど説明します。


(2)フィールドの設定

CRDを有効にするために、以下のフィールドを設定してください。

- SidecarSetSpec: CRDに関する記述的な情報を入力します。
- SidecarSetStatus：CRDのステータスを入力します。CRDのステータスを入力します。

2、 makeコマンドを実行してコードを生成します。

gRPCインターフェースの設定やコーデックなど、CRDの基本的なコントローラの実装を完了する必要はありません。

最終的なフィールドは以下の通りです。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/cd62a19c-1b8e-b62f-32ce-453d5c8d89a8.png)


SidecarSetsはSidecarをpodに注入するためのものです。左図のようにSidecarSetSpecにはSelectorとContainerが定義されています。SelectorはSidecarを注入するPodを示し、ContainersはSidecarのコンテナを定義します。

右図に示すように、SidecarSetStatus にはステータス情報が定義されています。MatchedPodsは、SidecarSetにマッチするポッドの数を示しており、UpdatedPodsは、Sidecarが注入されたポッドの数を示しています。ReadyPodsは、正常に動作しているPodの数を示します。

詳細については、[このドキュメント](https://github.com/openkruise/kruise?spm=a2c65.11461447.0.0.56016056cyOGOW)を参照してください。

###ステップ4：Webhookフレームワークの作成
1、以下のコマンドを実行して、2つのmutating webhookを作成します。


```
kubebuilder alpha webhook --group apps --version v1alpha1 --kind SidecarSet --type=mutating --operations=create
kubebuilder alpha webhook --group core --version v1 --kind Pod --type=mutating --operations=create
```

2、以下のコマンドを実行して、検証用のWebhookを作成します。

```
kubebuilder alpha webhook --group apps --version v1alpha1 --kind SidecarSet --type=validating --operations=create,update
```

パラメータの説明:

- groupおよびkindパラメータは、処理対象となるリソースオブジェクトを示します。
- typeパラメータは、作成されるフレームワークのタイプを示します。
- operationsパラメータは、リソースオブジェクトに対する主要な操作を示します。

結果:

- Webhook フレームワークが作成されます。コードは手動で入力する必要があります。

結果は以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/bcae2d9c-49f7-77b7-1d03-c8b81b577b5b.png)


青字の内容は、先の3つのコマンドを実行することで作成される3つのハンドラーを示しています。以下では、ハンドラーの設定方法を説明します。

###ステップ5：Webhookの設定
作成されたWebhookハンドラは、以下の場所にあります。

- pkg/webhook/default_server/sidecarset/mutating/xxx_handler.go
- pkg/webhook/default_server/sidecarset/validating/xxx_handler.go
- pkg/webhook/default_server/pod/mutating/xxx_handler.go

コードを以下のように書き換えるか、記入します。

- 入力CRD以外のリソースが必要な場合は、Kubernetesクライアントをインジェクトします。本実施例では、SidecarSetが作成された後にSidecarがPodに注入されるため、Kubernetesクライアントを注入します。
- mutatingSidecarSetFnまたはvalidatingSidecarSetFnでWebhookを設定します。リソースオブジェクトのポインタが渡された後、webhookの設定のためにobject属性を調整します。

最終的なコードは以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/02266db4-e40d-6cb9-4baf-89a1633e13d2.png)


ステップ4では、SidecarSet mutating webhook、SidecarSet validating webhook、Pod mutating webhookの3つのwebhookを作成します。

前述の左図は、SidecarSet mutating webhookを示しています。デフォルトの設定はsetDefaultSidecarSetで完了します。

右図は、SidecarSetのフィールドをチェックするためのValidating webhookを示しています。

mutatingSidecarSetFnはデフォルト値を設定せず、setDefaultSidecarSetの値をフェッチし、その値をpodに注入します。

###ステップ6：コントローラーの設定
作成したコントローラフレームワークは、pkg/controller/sidecarset/sidecarset_controller.goにあります。以下の修正を行います。

- パーミッションのコメントを修正します。コントローラフレームワークは、//+kuberbuilder:rbac;groups=apps,resources=deployments/status,verbs=get;update;pathのようなコメントを自動的に作成します。必要に応じてコメントを修正し、ロールベースのアクセスコントロール（RBAC）ルールを作成します。
- キューイングロジックを追加します。デフォルトのコードフレームワークでは、CRDのキューイングロジックを記入します。例えば、SidecarSetオブジェクトの追加、削除、修正のアクションは、ワークキューに追加されます。例えば、SidecarSetがポッドの変更を監視することを要求するなど、関連するリソースオブジェクトのトリガーメカニズムが必要な場合は、手動でキューイングロジックを追加します。
- ビジネスロジックを入力します。Reconcile 関数を変更して、ワークキューを周期的に処理します。Reconcile 関数は、spec に基づいてビジネス・ロジックを実装し、ビジネス・ロジックの実装結果を status に返します。デフォルトでは、Reconcile関数から返されたエラーメッセージが再キューイングのトリガーとなります。

次の図は、SidecarSetコントローラの最終的なコードを示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/f3cdaa18-dc6a-dccc-818c-340bad4e9ee2.png)


addPodは、PodにマッチしたSidecarSetを取得し、Reconcile関数による処理のためにキューに入れるために使用されます。

SidecarSetを取得した後、Reconcile機能はSelectorの設定に基づいてマッチしたPodを選択し、現在のPodのステータスに基づいてクラスターのステータスを計算し、それに応じてCRDのステータスを記入します。

#3、SidecarSetのワークフロー
このセクションでは、Operatorの動作を理解するために、SidecarSetのワークフローをまとめます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/de6805ce-2508-2c49-fe4b-be9ee9dcb637.png)


1、SidecarSetを作成します。
2、SidecarSet作成リクエストを受信した後、Webhookがデフォルト設定を完了し、設定内容を確認します。その後、SidecarSetはデータベースに保存され、あなたに返却されます。
3、Podを作成します。
4、webhookは対応するSidecarSetをフェッチし、コンテナを取得し、Podに注入します。そのため、データベースに格納されているときのSidecarがPodに含まれています。
5、コントローラはバックグラウンドで常にポーリングを行い、クラスターの状態変化を監視します。ステップ4のインジェクションにより、SidecarSetのキューイングが開始されます。コントローラは、SidecarSetのUpdatedPodsに1を追加します。

以上のプロセスは、SidecarSetの簡単な実装です。

追加情報です。一般的に、webhookはコントローラを介してビジネスロジックを実装し、ステータスを更新します。ただし、コントローラがオプションになっている場合もあります。前述の例では、コントローラを介さずに webhook が主要なビジネスロジックを実装しています。



#4. まとめ
この記事で学んだことをまとめてみましょう。

- オペレータは、CRD、Webhook、コントローラと組み合わせて、[Kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.56016056cyOGOW)でユーザーのビジネスロジックを拡張するために使用されます。
- Kubebuilderは公式に標準化されたOperatorフレームワークであり、コミュニティで高く評価されています。
- この記事で紹介されている手順に基づいて、カスタムコードでOperatorを実装することができます。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/getting-started-with-kubernetes-%7C-operator-and-operator-framework_596320)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*