「APC勉強会#34 Kubernetesのソースコードリーディング入門」の発表資料です。
Kubernetesのソースコードリーディング入門2019.08.23 @shin_matsuzakiAPC勉強会 #34
View Slide
自己紹介松崎 新 (まつざき しん)@株式会社エーピーコミュニケーションズ趣味:野外コンサート、eSports観戦(LOL)
本日のアジェンダ前半: k8sのソースコード入門- ソースコードを読むと幸せになれますか?- レポジトリの紹介とゆるふわに入門するためのTIPS後半: k8sぷちDeepdive- kube-schedulerを読もうとしてみる- CustomControllerを題材に学ぶ、k8sのイベント通知
Disclaimer- 本発表に出てくるk8sのcodeは、v1.15準拠です- スライドの枠内に収めるためにエラーハンドラやコメント等を大幅に省略しています- 「ここは違うぞ」等ありましたら、懇親会の場でご指摘頂けますと嬉しいです
アジェンダ前半: k8sのソースコード入門- ソースコードを読むと幸せになれますか?- レポジトリの紹介とゆるふわに入門するためのTIPS後半: k8sぷちDeepdive- kube-schedulerを読もうとしてみる- CustomControllerを題材に学ぶ、k8sのイベント通知
何らかのOSSのコードを読んだことある人?アンケート
システムの仕組み/構造が細かい粒度でわかるようになる
(できれば)「細かい粒度」でシステムを理解したい図:LinuxのIO Schedulerの動作とiostat(1)による可観測性
コードが読めると....• 仕様に強くなれる• トラブルシュート力が向上• 試験設計に強くなれる
コードが読めると....• 仕様に強くなれる• トラブルシュート力が向上• 試験設計に強くなれる• Deepな技術解説記事が書けるかも?• Operator*1の開発者になれるかも?*1 CustomControllerを使って運用を自動化しようという手法、開発されるCustomControllerのことhttps://coreos.com/blog/introducing-operators.html
あなたもLet's try!
Q. そもそもk8sのSRCって、どうやって管理されているの?A. githubレポジトリで管理されています⇒ https://github.com/kubernetes/kubernetes
kubernetes / kubernetes• メインとなるレポジトリ• kube-apiserver, kube-controller-manager, kube-scheduler等主要なdaemonのソースコードが格納主要なレポジトリ(1)
daemonの本体となる構造体+ メソッド等エントリポイントとなるmain()関数, etc
kubernetes / apimachinery• APIオブジェクトのSchemaを定義するレポジトリ• k8s v1.8より、APIオブジェクトは kubernetes/apimachinery にて定義されるデータ構造とコードの自動生成機能(code generation)を使って、実装される仕様に変わった。[1]kubernetes / api• Kubernetes / apimachinery にて定義のSchemaを元に実際のAPIオブジェクトを定義kubernetes / client-go• kube-apisererと通信を行うclient生成用の各種関数(i.e. ) + 構造体(i.e. struct Clientset)• イベント通知エンジンである informer framework を構成する関数, キュー, キャッシュ機構etc[1] Kubernetes Deep Dive: Code Generation for CustomResourceshttps://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/主要なレポジトリ(2)
Q. 入門してみたいのですが、どういう観点で読むとGood?A. お勧めは…
お勧め(1): まずはgit blameしてみるコード内の各行が、いつ誰によってコミットされたものなのか?が一望できる
お勧め(1): まずはgit blameしてみる
なにが嬉しいかというと ...⇒開発者の存在を体感できる⇒アクティブに活動している人ならfollowしてみるといいかも読んでいるコードの各行更新が最後にいつ実施されたのかがわかる⇒ どの程度枯れた実装なのかの判断材料になる
プルリクを見ることで、過去の変更の意図が確認できる。コミット差分と照らし合わせてみるとよりGoodなにが嬉しいかというと ...プルリク:コミットの設計意図が読み解けるコミット差分:コードの変更箇所が確認できるコミット差分が見られるプルリクエストが見られるコミットの詳細が見られる
kube-controller-managerのメインルーチン (...から呼び出されているcobra command)https://github.com/kubernetes/kubernetes/blob/091a5dc53b1b961f95de225ca4cddf33193051cd/cmd/kube-controller-manager/app/controllermanager.go#L98-L107※割と初期(4 years ago)に書かれた、kube-controller-managerの概要を示したコメント。当時はまだ、replicasetが存在しなかったために、“replication controller”との表記になっている当時の設計意図や開発者の想いがわかってうれしい!お勧め(2): コメントを読んでみる
お勧め(3): 構造体を読むhttps://godoc.org/k8s.io/kubernetes/pkg/controller/namespace
Specification = DesiredStateを定義CurrentState. Reconciliation Loopの評価に使用されるKind(str型)とAIPVersion(str型)をメンバに持つ構造体Interface. kind情報生成用の関数 & APIオブジェクトの複製用の関数の実装を強制するhttps://github.com/kubernetes/api/blob/0772a1bdf941dcf775fef01dd13d667ea3031902/core/v1/types.go#L3488お勧め(3): 構造体を読むPodmetav1.TypeMetametav1.ObjectMetaPodSpecPodStatus
Piyo HogeM ( int型 )Hoge N ( int型 )参考: go言語の構造体の埋め込みパターン• 構造体メンバに別の構造体を追加できる ※ 構造体へのポインタ型としても埋め込みが可能• 以下の場合、Piyo.Hoge.N でもアクセスできるし、埋め込まれた構造名を省略し、 Piyo.N でもアクセスできる↑構造体名 ↑構造体メンバ
APIオブジェクトの実装https://github.com/kubernetes/apimachinery/blob/533d101be9a6450773bb2829bef282b6b7c4ff6d/pkg/apis/meta/v1/types.go#L41metav1.TypeMetametav1.ObjectMetaPodSpecPodStatus
APIオブジェクトの実装https://github.com/kubernetes/apimachinery/blob/533d101be9a6450773bb2829bef282b6b7c4ff6d/pkg/apis/meta/v1/types.go#L113※スペースの都合でコメントは削除(内容がすごく充実しているので、一読をお勧めします)metav1.TypeMetametav1.ObjectMetaPodSpecPodStatus
Podの場合のデータ構造https://github.com/kubernetes/api/blob/0772a1bdf941dcf775fef01dd13d667ea3031902//core/v1/types.go#L2816:6APIオブジェクトの実装※ 実際のkubectl get -oyamlの実行結果metav1.TypeMetametav1.ObjectMetaPodSpecPodStatus
Podの場合のデータ構造https://github.com/kubernetes/api/blob/0772a1bdf941dcf775fef01dd13d667ea3031902//core/v1/types.go#L3376:6APIオブジェクトの実装※ 実際のkubectl get -oyamlの実行結果metav1.TypeMetametav1.ObjectMetaPodSpecPodStatus
初期化処理メインループ終了処理podのeventをwatchし、podの作成イベントを検知したら以下のループを回すkube-schedulerのバイナリを起動メインループとして無限ループを開始Stopシグナルを受け付けると、ハンドラ実行し停止シーケンス開始目標設定:アプリケーションの起動からメインルーチンまでを読むProcessが終了読む範囲※具体的には、以下のscheduling処理の実装内容と、そこに至るまでの処理の流れをざっくり追いかけたい
読み進め方の指針• エントリポイントから読み始め、処理の流れを関数単位追いかけていく※コードを全て隅から隅まで全部読むということはしない/必要な所のみ読む• オブジェクトジャンプ/メソッドジャンプ= 構造体やメソッドの定義元に定義部分にジャンプする機能の総称main()関数 1関数 2
【参考】GithubをWebで見る場合のお勧めのchomeエクステンション各種変数や関数のDefinitionをgithub上で左クリックで表示,定義元 / 呼び出している関数へ1クリックでジャンプできるので、それを使ってタグジャンプしていく左クリックでポップアップ
実際にkube-schedulerのコードを読んでみる• エントリポイントは通常、cmdディレクトリにあるので、ディレクトリ直下のファイルを開け、まずは main() を探す
実際にkube-schedulerのコードを読んでみるhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/scheduler.go#L34
実際にkube-schedulerのコードを読んでみるhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L62
参考:cobra.Command.Execute() -> cobra.Command.Run()https://github.com/spf13/cobra
structCommandUse stringLong string参考:cobra.Command.Execute() -> cobra.Command.Run()Run func(cmd *Command, args []string)Run (c *Command) Execute() errorRun (c *Command) ExecuteC() (cmd *Command, err error)Run (c *Command) execute(a []string) (err error)(1)callポインタレシーバ(2)call(3)call
実際にkube-schedulerのコードを読んでみるhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L109*completedConfigstruct CompletedConfig //public*Configstruct completedConfig //private// CustomControllerが必要とする諸々の// コンテキスト情報- ComponentConfig- LoopbackClientConfig- InsecureServing- InsecureMetricsServing- Authentication- Authorization- SecureServing- Client- InformerFactory- PodInformer- EventClient- Recorder- Broadcaster- LeaderElectionstruct Config埋込埋込
実際にkube-schedulerのコードを読んでみるScheduler構造体を初期化https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L159※ scheduler構造体の定義
実際にkube-schedulerのコードを読んでみるhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L254停止シグナルを受け付けるまで、無限ループで、scheduleOne()を実行し続けるつまり、以降の処理がメインルーチンとなる受信専用チャネルにエンキューされると、無限ループが止まる。つまり、ストップシグナルの受付をキューがしているhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/factory/factory.go#L118https://github.com/kubernetes/apimachinery/blob/f2f3a405f61d6c2cdc0d00687c1b5d90de91e9f0/pkg/util/wait/wait.go#L87
実際にkube-schedulerのコードを読んでみるhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L442https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L283← workqueueからpod名+タイムスタンプをpop()
sched.config.Argorithm.Scheduleすると、実際には何が実行されるのか?※ sched.Config.Algorithm は interface型 なので、実際にどの構造体が入るかは不定(interfaceを満たせば何でも入る)⇒Schedule() で何が実行されるのかも、その埋め込まれる構造体に依存するhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/factory/factory.go#L82https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/core/generic_scheduler.go#L128
Scheduler構造体を初期化sched.config.Argorithm.Scheduleすると、実際には何が実行されるのか?config *Configstruct Schedulerstruct Configstruct genericScheduler埋込埋込Algorithm: algohttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L159func (g *genericScheduler)Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister)(result ScheduleResult, err error)ポインタレシーバScheduler構造体生成時に、2種の構造体が埋め込まれている
各nodeがpodのデプロイ条件を満たすか判定する処理条件を満たすノードが複数ある場合の優先度付け実際にkube-schedulerのコードを読んでみるhttps://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/core/generic_scheduler.go#L184
findNodesThatFit() : perdicate判定Predicate Rule #1Predicate Rule #2Predicate Rule #3✓✓✓• findNodesThatFit() 及び podFitsOnNode() により各ノードに対しpredicateルールに基づいたチェックが実行される。• Nodeが全てのpredicateに適合する場合のみ、ノードが filtered に追加され、次のprioritizeステージに進むfiltered failedPredicateMap全てのルールに適合?Yes No
PrioritizeNodes : prioritize判定• predicates判定を全てクリアしたノードは、次にprioritizeルールにより点数評価をされるルール名 条件の合否 重み スコアルール#1 0 (不合格) 1 0ルール#2 1 (合格) 2 2ルール#3 1 (合格) 1 1ルール#4 0 (不合格) 0 0ルール#5 1 (合格) 3 3合計点 6← のようなイメージでノード毎の総合得点が採点される
/pkg/cmd以上の処理の流れをまとめると..main() *エントリーポイント↓cobra.Command.Execute()↓cobra.Command.Run()↓runCommand()↓Run↓func (sched *Scheduler) Run()↓(wait.Until)↓func (sched *Scheduler) scheduleOne() ※メインルーチン↓func (sched *Scheduler) schedule()↓func (g *genericScheduler) Schedule() ※実際のPredicate/Prioritize処理
構造体関係の情報をまとめると..struct Scheduler struct ConfigAlgorithm: algostruct genericSchedulerfunc (g *genericScheduler)Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister)(result ScheduleResult, err error)ポインタレシーバfunc (sched *Scheduler) Run()func (sched *Scheduler) scheduleOne()func (sched *Scheduler) schedule()ポインタレシーバcallcallcall・実際のスケジューリングを行うメソッド
daemonの実装など、ソースコードをガッツリ読むときのコツ
よく出てくるパターンstructConfigHogeM ( int型 )structHogeN ( int型 )① Daemonを生成するために必要な変数や関数を Config として構造体にまとめる② それをDaemon生成用の関数(例:newDaemon())に引数として渡し、Daemonを表すインスタンス(構造体)を初期化/生成structConfigHogeM ( int型 )Hoge N ( int型 )structDaemonConfig③ Daemonを表すインスタンス(構造体)に付属のRun()メソッドを実行し、Linux上でProcessを起動例: Daemon.Run()
関数を読む際関数を読むときは、caller側とcallee側両方確認して、頭の中でマージする呼び出し側(caller) 呼ばれている側(callee)sched に何が代入されるのかは、callee側にて、戻り値を確認しないとわからない引数に渡される実際の値は、呼び出し元を見ないとわからない(左) https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L159(右) https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L121
複数の構造をイメージしながら読む処理の流れ 構造体とメソッドmain()↓cobra.Command.Execute()↓cobra.Command.Run()↓runCommand()↓Run↓func (sched *Scheduler) Run()↓(wait.Until)↓func (sched *Scheduler) scheduleOne()↓func (sched *Scheduler) schedule()↓func (g *genericScheduler) Schedule()struct SchedulerAlgorithm: algofunc (sched *Scheduler) Run()func (sched *Scheduler) scheduleOne()func (sched *Scheduler) schedule()ポインタレシーバcallcallstruct Config
その他TIPSこれは、構造体.メソッド orパッケージ名.メソッドどっち?※この場合は、optionsが未定義&NewSchedulerCommad()の引数にも存在しないので、わかりやすいが、関数が長くなると混乱しがち..SourceGraphで確認すると、頭を使わずに答えが得られる
kube-schedulerの処理を詳細に追いかけたい場合は、以下の記事がお勧めです!kube-schedulerのソースコードを読みながらPodがNodeにBindされるまでを理解するhttps://qiita.com/everpeace/items/601dc613a0f424fb5619The Kubernetes Schedulerhttps://medium.com/@dominik.tornow/the-kubernetes-scheduler-cd429abac02f
Controllerとは何か?• kube-controller-managerにより起動される、各controllerは以下の機能を持つ(1) 管轄するAPIオブジェクト(Resource)の変更有無をWatchする(2) APIオブジェクト(Resource)に作成/更新/削除のイベントが発生したら、対応する処理を行うkube-apiservercontroller watch作成/更新/削除を検知するたびに、イベントハンドラに従い対応する処理を実行
Controllerはイベントドリブンに動く• kube-controller-managerにより起動される、各controllerは以下の機能を持つ(1) 管轄するAPIオブジェクト(Resource)の変更有無をWatchする(2) APIオブジェクト(Resource)に作成/更新/削除のイベントが発生したら、対応する処理を行うkube-apiservercontroller watch作成/更新/削除を検知するたびに、イベントハンドラに従い対応する処理を実行
custom controllerを構成する2つの実装作成/更新/削除を検知するたびに、イベントハンドラに従い対応する処理を実行kube-apiservercustomcontrollerwatch②APIオブジェクトの変更イベントが通知される方法clientset①各種controllerがapiserverと通信を行う方法
• client-goにて定義のClientset構造体を利用することで、kube-apiserverにAPIリクエストを送信することができるので、controller等APIサーバと通信するdaemonを実装する際はこれを利用するAPIサーバとの通信の実装方法https://github.com/kubernetes/client-go/blob/637fc595d17acea6ce5f5b894b0a3ab301bf674e/kubernetes/clientset.go#L105corev1 = apiVersion: v1 に相当する※httpsエンドポイントとしては、/api/v1※API の階層構造についてはkubernetes.io のKubernetes API Conceptsを参照https://bit.ly/2Z8mpxS
• Clienset構造体は各APIオブジェクトに対応の構造体をメンバに持つ。構造体に付属のメソッドを実行することで、RESTClientをメンバに持つ構造体が得られる(1) “apiVersion: v1” 用の構造体(2) “kind: Nodes” 用のメソッドClientset構造体(3) メソッドを実行すると、restClientをメンバに持つnodes構造体が生成される [1][1] c.restClientの中身はRESTClient構造体。NewForConfigによりClientset生成時に、 RESTClientFor()-> NewRESTClient() により生成されるhttps://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3/rest/client.go#L119-L127
Client set構造体• 前頁で得られた構造体に付属のメソッドを実行する事で、APIオブジェクトに対し各種操作(Verb)が実行できる←で、前ページで登場のRESTClient構造体が使われる各種操作
※見やすくするために、各種エラーハンドラはばっさり削除しています※コード全文は以下https://gist.github.com/shinmatsuzaki/17f6ac8bb905a8e377d24f04d8e0cb7c実際の実装例 / sample codekind: node のオブジェクトを全て取得各オブジェクトに付属のメソッドを使って名前を表示
実行結果• これを動かすと、Nodeの一覧が取得出力されます
$HOME 配下のkubecofngを元にconfigを生成1. ~/.kube/config を元に Config構造体 を生成NewForConfig()でclientset構造体を生成CoreV1().Nodes().List()でAPIオブジェクトを取得-> GetName()メソッドで名前を取得参考: BuildConfigFromFlags()https://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3/tools/clientcmd/client_config.go#L539★
$HOME 配下のkubecofngを元にconfigを生成2. Config構造体を元にClientset構造体を作るNewForConfig()でclientset構造体を生成CoreV1().Nodes().List()でAPIオブジェクトを取得-> GetName()メソッドで名前を取得参考: NewForConfig()https://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3/kubernetes/clientset.go#L346★
$HOME 配下のkubecofngを元にconfigを生成3. Clientのメソッド経由でAPI情報を取得NewForConfig()でclientsetを生成CoreV1().Nodes().List()でAPIオブジェクトを取得-> GetName()メソッドで名前を取得CoreV1上のKind: Nodeオブジェクトを全て取得取得したAPIオブジェクトに具備のメソッドを実行し名前を取得
custom controllerを構成する2つの実装kube-apiservercustomcontrollerwatch作成/更新/削除を検知するたびに、イベントハンドラに従い対応する処理を実行①各種controllerがapiserverと通信を行う方法②APIオブジェクトの変更イベントが通知される方法clientset
informer framework• k8sの各controllerは、APIオブジェクトに対する変更を watch と呼ばれる機能で監視し、イベントが検知されると、イベントハンドラに従い処理を行う• イベント情報を流通させるパイプラインは、informer framework と呼ばれており、client-goライブラリにより実装されている画像の出展https://github.com/kubernetes/sample-controller/blob/master/docs/controller-client-go.mdAPIオブジェクト変更イベントのデータフローcelint-goにて、実装が提供されている部分個別の実装が必要な部分※workqueue除く
kube-apiserverCustomController
①Reflectorと呼ばれる機能が、定期的にkube-apiserverにアクセスし、APIオブジェクトの変更をチェックする変更イベント
②APIオブジェクトの変更が検知されると、reflectorはDeltfaFIFO queueと呼ばれるFIFO queueに変更イベントをenqueueする変更イベント
- string- Deltas- string- Deltas- string- Deltas- string- Deltasenqueue enqueuetype Deltas []DeltaType ObjectDeltaFIFO.itemsDelta FIFO Queueの上は何が流れている?https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.goDelta.Typeは、DeltaType型(=string)型であり、addition,deletion, etc が入る• k8sのAPIオブジェクトは、Delta FIFO と呼ばれるQueueを通じて流通される(※注1)• Delta = APIオブジェクトの変更イベントを指す• キュー内部にDeltas というスライスが用意されており、1つのキューで複数のイベント情報を流通できるType ObjectType Object注1:実際は、struct DeltaFIFO のメンバのitemsというmapに格納されるinterface{} 型、実際のAPIオブジェクトの情報が格納される
⑤InformerはdequeueしたイベントをObjectのStateという形式でcache storeに保存する。この際、Indexerがインデックスを付与する③Informerと呼ばれる機能はDeltaFIFO Queueをsubscribeしており、Refrectorがenqueueすると反対側でdequeue(Pop())する変更イベント④Informerはdequeue(Pop())の結果得られたオブジェクトをIndexerに渡す
⑥Informer側に、任意のCallBack関数をイベントハンドラして登録すると、イベントがDeltaFifoQueueからdequeueされる度に発火をさせることができる。変更イベント※イベントハンドラのイメージ⑦これを利用して、APIオブジェクトからkeyを抽出し、「APIの変更イベント」としてControllerが持つ別のQueue(WorkQueue)へのenqueueを行う。↑ APIオブジェクトから、/形式のkeyを生成する↑ workqueueにpush / 形式
⑧Custom controllerのメインルーチーンは、controller専用のQueueからイベントを取り出し、処理を行う ※Pop()⑨APIオブジェクトのStateの取得が必要な場合、Indexerに対しkeyを使ってオブジェクトの取得要求を発行変更イベント / 形式
これらを組み合わせるとcustom controllerができるkube-apiservercustomcontrollerwatch作成/更新/削除を検知するたびに、イベントハンドラに従い対応する処理を実行①各種controllerがapiserverと通信を行う方法②APIオブジェクトの変更イベントが通知される方法clientset
Workqueue example(pod event notifier)以下に少し改造したコードを載せていますhttps://gist.github.com/shinmatsuzaki/4b2b10fefb1f9fa18e18be109ee32784• client-goのレポジトリにて提供されているリファレンス実装https://github.com/kubernetes/client-go/blob/master/examples/workqueue/main.go• CRDは使わず、単にpodの何らかのevent(Sync/Add/Update)が発生したことのみを通知• sig-api-machinery にて策定のcontrollerのデザインのサンプル実装でもあるhttps://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/controllers.md• 1 File, 約200行のみで動く! ⇒ 学習に最適
main() の処理の流れ(1)• BuildConfigFromFlags() により、kubeconfigをインプットにConfig構造体を生成• Config構造体をNewForConfig() の引数に渡し、Clientset構造体を生成• 後段で生成されるreflectorが、kube-apiserverからAPIオブジェクトを取得するための関数を、ListWatch構造体として生成• Controller用のWorkqueueを生成ListWatchClientsetworkqueue
main() の処理の流れ(2)• indexerは、CacheStoreとkey funcを持つ cache構造体 として生成されるhttps://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3///tools/cache/store.go#L239-L244• informerは、reflectorをメンバに持つ、controller構造体として生成される。この際に、DeltaFIFOも併せて生成されるhttps://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3///tools/cache/controller.go#L75-L126https://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3///tools/cache/controller.go#L345informer indexerDeltaFIFO
main() の処理の流れ(3)① workqueue, indexer, informer をNewController() に渡し、ControllerをController構造体として生成② Controllerをworkerスレッド数=1で、起動①②
Run以降の処理(1)① informer / reflectorを起動② 1秒毎に run.Worker() を実行し続ける①②reflectorinformer
Run以降の処理(2)①②① workqueueからkeyをpop② 実際のビジネスロジックを実装するメソッドに key を渡す
Run以降の処理(3)①②① keyをキーにして、indexerからキャッシュされたAPIオブジェクトを取得② APIオブジェクトの GetName() メソッドを実行し、オブジェクト名を出力podのAPIオブジェクトをfetch
以上を全て実装すると...• Podの更新系のイベントがCustomControllerに流入する用になりますGetName()の結果得られたAPIオブジェクト(Pod)の名前
Workqueue example(pod event notifier)以下に少し改造したコードを載せていますhttps://gist.github.com/shinmatsuzaki/4b2b10fefb1f9fa18e18be109ee32784• client-goのレポジトリにて提供されているリファレンス実装https://github.com/kubernetes/client-go/blob/master/examples/workqueue/main.go• CRDは使わず、単にpodのevent(Sync/Add/Update)のみを通知• sig-api-machinery にて策定のcontrollerのデザインのサンプル実装でもあるhttps://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/controllers.md• 1 File, 約200行のみで動く! ⇒ 学習に最適
参考:k8sの標準のControllerのArchitecture出展:https://www.slideshare.net/harryzhang735/kubernetes-beyond-a-black-box-part-1 P28ReflectorThreadSafeStoreEventHandler各Controllerが持つ独自のWorkQueueshared infromerは、各APIリソースの変更を検知するためのkube-controller-managerにより起動される共用Infomer,複数のControllerで別個にinformerを持つことが無駄のだめ、共用I/Fとして提供されている
kube-schedulerも同様の仕組みを採用しているpop()eventhandlerinformerDelta FIFOScheduling Queue* 1スケジューリング処理• informerでpodをwatch ⇒ add eventが発生の場合のみ、Pod Queueにenqueueし、schedulingを実行pop()Shedulerのメインルーチン=無限ループ*1 pod priorityが有効な場合は、PriorityQueue
参考書籍• Operator Patternを主題にした本• Chapter 3 Basics of clien-go にて、後半で解説のClient-go, Clientset, Informer等の解説がされています。
宣伝• Rancherのスゴ本!
ご清聴ありがとうございました