Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

Kubernete Meetup Tokyo #18 - Kubebuilder/controller-runtime 入門

4.838 visualizaciones

Publicado el

Kubernetesの近年の大きなbreakthroughの一つにCRD & Controllerを使った拡張があります。このセッションではCRD & Custom Controller開発を始めようと思っている人向けに、Kubernetesコミュニティで開発されているCustom Controller向けSDKであるkubebuilder, controller-runtimeについての解説を行います。

Publicado en: Software
  • Sé el primero en comentar

Kubernete Meetup Tokyo #18 - Kubebuilder/controller-runtime 入門

  1. 1. Shingo Omura (@everpeace), Preferred Networks, Inc. Kubernetes Meetup Tokyo #18 2019-04-22 Kubebuilder/controller-runtime入門
  2. 2. 大村伸吾 (おおむらしんご) • エンジニア, Preferred Networks, Inc. • 社内向けGPUクラスタの開発運用 • kubeflow contributor • everpeace/kube-throttler – scheduler extender として 動作 実行中Podの利用リソース量をthrottle • @everpeace 2 We’re Hiring!!
  3. 3. • CRD ベース の controller を作るためのフレームワーク • kubebuilder 単体は コードテンプレ/manifest 群を生成してくれるCLIツール • book.kubebuilder.io がとても良く書かれていてるのでちゃんと読み進めれば 特に詰まることなく使えます Kubebulider (v1.0.8が最新) kubernetes-sigs/kubebuilder Kubebuilder Icons made by Icongeek26 from www.flaticon.com is licensed by CC 3.0 BY *.go *.yaml controller-runtime controller-tools ● CRD, controller(deploy用), controller用のRBAC用 manifest を生成してくれる ● //+ annotation による冪等 な manifest 生成が可能 ● controller-manager, controller, webhook 用のコード生成(テスト有) − controller 部分 は CRD 単位で後から追加できる ● controllerを書くには結局controller-runtimeを勉強する必要がある ● kubebuilderアップグレード時に依存(vendor) 管理もしてくれる 生成 生成 依存 3
  4. 4. Kubebuilder の作る Project の 構造 cmd/…,docs/…,Makefile,Dockerfile pkg/apis/.../*_types.go,*_types_test.go pkg/controllers/.../*-controller.go,*-controller_test.go config/{crds,rbac,rbac_proxy,manager,default,sample}/*.yaml Icons made by Icongeek26 from www.flaticon.com is licensed by CC 3.0 BY − API resource の定義. kind毎に生成される − 生成されるテンプレートを元に定義したい spec, status とそのテストを書く その他もろもろ − Controller の実装. kind毎に生成される − 生成されるテンプレートを元に controller-runtime を使って実装/テストを書く − 各種 manifest ファイル, kustomize の app が生成される − ソースに書いたアノテーションを元に controller-tools が生成してくれる 最初大変 4
  5. 5. Kubebuilder の 嬉しみ と 辛み Icons made by Roundicons from www.flaticon.com is licensed by CC 3.0 BY ● 使い始めるのが簡単 ● コピペしなくて済む (大事!) ● kubebuilder/make による標準的な開発 フローにで toil を最小化 ○ kubebuilder create api ○ make manifests/test/install/deploy ● 標準的な Controller 実装 に乗れるので コアロジック(Reconcileループ)に集中 ● controller-runtimeによる抽象度の高い ライブラリが使える ● controller の integration test もついてくる ● code 生成は 初回生成のみを想定 ○ 書換NGな場所をfencingしてくれている訳じゃ ないので基本 1 度生成したらおしまい ● CRD の Webhook Conversion は未対応 なので理解して使うべき (#275) ● probe のサポートがない (#297) ● Admission系が大規模にrefactoringされて いるのでもし使うなら将来影響あり ● controller-runtime は godoc くらいしかな いので 学習コストがそこそこある 5
  6. 6. controller-runtime 入門 実際にController 書く時に 理解しておいたほうが良い ことを解説していきます!
  7. 7. controller-runtime(v0.1.10が最新) kubernetes-sigs/controller-runtime • Kubebuilderのサブプロジェクトとして開発されている • Kubebuilder v1.0.8 は controller-runtime v0.1.1 に依存 (古め) – 最新版にupdateするPR (kubebuilder#657) はある cache by SBTS from the Noun Project Icons made by Vincent Le Moign from https://icon-icons.com/ licensed by CC 3.0 BY Icons made by Gregor Cresnar, Pause08, Freepik, Those Icons, Monkik from www.flaticon.com is licensed by CC 3.0 BY Controller Admission Webhook envtest metrics その他 client, cache, logging, leader election (resource lock) Manager controller-runtimeの主な機能 : 本日は時間都合で省略する部分 7
  8. 8. Manager (a.k.a controller manager) • Runnable の lifecycle管理 (Add / Start) – Manager 経由で Start しないと ダメなので注意!! (共通依存の処理が色々必要) • 共通依存を保持: client, cache, scheme 等 – getterが提供されている(GetClient()とか) – 簡易DIの仕組みもある(runtime/inject) • Leader Election にも対応 – optionで指定するだけでOK (default false) • Graceful shutdown用のsignal handlerも完備 Controller Admission Webhook Manager Runnable * 依存を注入 8
  9. 9. runtime/inject による依存の注入 • 特定のinterfaceを実装しておくとmanagerへの追加時/controller生成時 に注入してくれる package runtime/inject // 下記の依存に対応 type Cache interface { InjectCache(cache cache.Cache) error } type Client interface { InjectClient(client.Client) error } type Decoder interface { InjectDecoder(types.Decoder) error } type Scheme interface { InjectScheme(scheme *runtime.Scheme) error } type Stoppable interface { InjectStopChannel(<-chan struct{}) error } // graceful shutdown用 type Injector interface { InjectFunc(f Func) error } // injector そのもの type Logger interface { InjectLogger(l logr.Logger) error } // v0.1.1には無い // 利用例 type myReconciler struct { client.Client } func (*r myReconciler) InjectClient(c client.Client) error { r.Client = c } 9
  10. 10. Manager の 作り方 import "sigs.k8s.io/controller-runtime/pkg/manager" import "sigs.k8s.io/controller-runtime/pkg/client/config" import "sigs.k8s.io/controller-runtime/pkg/runtime/signals" // v0.1.10ではmanager/signals mgr := manager.New( // manager.New で作る config.GetConfigOrDie(), // --kubecongig, KUBECONFIG, incluster, .kube/config 探して自動で読込 manager.Options{}, // Manager初期化用オプション(LeaderElection, SyncPeriod 色々設定可) ) mgr.Add(controller) // controllerを追加. 初期化方法は後述. mgr.Start(signals.SetupSignalHandler()) // signal handling 用の channel を渡して起動 注: kubebuilderを使う限りはこの辺はあまり触らなくても大丈夫なはず 10
  11. 11. controller.New(name, controller.Option{ Reconciler, MaxConcurrentReconciles }) Controller (controller-runtimeにおけるcontrollerのアーキテクチャ) controller.Watch() Source Predicate EventHandler controller.Watch() Source Predicate EventHandler controller.Watch() Source Predicate EventHandler workqueue with RateLimiting ... reconcile.Request Reconciler Reconciler Reconciler Reconciler reconcile.Result (resultで指定されていたら) delay付きでrequeue 並列数は MaxConcurrentReconciles で指定できる(default = 1) • 構造的にはこれまでのベストプラクティスと同じ • Watch部分, Reconciler 部分が綺麗に整理されている 11
  12. 12. controller.Watch(source, predicates, eventEandler) Source Predicate EventHandler Watchの基本構造 package controller type Controller interface { // Sourceから流れてくる event を predicateでfilter して // handlerで reconcile.Request(複数可) に変換して workqueue に enqueue Watch(source.Source, handler.EventHandler, ...predicate.Predicate) error } CreateEvent UpdateEvent DeleteEvent GenericEvent CreateEvent UpdateEvent DeleteEvent GenericEvent reconcile.Request 12
  13. 13. Source は 4 種類 package source type Source interface { // Sourceは predicateとEventHandlerとqueueを渡すと開始できる // controller.Watch() は内部でこれを呼んで Watch を開始する Start(handler.EventHandler, workqueue.RateLimitingInterface, ...predicate.Predicate) error } Source Kind Channel Informer Func Kubernetes Resource用: &source.Kind{ Type: &corev1.Pod{} } 外部event用: &source.Channel{ Source: make(chan GenericEvent) } Informer直接: &source.Informer{ Informer: sharedIndexInformer } Func直接: interfaceと同じsignatureの関数はSourceとして振る舞える 13
  14. 14. Predicate package predicate // 各EventType毎 に EventHandler に渡すかどうかを決める type Predicate interface { Create( event.CreateEvent) bool Delete( event.DeleteEvent) bool Update( event.UpdateEvent) bool Generic(event.GenericEvent) bool // Channel用 } Predicate Func ResourceVersionChangedPredicate カスタムしたい関数だけ渡して Predicateを生成 (登録していない関数は _ => true になる) UpdateEvent: Old!=nil && New!=nil で ResourceVersion が異なる時のみ true それ以外 : true 14
  15. 15. EventHandler package reconcile type Request struct { types.NamespaecedName } // Namespace, Nameしか入れられないことに注意 ! package handler // 各EventType毎 に reconcile.Request(複数可) に変換して workqueue に enqueue する type EventHandler interface { Create( event.CreateEvent, workqueue.RateLimitingInterface) Update( event.UpdateEvent, workqueue.RateLimitingInterface) Delete( event.DeleteEvent, workqueue.RateLimitingInterface) Generic(event.GenericEvent, workqueue.RateLimitingInterface) // Channel用 } Event Handler Func EnqueueRequestForObject 必要な関数だけを渡して EventHandler を生成 Event に入っている NamespacedName に変換 EnqueueRequestForOwner OwnerReferences に入っている NamespacedNames に変換 EnqueueRequestsFromMapFunc Mapper{ Map(MapObject) []reconcile.Request } で変換 15
  16. 16. Watchの例 ctr := controller.New(...) // MyKind を watch してそのNamespace/Name を reconcile.Request に ctr.Watch( &source.Kind{Type: &myKind{}}, &handler.EnqueueRequestForObject{}, ) // MyKind を Owner にもつ Pod を Watch して変化があれば、 // Owner である MyKind の Namespace/Name を reconcile.Request に ctr.Watch( &source.Kind{ Type: corev1.Pod{} }, &handler.EnqueueRequestForOwner{ OwnerType: &myKind{}, IsController: true }, ) 16
  17. 17. Reconciler (Reconcileループ本体) package reconcile // 本丸。基本これを作る。 // controller 内で MaxConcurrentReconciles 個分の reconcile ループが回る type Reconciler interface { Reconcile(Request) (Result, error) } // Request は Namespace, Name しか入れられない // → Reconciler は特定の Kind の reconcile を責務とするように設計されている type Request struct { types.NamespaecedName } // Requestを再処理したい時(API erorr, race condition, preodic)とかに requeue with delay を要求可 // 成功時は reconcile.Result{} で OK type Result struct { Requeue bool, RequeueAfter time.Duration } 17
  18. 18. Controller の 作り方 type MyKindReconciler struct { client.Client /* その他自分のほしい依存 */ } func (r *MyKindReconciler) InjectClient(c client.Client) error { r.Client = c } func (r *MyKindReconciler) Reconcile(o reconcile.Request) (reconcile.Result, error) { return reconcile.Result{}, nil } // reconciler を渡して controller インスタンスを作る myreconciler := &MyKindRecociler{ } ctr := controller.New( "MyKindController" , controller.Option{ Reconciler: myreconciler, MaxConcurrentReconciles: 1, }) // Watchを設定(省略)してmanagerにcontrollerを追加 mgr.Add(ctr) 18
  19. 19. Controller の 作り方 (builderもある) import "sigs.k8s.io/controller-runtime/pkg/builder" builder. ControllerManagedBy(mgr). // この Manager に Add する For(&MyKind{}). // MyKind用のcontroller (自動watch) Owns(&corev1.Pod{}). // MyKindはPodをOwnする (自動watch) Complete(&myreconciler{}) // reconcilerを渡してcontrollerを作成してmanagerに登録 // 他にも色々なメソッドがある // builder.Watches(...): カスタム Watch // builder.WithEventFilter(...): 全体に適用する predicates // builder.Build(...): 登録せずに controller を返す 19
  20. 20. Reconcile ループでよく使うもの (controllerutil) package controller/controllerutil // object に owner を OwnerReference (isController: true) で セットする // これをつけると Watchの時に EnqueueRequestForOwner で親Resourceのreconcile.Requstにできる // 何らかの事故で子リソースが orphanedになるとkubernetesのgarbage collection対象になる func SetControllerReference(owner, object v1.Object, scheme *runtime.Scheme) error // CreateOrUpdate そのまま 存在する時の Update は 関数で渡す // OperationResultNone OperationResult = "unchanged" // OperationResultCreated OperationResult = "created" // OperationResultUpdated OperationResult = "updated" func CreateOrUpdate( ctx context.Context, c client.Client, obj runtime.Object, f MutateFn ) (OperationResult, error) 20
  21. 21. Reconcile ループでよく使うもの (client) package client // reflection を使って obj の型を検出してapiを呼んでくれる. types.NamespacedNameをkeyとして使う type ObjectKey = types.NamespacedName // Client 3 種類 の interface に分離されている type Client interface { Reader, Writer, StatusClient } type Reader interface { // key,obj を渡して obj が update される. obj struct pointer (例:&Pod{}) でないとダメ. Get(ctx context.Context, key ObjectKey, obj runtime.Object) error List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error } type Writer interface { Create(ctx context.Context, obj runtime.Object) error Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error Update(ctx context.Context, obj runtime.Object) error } type StatusClient interface { Status() StatusWriter } // status subresource のみを update type StatusWriter interface { Update(ctx context.Context, obj runtime.Object) error } 21
  22. 22. 例を見てみましょう kubernetes-sigs/controller-runtime/examples/crd // 同じ namespace/name が居なければ Pod を作る(status.lastRunもセット) // 時刻がnextStopを超えたらPodを止めて再度作る kind: ChaosPod spec: template: // pod template nextStop: "2019-04-01T01:00:10Z000:00" status: lastRun: "2019-04-00T01:00:00Z00:00" Reconciler loop実装: kubernetes-sigs/controller-runtime/examples/crd/main.go#L40 Manager/Controller初期化: kubernetes-sigs/controller-runtime/examples/crd/main.go#L108-L138 22
  23. 23. Metrics // controller, admission webhook のメトリクスは自動で取られるようになっているので // manager.Options に MetricsBindAddress をセットするだけでよい // Kubebuilderでは自動で入ります manager.New(... , manager.Options{ MetricsBindAddress: ":8080", // http://localhost:8080/metrics …, } • 取れるメトリクス – controller: reconcilation error, reconcilation queue length, reconcile latency – webhook: total requests, latency – system metrics, client-go metrics • Kubebuiler の生成する manifest だと metrics endpoint は rbac-proxy で保護されている ので注意 – prometheus SA に 'GET /metrics' が RBAC で許可必要(基本許可されていると思う ) 23
  24. 24. envtest - test 用の controll plane(api-server, etcd) Start/Stop import "sigs.k8s.io/controller-runtime/pkg/envtest" te := &envtest.Environment{ CRDDirectoryPaths : []string{filepath.Join("..", "config", "crd" }, UseExistingCluster: false, // 既存クラスタも利用可 ControlPlaneStartTimeout: 20 * time.Seconds, // (default 20sec), 環境変数でも設定可 ControlPlaneStopTimeout : 20 * time.Seconds, KubeAPIServerFlags: []string{}, // api server flags もセット可能 } // KUBEBUILDER_ASSETS(defualt=/usr/local/kubebuilder/bin) のバイナリを使って // api-server, etcd を起動して CRD をinstall して rest.Config を返してくれる config, err := te.Start() 注: kubebuilderを使う限りはこの辺は生成してくれます 24
  25. 25. // SetupTestReconcile() で probe 可能な channel 付きの reconciler を作ってくれる recFn, requests := SetupTestReconcile(newReconciler(mgr)) g.Expect(add(mgr, recFn)).Should(gomega.Succeed()) stopMgr, mgrStopped := StartTestManager(mgr, g) // myKind を作れて g.Expect(c.Create(context.TODO(), myKind)).Should(gomega.Succeed()) // するとそれに対する reconcile.Request を受け取って // ハマリポイント: 作られる probe channel の capacity が 0 なので、すべて Receive してあげないと先に進まないので注意!! g.Eventually(requests).Should(gomega.Receive(gomega.Equal(expectedReconcileRequest))) // そのうち pod が出来る g.Eventually(func() error { return c.Get(context.TODO(), podNamespacedName, expectedPod) }).Should(gomega.Succeed()) envtest をつかったテスト例 (Kubebuilderの場合) 25
  26. 26. Controller を 作る際 の Tips ● 大切: Reconcile Loop は 常に 滑らかに早く 回しましょう ● 理由: 複数 resource の reconcile を担うので block するとすぐ応答が悪くなります ● Tips: - Reconcile Loop は 小さくつくりましょう O(N) の loop を 1 回 ( N 個のリソースを更新 ) O(1) の loop を N 回 ( 1 個のリソースを更新 ) - State 遷移は モデル化 しておきましょう - ありとあらゆる状態を想定すると良いです - 分散システムでは「こういう状態にはならないはず」という前提を安易に設けるのは危険 - 不要な reconcile を避けましょう (event は multiplexing しましょう) - 100個の子Podが短時間死んでも1 回 reconcile を kick すれば済むはず - 親 resource を指す reconcile.Request に変換している限りworkqueueが rate limiting & dedupしてくれるのでcontroller-runtimeを使いましょう Icons made by Roundicons from www.flaticon.com is licensed by CC 3.0 BY 26
  27. 27. PFNでは効率的で柔軟な機械学習クラスタの構築 を一緒に挑戦してみたい人を募集しています https://www.preferred-networks.jp/ja/jobs/engineer (該当職種: Infrastructure (Software) , Middleware for ML Cluster ) We’re Hiring!! 27
  28. 28. Icons made by Vincent Le Moign from https://icon-icons.com/ licensed by CC 3.0 BY Thank you for Listening!! Any Questions?

×