Upgrade to Pro — share decks privately, control downloads, hide ads and more …

controller-runtime Deep Dive

bells17
October 06, 2022

controller-runtime Deep Dive

Kubernetes Meetup Tokyo #53 ( https://k8sjp.connpass.com/event/259350/ ) のセッション資料です。
controller-runtimeのアーキテクチャや内部実装について解説しています。

セッション動画はこちらです。
https://youtu.be/jCyt993dzaU

以下スライドで紹介しているリンク:

controller-runtime clientについて: https://zenn.dev/bells17/articles/controller-runtime-client
controller-runtime: https://github.com/kubernetes-sigs/controller-runtime/tree/v0.12.3
aws-load-balancer-controller: https://github.com/kubernetes-sigs/aws-load-balancer-controller/tree/v2.4.4
kueue: https://github.com/kubernetes-sigs/kueue/tree/v0.2.1
Kubebuilder Book: https://book.kubebuilder.io/architecture.html
つくって学ぶKubebuilder: https://zoetrope.github.io/kubebuilder-training/
Ginkgo/GomegaによるKubernetes Operatorのテスト手法: https://zenn.dev/zoetro/books/testing-kubernetes-operator
Caching Unstructured Objects using controller-runtime: https://ymmt2005.hatenablog.com/entry/2021/07/25/Caching_Unstructured_Objects_using_controller-runtime
kubebuilder-declarative-pattern: https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern
kubebuilder: https://github.com/kubernetes-sigs/kubebuilder
controller-tools: https://github.com/kubernetes-sigs/controller-tools

aws-load-balancer-controller(Ingress Controller for AWS): https://github.com/kubernetes-sigs/aws-load-balancer-controller
kueue(Job Queueing): https://github.com/kubernetes-sigs/kueue
topolvm(CSI Driver for LVM): https://github.com/topolvm/topolvm
moco(MySQL Operator): https://github.com/cybozu-go/moco
logging-operator: https://github.com/banzaicloud/logging-operator
istio(Service Mesh): https://github.com/istio/istio

bells17

October 06, 2022
Tweet

More Decks by bells17

Other Decks in Programming

Transcript

  1. controller-runtime Deep Dive
    Kubernetes Meetup Tokyo #53 (2022/10/06)
    @bells17

    View Slide

  2. ▶ @bells17
    ▶ Software Engineer
    ▶ 普段やってること:
    + Kubernetes 関連コンポーネントの開発
    + Kubernetes as a Service開発
    + 社内におけるKubernetes普及活動
    ▶ Kubernetes SIG-Docs Japanese localization reviewer
    ▶ Kubernetes Internal Organizer
    ▶ #kubenews
    ▶ @bells17_

    View Slide

  3. #kubenews 隔週⾦曜22:00~YouTubeで配信中
    Kubernetes/Cloud Native関連のニュースを中⼼に技術雑談してます

    View Slide

  4. このセッションについて
    ▶ セッションを聴いてわかること
    + controller-runtimeのアーキテクチャ
    + controller-runtimeの内部実装
    + controller-runtimeのtips
    ▶ このセッションの⽬的
    + 既存のドキュメントではあまり解説されていないcontroller-runtimeのアーキテクチャや内部実装、tipsなどを
    まとめること
    + controller-runtimeの内部実装を把握することで開発者がcontroller-runtimeを拡張するための基礎知識を

    まとめること
    ▶ 対象とする視聴者
    + controller-runtimeを使ってアプリケーションを開発する⼈
    + Kubernetes関連アプリケーションのアーキテクチャや内部実装に興味のある⼈
    ▶ 視聴者に要求する前提知識
    + controller-runtimeの概要や基礎知識

    View Slide

  5. 注意点
    ▶ 対象バージョン: controller-runtime v0.12.3
    + https://github.com/kubernetes-sigs/controller-runtime/tree/v0.12.3
    ▶ あくまでcontroller-runtimeの実装を追った結果での理解の説明になるので、

    間違いが含まれている可能性があります
    ▶ 引⽤しているコードはスライドにうまく収めるため省略、⼀部書き換えを

    ⾏っている箇所があります

    View Slide

  6. controller-runtimeとは?

    View Slide

  7. controller-runtime
    ▶ Kubernetesの以下のようなアプリケーションを構築するためのフレームワーク
    + Kubernetes Operator(CRD+Controller)
    + Webhook Server(Validating/Mutating/Conversion)
    + その他k8s clientなどを使⽤したKubernetes向けアプリケーション
    ▶ client-go単体で使うよりも便利なので、Kubernetesに関するアプリケーションを構築するときには

    とりあえずcontroller-runtimeを使っておけばOK(拡張API Serverなど⼀部ケースでは向いてない)
    ▶ kubebuilder/operator-sdkなどのフレームワークの内部で利⽤されている
    ▶ 上記のフレームワークを使わずに、controller-runtime単体での使⽤も可能
    ▶ 以下のようなKubernetesを使ったアプリケーション構築に必要なものが⼀通り

    含まれている
    + Controller実⾏を含めたrunnerの管理機能
    + Webhook Server
    + Prometheus Server
    + cache機能付きk8s clientの提供
    + e2eテスト環境構築ツール(envtest)

    View Slide

  8. controller-runtimeの利⽤例
    ▶ aws-load-balancer-controller(Ingress Controller for AWS): https://
    github.com/kubernetes-sigs/aws-load-balancer-controller
    ▶ kueue(Job Queueing): https://github.com/kubernetes-sigs/kueue
    ▶ topolvm(CSI Driver for LVM): https://github.com/topolvm/topolvm
    ▶ moco(MySQL Operator): https://github.com/cybozu-go/moco
    ▶ logging-operator: https://github.com/banzaicloud/logging-operator
    ▶ istio(Service Mesh): https://github.com/istio/istio
    ▶ etc

    View Slide

  9. controller-runtimeの使い⽅を学ぶ
    ▶ controller-runtime(kubebuilder)の使い⽅については以下のような充実した

    ドキュメントが公開されている
    + Kubebuilder Book
    + つくって学ぶKubebuilder
    + Ginkgo/GomegaによるKubernetes Operatorのテスト⼿法
    ▶ controller-runtimeについて詳しく知りたい⽅は上記ドキュメントと合わせて
    このセッション資料を⾒てもらえると

    View Slide

  10. controller-runtimeのアーキテクチャ

    View Slide

  11. controller-runtime architecture overview
    画像はKubebuilder Book( https://book.kubebuilder.io/architecture.html ) より引⽤
    ▶ Manager:

    実⾏する各種runnerやclientなどを管理
    ▶ Client & Cache:

    client-goをベースにしたcache機能付きの

    独⾃k8s clientを提供
    ▶ Controller:

    ユーザーが実装したReconcilerを実⾏
    ▶ Webhook:

    ユーザー定義に基づくWebhook Serverを

    実⾏
    ※ runner:
    「各種Controller/Serverなどを含めたManagerが管理
    する処理の実⾏単位」くらいに考えてもらえればOK
    ユーザー独⾃のrunnerを実⾏することも可能

    View Slide

  12. Reconciler & Controller

    View Slide

  13. kubebuilderでControllerを⽣成すると
    $ kubebuilder create api --group sample-operator --version
    v1beta1 --kind Sample


    Create Resource [y/n]


    y


    Create Controller [y/n]


    y


    View Slide

  14. CRDが⽣成され
    // SampleSpec defines the desired state of Sample


    type SampleSpec struct {


    Message string `json:"message,omitempty"`


    }


    // SampleStatus defines the observed state of Sample


    type SampleStatus struct {


    Message string `json:"message,omitempty"`


    }


    // Sample is the Schema for the samples API


    type Sample struct {


    metav1.TypeMeta `json:",inline"`


    metav1.ObjectMeta `json:"metadata,omitempty"`


    Spec SampleSpec `json:"spec,omitempty"`


    Status SampleStatus `json:"status,omitempty"`


    }

    View Slide

  15. Reconcilerが⽣成されて
    // SampleReconciler reconciles a Sample object


    type SampleReconciler struct {


    client.Client


    Scheme *runtime.Scheme


    }


    // Reconcile is part of the main kubernetes reconciliation loop which aims to


    // move the current state of the cluster closer to the desired state.


    func (r *SampleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {


    _ = log.FromContext(ctx)


    // TODO(user): your logic here


    return ctrl.Result{}, nil


    }


    // SetupWithManager sets up the controller with the Manager.


    func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager) error {


    return ctrl.NewControllerManagedBy(mgr).


    For(&sampleoperatorv1beta1.Sample{}).


    Complete(r)


    }

    View Slide

  16. main.goのコードが⾃動でアップデートされる
    func init() {


    utilruntime.Must(clientgoscheme.AddToScheme(scheme))


    utilruntime.Must(sampleoperatorv1beta1.AddToScheme(scheme))


    //+kubebuilder:scaffold:scheme


    }


    func main() {





    if err = (&controllers.SampleReconciler{


    Client: mgr.GetClient(),


    Scheme: mgr.GetScheme(),


    }).SetupWithManager(mgr); err != nil {


    setupLog.Error(err, "unable to create controller", "controller", "Sample")


    os.Exit(1)


    }


    //+kubebuilder:scaffold:builder


    }

    View Slide

  17. controllerの登録と実⾏の流れ

    View Slide

  18. controller登録の流れ
    ▶ Controllerを⽣成してmgr.Add()でManagerのrunnerに登録
    ▶ 対象リソースの変更監視を設定: SharedInformerのEventHandlerを追加
    ▶ controller-runtime Managerの起動時にControllerを起動

    View Slide

  19. controller実⾏の流れ
    ▶ SharedInformerがKube API Serverから対象リソースをList & Watchで監視
    ▶ 対象リソースの変更時に登録したEventHandlerでイベントを受信
    ▶ 受信したイベントをPredicatesでフィルタリング
    ▶ フィルタリングしたイベントをEventHandlerでWorkQueueのイベントに変換
    ▶ ControllerがWorkQueueに溜まっているイベントを元にReconcilerを実⾏

    View Slide

  20. Reconciler & Controllerまとめ
    ▶ Reconciler: controller-runtimeを使って開発者が実装する調整ループの中⾝
    ▶ Controller: 作成したReconcilerやPredicateなどの設定を元にcontroller-runtimeが
    ⽣成~実⾏制御を⾏うもの
    ▶ Controllerの起動はmgr.Add()によってManagerに管理される
    controller-runtimeにおいては開発者が実装するのはReconcilerとControllerの設定のみ
    Controller本体はcontroller-runtimeが⽣成や管理を⾏ってくれる

    View Slide

  21. Managerについて

    View Slide

  22. Manager(controller-runtime) overview

    View Slide

  23. Manager(controller-runtime)の全体像
    ▶ Managerを通してアプリケーション全体を管理するのでManager=controller-runtimeと思ってもらってOK
    ▶ ManagerはRunnerという仕組みを中⼼成り⽴っている
    ▶ Runnerの登録はmgr.Add()というメソッドで⾏うことができる
    ▶ Runner: Runnableというinterfaceを満たす実装を予め登録しておき、Manager起動時に登録した実装を実
    ⾏する機能
    ▶ Runnerの種類: Runnerはどのようなinterfaceを満たすかに応じて4種類に分類される
    + Webhooks: Webhookサーバーへのhandlerの登録処理を⾏う
    + Caches: Informerの起動を⾏う
    + LeaderElections: LeaderElectionでリーダー選出された際のみ実⾏されるRunner
    + Others: LeaderElectionによるリーダー選出に関わらず実⾏されるRunner
    ▶ 以下のServerはRunnerとは独⽴して実⾏される
    + Metric Server: controller-runtimeのメトリクスを取得するためのprometheus server
    + Health Prove Server: 任意のcheckerを設定できるヘルスチェック⽤サーバー

    View Slide

  24. Webhook Runners
    ▶ Webhook Runnerは基本的にmgr.GetWebhookServer()によってWebhook Serverを⽣成
    したときのみに設定されるRunner種別
    ▶ Webhook ServerはRunnerの仕組みを通して起動される
    ▶ Webhook ServerにはCertWatcherという機能があり、SSL証明書ファイルが更新された
    際に⾃動でリロードする仕組みが提供されている

    View Slide

  25. Cache Runners
    ▶ Cache Runnersは基本的にManagerが内部で⽣成するclusterオブジェクトのみに設定
    されるRunner種別
    ▶ 設定したInformerを起動しKube API Serverからのリソース監視処理を開始するために
    利⽤される

    View Slide

  26. LeaderElection Runners
    ▶ LeaderElectionでリーダー選出された際にのみ実⾏されるRunner
    ▶ Runnerはデフォルトでこの種類のRunnerとして実⾏される
    ▶ LeaderElectionでリーダー選出された際に実⾏されるため、この種別のRunnerが起動
    されるのはManagerの起動時(mgr.Start()を呼び出したとき)ではなく、Leader選出され
    た際になる

    View Slide

  27. Other Runners
    ▶ LeaderElectionによるリーダー選出に関わらず実⾏されるRunner
    ▶ この種類のRunnerとして登録したい場合は、NeedLeaderElectionメソッドを実装し、
    このメソッドでfalseを返すようにする必要がある

    View Slide

  28. Metric Server
    ▶ Metrics Serverはcontroller-runtimeのメトリクスを設定されたPrometheus Server
    ▶ そのためmetrics.Registry.MustRegisterを呼び出し、このMetrics Serverに

    カスタムメトリクスを設定することができる

    View Slide

  29. Health Probe Server
    ▶ Health Probe Serverはヘルスチェック⽤のHTTPサーバー
    ▶ `/healthz`と`/readyz`の2つのヘルスチェック⽤のエンドポイントを提供している
    ▶ ヘルスチェック処理は予め登録したcheckerによるチェックをパスしたかどうかで判定
    される仕組みになっている
    ▶ このcheckerはそれぞれ、AddHealthzCheckとAddReadyzCheckという2つメソッドに
    よって登録することができるようになっている

    View Slide

  30. Manager(controller-runtime)まとめ
    ▶ ManagerはRunnerという仕組みを中⼼成り⽴っていて、Manager起動時にRunnerとし
    て登録したアプリケーション群の実⾏を⾏う仕組みになっている
    ▶ Runnerには4つ種類がある
    ▶ Runnerの内、LeaderElectionsはManager起動後、Leader選出された際に実⾏される
    ▶ その他のRunnerはManager起動時に実⾏される
    ▶ Metric ServerとHealth Probe ServerはRunnerとは独⽴した仕組みで実⾏される

    View Slide

  31. k8s client

    View Slide

  32. k8s client
    ▶ controller-runtimeではclient-goをベースとした3種類のk8s clientが提供されている
    ▶ k8s clientの種類は以下の通り
    + Delegating Client: 読み取り系の処理時にCache clientを利⽤するクライアント
    + Cache Client: Informerを通したローカルキャッシュからデータ取得を⾏う

    reader
    + API Reader: キャッシュを使⽤せず都度API Serverにリクエストを送りデータ取得を⾏
    うreader
    ▶ そのため基本的には以下のようにclientを使い分けておけばOK
    + Delegating Client: 特に理由がなければこのクライアントを利⽤する
    + API Reader: 最新のデータ取得を確実に⾏いたい際に利⽤
    + Cache Client: Delegating Client内部で使⽤されているので基本的に利⽤する必要無し

    View Slide

  33. Delegating ClientとCache Clientはオプションから独⾃クライアントを
    使⽤可能
    func main() {


    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{


    NewCache: cache.New,


    NewClient: cluster.DefaultNewClient,





    })


    if err != nil {


    setupLog.Error(err, "unable to start manager")


    os.Exit(1)


    }


    }
    ※ API Readerについては独⾃クライアントへの上書きはできない

    View Slide

  34. 各clientは以下のように取得することが可能
    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{})


    if err != nil {


    setupLog.Error(err, "unable to start manager")


    os.Exit(1)


    }


    delegatedClient := mgr.GetClient()


    cache := mgr.GetCache()


    apireader := mgr.GetAPIReader()


    View Slide

  35. client-go overview
    画像引⽤元: https://github.com/kubernetes/sample-controller/blob/v0.25.0/docs/images/client-go-controller-interaction.jpeg
    ▶ Re
    fl
    ector: API Serverから指定リソースをList & Watch
    で取得 & 以降の変更を監視
    ▶ Delta Fifo queue: Re
    fl
    actorが取得したデータをqueue
    として格納
    ▶ Informer: Delta Fifo queueからデータを取り出して
    1. データをIndexerへと渡す
    2. Informerに設定された各EventHandlerを呼び出す
    ▶ Indexer: 渡されたデータをインメモリにキャッシュ

    (structのmapにデータを設定するだけ)
    Kubernetes ControllerはInformerのEventHandlerに

    それぞれのhandlerを設定することでイベント受信して

    動作するよう振る舞うが、ここではclientがメインなので
    以降の流れは省略

    View Slide

  36. controller-runtime client overview
    ▶ Delegating Client:

    Cache ClientをReaderとして

    Default ClientをWriterとしてそれぞれ使⽤
    ▶ Cache Client: リソースごとの各SharedInformerを

    データソースとしてデータを取得するクライアント
    ▶ Base Client:

    Delegating ClientのWriterとして使⽤される

    内部クライアント

    API ReaderはこのBase ClientをReaderとして使⽤
    したもの(structとしては同じもの)
    ▶ Rest Client:

    実際にAPI Serverにhttp(s) requestを送るclient

    View Slide

  37. controller-runtime object
    controller-runtimeのclientは内部で更に3種類のclientを保持している

    これらのclientを渡されたObjectに応じて⾃動で使い分ける仕組みになっている

    View Slide

  38. clientに渡すObjectに応じて内部clientは⾃動で切り替わる
    client := mgr.GetClient()


    // use structured client


    podList := corev1.PodList{}


    err := client.List(ctx, podList)


    // use unstructured client


    uList := unstructured.UnstructuredList{}


    uList.SetGroupVersionKind(corev1.GroupVersion.WithKind("PodList"))


    err := client.List(ctx, podList)


    // use metadata client


    pList := metav1.PartialObjectMetadataList{}


    pList.SetGroupVersionKind(corev1.GroupVersion.WithKind("PodList"))


    err := client.List(ctx, podList)

    View Slide

  39. k8s client object
    ▶ Structured: PodやPodListなどの各リソースごとのstruct
    ▶ Unstructured: "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" にある以下のstruct
    + Unstructured: Podなどのリソース単体向けに使⽤
    + UnstructuredList: PodListなどのListリソース向けに使⽤
    + Unstructured ObjectはStructured Objectのようにリソースの取得・更新など諸々の操作を
    ⾏うことが可能なObject
    ▶ Metadata: "k8s.io/apimachinery/pkg/apis/meta/v1" にある以下のstruct
    + PartialObjectMetadata: Podなどのリソース単体向けに使⽤
    + PartialObjectMetadataList: PodListなどのListリソース向けに使⽤
    + Metadata Objectはリソースのmetadata取得・更新にのみ利⽤できる

    View Slide

  40. scheme.Convert()を利⽤することで異なるObjectのデータを

    変換することができる
    client := mgr.GetClient()


    uList := unstructured.UnstructuredList{}


    uList.SetGroupVersionKind(corev1.GroupVersion.WithKind("PodList"))


    err := client.List(ctx, uList)


    if err != nil {


    return err


    }


    podList := corev1.PodList{}


    err = mgr.GetScheme().Convert(uList, podList, nil)


    if err != nil {


    return err


    }

    View Slide

  41. Unstructured(List)とMetadata(List)はGVKによってリソース種別を

    判定している
    u := unstructured.Unstructured{}


    u.SetGroupVersionKind(schema.GroupVersionKind{


    Group: "networking",


    Version: "v1",


    Kind: "Ingress",


    })


    p := metav1.PartialObjectMetadata{}


    u.SetGroupVersionKind(schema.GroupVersionKind{


    Group: "",


    Version: "v1",


    Kind: "Pod",


    })
    ※ Structured Objectはre
    fl
    ect.ValueOf(obj).Elem().Type() で取得できるObjectの re
    fl
    ect.Type をキーにリソースを判定している

    View Slide

  42. つまり、scheme.Convert()・GVK・Unstructuredを利⽤して異なる
    Structured Objectを変換可能
    client := mgr.GetClient()


    ingress := networkingv1.Ingress{}


    err := client.Get(ctx, types.NamespacedName{Name: "ingress", Namespace: "default"} ingress)


    if err != nil {


    return err


    }


    u := unstructured.Unstructured{}


    err = mgr.GetScheme().Convert(ingress, u, nil)


    if err != nil {


    return err


    }


    ingressbeta := networkingv1beta1.Ingress{}


    u.SetGroupVersionKind(networkingv1beta1.GroupVersion.WithKind("Ingress"))


    err = mgr.GetScheme().Convert(u, ingressbeta, nil)


    if err != nil {


    return err


    }
    ※ CRDのgroup名をrenameしたときになどはこの⽅法を使うと便利だった

    View Slide

  43. Unstructured Objectのキャッシュを有効化するには
    CacheUnstructuredオプションを有効化する必要がある
    func NewCustomDelegatingClient(


    cache cache.Cache,


    config *rest.Config,


    options client.Options,


    uncachedObjects ...client.Object


    ) (client.Client, error) {


    c, err := client.New(config, options)


    if err != nil {


    return nil, err


    }


    return client.NewDelegatingClient(client.NewDelegatingClientInput{


    CacheReader: cache,


    Client: c,


    UncachedObjects: uncachedObjects,


    CacheUnstructured: true // enable caching for unstructured objects


    })


    }


    func main() {


    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{


    NewClient: NewCustomDelegatingClient,





    })


    if err != nil {


    setupLog.Error(err, "unable to start manager")


    os.Exit(1)


    }


    }

    View Slide

  44. k8s clientまとめ
    ▶ controller-runtimeでは以下の3種類のclientが提供されている
    + Delegating Client: Readerのみキャッシュが利⽤されるクライアント
    + Cache Client: SharedInformerを利⽤したキャッシュクライアント
    + API Reader: 都度同期的にAPI Serverにリクエストするクライアント
    ▶ Delegating ClientとCache Clientはオプションで独⾃クライアントに上書き可能
    ▶ Client objectには以下の3種類が提供されている
    + Structured
    + Unstructured
    + Metadata
    ▶ Structured ObjectやUnstructured Objectはscheme.Convert()で相互変換が可能
    ▶ Unstructured ObjectとMetadata Clientは内部のGVK情報によってGVKを管理
    ▶ Structured Objectはstructのre
    fl
    ect.TypeをキーにGVKを管理
    ▶ `CacheUnstructured: true` に設定することでUnstructured Objectのキャッシュが可能になる

    View Slide

  45. より詳しい内容を知りたい⽅はこちら
    https://zenn.dev/bells17/articles/controller-runtime-client

    View Slide

  46. envtestについて

    View Slide

  47. envtest
    ▶ controller-runtimeに付属するe2eテストツール
    ▶ 指定Kubernetesバージョンのetcd/API Serverのダウンロード~起動・設定を⾏ってくれる
    ▶ controller-managerや実際のnodeなどが必要なe2eテストを動かしたい場合は実際のk8s
    クラスターを⽤意する必要あり
    + `USE_EXISTING_CLUSTER ` 環境変数を使うことで、構築済クラスターをe2eテストに
    利⽤することが可能

    View Slide

  48. envtest overview
    ▶ setup-envtest

    指定Kubernetesバージョンの

    etcd/API Serverなどのダウンロードを⾏う
    ▶ envtest.Environment.Start

    以下のようなAPI Serverを使ったKubernetes
    Operatorのe2eテスト環境のセットアップを⼀通り
    ⾏ってくれる
    + KUBEBUILDER_ASSETSを通して渡された

    パスにあるetcd/API Serverの起動
    + CRDのインストール
    + デフォルトユーザーの追加
    + Webhook設定
    ▶ envtest.Environment.Stopで起動した

    etcd/API Serverの停⽌が可能

    View Slide

  49. Tips:

    対象バージョンのkubectlの使⽤や独⾃ユーザーの設定などが可能
    testEnv = &envtest.Environment{


    CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},


    ErrorIfCRDPathMissing: true,


    }


    var err error


    cfg, err = testEnv.Start()


    Expect(err).NotTo(HaveOccurred())


    Expect(cfg).NotTo(BeNil())


    // use kubectl cmd


    testEnv.ControlPlane.KubeCtl().Run("get", "nodes")


    // add user & use kubectl cmd


    user, err := env.ControlPlane.AddUser(envtest.User{


    Name: "envtest-admin",


    Groups: []string{"system:masters"},


    }, nil)


    Expect(err).NotTo(HaveOccurred())


    kubectl, err := user.Kubectl()


    Expect(err).NotTo(HaveOccurred())


    kubectl.Run("get", "nodes")

    View Slide

  50. その他

    View Slide

  51. 設定ファイルの拡張
    type customConfig struct {


    metav1.TypeMeta `json:",inline"`


    configv1alpha1.ControllerManagerConfigurationSpec `json:",inline"`


    CustomValue string `json:"customValue"`


    }


    func main() {


    options := ctrl.Options{


    Scheme: scheme,


    HealthProbeBindAddress: ":8081",


    MetricsBindAddress: ":8080",


    Port: 9443,


    }


    var err error


    config := customConfig{}


    options, err = options.AndFrom(ctrl.ConfigFile().AtPath("./config.yaml").OfKind(&config))


    if err != nil {


    setupLog.Error(err, "unable to load the config file")


    os.Exit(1)


    }


    fmt.Println(config.CustomValue)


    }
    # ./config.yaml


    apiVersion: controller-runtime.sigs.k8s.io/v1alpha1


    kind: CustomControllerManagerConfiguration


    customValue: foo


    port: 9442
    controller-runtimeの設定ファイルを拡張して独⾃設定を渡せるので、設定の数が多いときに便利

    View Slide

  52. Controller:
    Reconcile対象以外のリソースからのイベントを元に調整ループを実⾏
    func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager) error {


    handler := &eventHandler{}


    return ctrl.NewControllerManagedBy(mgr).


    For(&sampleoperatorv1beta1.Sample{}).


    Watches(&source.Kind{Type: &corev1.Service{}}, handler).


    Complete(r)


    }


    type eventHandler struct {}


    func (h *eventHandler) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {


    svc := e.Object.(*corev1.Service)


    queue.Add(reconcile.Request{NamespacedName: types.NamespacedName{


    Namespace: svc.Namespace,


    Name: svc.Name,


    }})


    }


    func (h *eventHandler) Update(e event.UpdateEvent, queue workqueue.RateLimitingInterface) {}


    func (h *eventHandler) Delete(e event.DeleteEvent, queue workqueue.RateLimitingInterface) {}


    func (h *eventHandler) Generic(e event.GenericEvent, queue workqueue.RateLimitingInterface) {}
    上記の例の場合、Serviceリソースが作成されたら、ServiceリソースのName/NamespaceをキーにSample CRDのReconcileを実⾏することになる

    View Slide

  53. Controller:
    Goチャネルからのイベントを元に調整ループを実⾏
    func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager) error {


    handler := &eventHandler{}


    evt := make(chan event.GenericEvent)


    return ctrl.NewControllerManagedBy(mgr).


    For(&sampleoperatorv1beta1.Sample{}).


    Watches(&source.Channel{Source: evt}, handler).


    Complete(r)


    }


    type eventHandler struct {}


    func (h *eventHandler) Create(e event.CreateEvent, queue workqueue.RateLimitingInterface) {}


    func (h *eventHandler) Update(e event.UpdateEvent, queue workqueue.RateLimitingInterface) {}


    func (h *eventHandler) Delete(e event.DeleteEvent, queue workqueue.RateLimitingInterface) {}


    func (h *eventHandler) Generic(e event.GenericEvent, queue workqueue.RateLimitingInterface) {


    queue.Add(reconcile.Request{NamespacedName: types.NamespacedName{


    Namespace: e.Object.GetNamespace(),


    Name: e.Object.GetName(),


    }})


    }
    上記の例の場合、Serviceリソースが作成されたら、ServiceリソースのName/NamespaceをキーにSample CRDのReconcileを実⾏することになる

    View Slide

  54. まとめ

    View Slide

  55. まとめ
    ▶ controller-runtimeのアーキテクチャや内部実装の概要、tipsについてまとめました
    ▶ 独⾃クライアントを使えるようにしたりといったcontroller-runtimeを拡張して使ってい
    くためのベースとなる知識もいくつか紹介できたと思います

    View Slide

  56. 参考資料
    ▶ controller-runtime clientについて: https://zenn.dev/bells17/articles/controller-runtime-client
    ▶ controller-runtime: https://github.com/kubernetes-sigs/controller-runtime/tree/v0.12.3
    ▶ aws-load-balancer-controller: https://github.com/kubernetes-sigs/aws-load-balancer-controller/tree/v2.4.4
    ▶ kueue: https://github.com/kubernetes-sigs/kueue/tree/v0.2.1
    ▶ Kubebuilder Book: https://book.kubebuilder.io/architecture.html
    ▶ つくって学ぶKubebuilder: https://zoetrope.github.io/kubebuilder-training/
    ▶ Ginkgo/GomegaによるKubernetes Operatorのテスト⼿法: https://zenn.dev/zoetro/books/testing-kubernetes-operator
    ▶ Caching Unstructured Objects using controller-runtime: https://ymmt2005.hatenablog.com/entry/2021/07/25/
    Caching_Unstructured_Objects_using_controller-runtime
    ▶ kubebuilder-declarative-pattern: https://github.com/kubernetes-sigs/kubebuilder-declarative-pattern
    ▶ kubebuilder: https://github.com/kubernetes-sigs/kubebuilder
    ▶ controller-tools: https://github.com/kubernetes-sigs/controller-tools

    View Slide

  57. Thanks / Question?
    ▶ @bells17
    ▶ Slide: https://speakerdeck.com/bells17
    ▶ @bells17_

    View Slide