OpenShiftのカスタムコントローラーの標準形

Red HatでOpenShiftのサポートをしているid:nekopです。OpenShift 全部俺 Advent Calendar 2018 - Qiitaの15日目のエントリです。

前回はコントローラーのビルドの方法を解説しましたが、今回はもう少し実際の形のコントローラーがどのように構成されているかを見ていきます。

rheb.hatenablog.com

前回利用したサンプルコードexamples/out-of-cluster-client-configuration/main.goでは以下のようにシンプルにPodの一覧を取得するものでした。

clientset.CoreV1().Pods("").List(metav1.ListOptions{})

しかし、実際のコントローラーでは、最初にリストを取得し、以降は更新を検知して、それらをあるべき姿に更新する、current state -> desired stateという処理を行うループとなります。

この処理のサンプルがexamples/workqueue/main.goです。

ソースツリーのセットアップとビルド方法は前回と一緒なので省略します。今回はソースコードを見て行きますが、もしGo言語のソースコードを見る環境がない、という場合などはVSCodeを入れて該当ソースコードを開いて宣言部でF12キーを押せばジャンプできるので見やすいと思います。

初見だと結構読み解くのがつらい感じですが、sample-controllerのcontroller-client-go.mdというドキュメントにclient-goの動作の図解があります。

Informerというコンポーネントがclient-goサイドでリストの取得と更新検知を行ってくれます。

カスタムコントローラーを記述する側ではInformerにResourceEventHandlerFuncsを登録しておくことで、AddFunc, UpdateFunc, DeleteFuncがそれぞれコールバックされます。コールバックを受けたら一旦workqueueに置いて、カスタムコントローラー側はこのworkqueueを処理していく、というのがカスタムコントローラーの処理の基本形となります。

workquerueサンプルのコールスタックを簡略化すると以下のようになります。Controller.syncToStdoutがビジネスロジック部分となります。

- main
  - cache.NewIndexerInformer
  - Controller.Run
    - go informer.run
    - Controller.runWorker
      - Controller.processNextItem
        - workqueue.Get
        - Controller.syncToStdout
          - indexer.GetByKey(key)
        - workqueue.Done

- (go informer.run)
  - IndexerInformer
    - ResourceEventHandler
      - workqueue.Add

ビジネスロジック部分ではindexer.GetByKey(key)のようにIndexerを通してk8s objectを取得します。client-goのcache.NewIndexerInformerがこのInformerとIndexerを作成してくれます。

sample-controllerにもうちょっと応用的なコードがあり、こちらはcache.NewIndexerInformerではなくInformerFactoryを利用して複数のobjectのinformerを開始してハンドルするコントローラーとなっています。

また、コントローラーを作成する際のガイドラインがあるので、カスタムコントローラーを作成する場合は一度目を通しておく必要があります。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。