1クール続けるブログ

とりあえず1クール続けるソフトウェアエンジニアの備忘録

kubebuilderを利用して簡素なk8sのControllerを作ってみる

記事一覧はこちら

背景・モチベーション

Controllerを書きたい!とはずっと思っていましたが、なかなか一歩を踏み出せませんでした。 しかしながら、external-dnsのコードを読んでふんわり分かってきたので書き始めようと思います。

肝心なControllerの中身ですが、検証環境のコスト削減に役立つものにしたいと思います。Deploymentリソースのアノテーションに、起動する時間と落とす時間を宣言しておくと、そのとおりにpodをスケールアウト/インしてくれるものを書こうと思います。

Controllerを作るときの指針

参考文献

kubebuilder book

つくって学ぶKubebuilder (母国語かつ無料で読める質とは思えないくらいにとても良いドキュメント様です)

Bitnamiさんの記事

Kubernetes公式リポジトリにあるGuideline

Controllerの作り方について

external-dns や Bitnamiさん の記事を読んでいると client-go のライブラリをそのまま利用してControllerが実装されています。しかしながら、Bitnamiさんの記事は少し古く2017年時点でのものでした。 しかし近年は、 kubebuilder というフレームワークを利用することが多いようです。 aws-load-balaner-controllerkubebuilder をベースに作成されていました。

Controllerの仕事

この部分は、k8sのcommunityのリポジトリ作って学ぶKuberbuilderさんdeeeetさんのブログ記事 で触れられています。

Controllerの仕事は、任意のオブジェクトについて、現在の状態が望ましい状態と一致することを保証することです。各Controllerはrootとなる一つのKindにフォーカスしますが、他のkindと相互に作用することもあります。 これらの処理を reconciling と言うそうな。

(…そう思うと今回実装しているのって厳密にControllerと言えるものなのだろうか)

// reconciling loop
for {
  desired := getDesiredState()
  current := getCurrentState()
  makeChanges(desired, current)
}

kubebuilderを利用して雛形を作り不要なものを捨てる

kubebuilderは、CRDやAdmission Webhookを作成することなども想定してコードを生成しています。ただ、今回の自分の用途の場合にはCRDやAdmission Webhookは必要としません。主に検証環境で利用する用途のため、冗長性を確保するためのLeader Electionも今回は利用しません。 そのため不要な生成済コードは取り除くことにします。

生成されたコードの役割はパッと見ではわかりません。公式のドキュメントで一箇所にまとめてある記述が見つからなかったので、作って学ぶKunebuilderさんのこのページを参考にしました。

kubebuilder init --domain ars.44smkn.github.io
kubebuilder create api --group apps --version v1 --kind Deployment --namespaced=false --resource=false --controller=true

雛形を作ったので実装していきます。

Controllerの実装

github.com

実装の大枠

Controllerを管理してくれる部分は、前述の kubebuilder コマンドによって既に作成済です。 controller-runtime によって提供された Manager が、全てのControllerの実行、共有キャッシュの設定、APIサーバーへのクライアントの設定を管理してくれます。 生成された main.go 内にてManagerのインスタンス化と Reconciler のセットアップが行われています。

func main() { 
    // ...
    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
        Scheme:             scheme,
        MetricsBindAddress: metricsAddr,
        Port:               9443,
        LeaderElection:     false,
        LeaderElectionID:   "08a80630.ars.44smkn.github.io",
    })
    // ...
    if err = (&controllers.DeploymentReconciler{
        Client: mgr.GetClient(),
        Log:    ctrl.Log.WithName("controllers").WithName("Deployment"),
        Scheme: mgr.GetScheme(),
    }).SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "Deployment")
        os.Exit(1)
    }
    // ...
}

では、どこにコードを書いていくことになるかと言うと、 controller ディレクトリ配下の xxxxx_controller.goReconcile メソッドの中です。 下記のようにコメントしてあるので分かりやすいです。

// your logic here

Reconcile が呼び出されるのは、下記のタイミングだそうです。 こちら より引用させていただきました。

  • コントローラが扱うリソースが作成、更新、削除されたとき
  • Reconcileに失敗してリクエストが再度キューに積まれたとき
  • コントローラの起動時
  • 外部イベントが発生したとき
  • キャッシュを再同期するとき(デフォルトでは10時間に1回)

このタイミングで自分の実装ではDeploymentのアノテーションを読み取り、replicas数を操作する処理をcronに登録します。

func (r *DeploymentReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
    // ...
 
    // Reconcilerに生えている Getメソッドを利用してイベントがあったリソースを取得する
    var deployment appsv1.Deployment
    err := r.Get(ctx, req.NamespacedName, &deployment)
    if err != nil {
        return ctrl.Result{}, err
    }
 
    // ...(アノテーションのパースなどをする)
    return ctrl.Result{}, nil
}

動作確認

ローカルでは Kind の環境を利用して動作確認しました。
その後にRBACのマニフェスト等を整備して、Katacodaにデプロイして動くことを確認できました!気持ちいい!

$ kubectl apply -f mf.yaml
serviceaccount/app-regulary-scaler created
clusterrole.rbac.authorization.k8s.io/app-regulary-scaler created
clusterrolebinding.rbac.authorization.k8s.io/app-regulary-scaler created
deployment.apps/controller-manager created
$ kubectl get po
NAME                                  READY   STATUS    RESTARTS   AGE
controller-manager-75ccf997dd-jl426   1/1     Running   0          63s

# 1分後にreplicas数が2になる、replicas数1を宣言しているnginxのマニフェスト
$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment created
$ kubectl get po
NAME                                  READY   STATUS    RESTARTS   AGE
controller-manager-75ccf997dd-jl426   1/1     Running   0          4m34s
nginx-deployment-6b474476c4-nrjsm     1/1     Running   0          3m15s
nginx-deployment-6b474476c4-s55xm     1/1     Running   0          2m9

README.mdやリリースをちゃんと整備して仕事の環境とかで使えると便利だなあと思いました!以上!