Slide 1

Slide 1 text

Kubernetesのソースコードリーディング入門 2019.08.23 @shin_matsuzaki APC勉強会 #34

Slide 2

Slide 2 text

自己紹介 松崎 新 (まつざき しん) @株式会社エーピーコミュニケーションズ 趣味:野外コンサート、eSports観戦(LOL)

Slide 3

Slide 3 text

本日のアジェンダ 前半: k8sのソースコード入門 - ソースコードを読むと幸せになれますか? - レポジトリの紹介とゆるふわに入門するためのTIPS 後半: k8sぷちDeepdive - kube-schedulerを読もうとしてみる - CustomControllerを題材に学ぶ、k8sのイベント通知

Slide 4

Slide 4 text

Disclaimer - 本発表に出てくるk8sのcodeは、v1.15準拠です - スライドの枠内に収めるためにエラーハンドラや コメント等を大幅に省略しています - 「ここは違うぞ」等ありましたら、懇親会の場でご指摘 頂けますと嬉しいです

Slide 5

Slide 5 text

アジェンダ 前半: k8sのソースコード入門 - ソースコードを読むと幸せになれますか? - レポジトリの紹介とゆるふわに入門するためのTIPS 後半: k8sぷちDeepdive - kube-schedulerを読もうとしてみる - CustomControllerを題材に学ぶ、k8sのイベント通知

Slide 6

Slide 6 text

何らかのOSSのコードを読んだことある人? アンケート

Slide 7

Slide 7 text

システムの仕組み/構造が 細かい粒度でわかるようになる

Slide 8

Slide 8 text

(できれば)「細かい粒度」でシステムを理解したい 図:LinuxのIO Schedulerの動作とiostat(1)による可観測性

Slide 9

Slide 9 text

コードが読めると.... • 仕様に強くなれる • トラブルシュート力が向上 • 試験設計に強くなれる

Slide 10

Slide 10 text

コードが読めると.... • 仕様に強くなれる • トラブルシュート力が向上 • 試験設計に強くなれる • Deepな技術解説記事が書けるかも? • Operator*1 の開発者になれるかも? *1 CustomControllerを使って運用を自動化しよう という手法、開発されるCustomControllerのこと https://coreos.com/blog/introducing-operators.html

Slide 11

Slide 11 text

あなたもLet's try!

Slide 12

Slide 12 text

アジェンダ 前半: k8sのソースコード入門 - ソースコードを読むと幸せになれますか? - レポジトリの紹介とゆるふわに入門するためのTIPS 後半: k8sぷちDeepdive - kube-schedulerを読もうとしてみる - CustomControllerを題材に学ぶ、k8sのイベント通知

Slide 13

Slide 13 text

Q. そもそもk8sのSRCって、どうやって 管理されているの? A. githubレポジトリで管理されています ⇒ https://github.com/kubernetes/kubernetes

Slide 14

Slide 14 text

kubernetes / kubernetes • メインとなるレポジトリ • kube-apiserver, kube-controller-manager, kube-scheduler等主要なdaemonのソースコードが格納 主要なレポジトリ(1)

Slide 15

Slide 15 text

daemonの本体と なる構造体 + メソッド等 エントリポイン トとなるmain() 関数, etc

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

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 CustomResources https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/ 主要なレポジトリ(2)

Slide 18

Slide 18 text

Q. 入門してみたいのですが、 どういう観点で読むとGood? A. お勧めは…

Slide 19

Slide 19 text

お勧め(1): まずはgit blameしてみる コード内の各行が、いつ誰によって コミットされたものなのか?が一望できる

Slide 20

Slide 20 text

お勧め(1): まずはgit blameしてみる

Slide 21

Slide 21 text

なにが嬉しいかというと ... ⇒開発者の存在を体感できる ⇒アクティブに活動している人なら followしてみるといいかも 読んでいるコードの各行更新が最後にいつ 実施されたのかがわかる ⇒ どの程度枯れた実装なのかの判断材料になる

Slide 22

Slide 22 text

プルリクを見ることで、過去の変更の意図が確認できる。 コミット差分と照らし合わせてみるとよりGood なにが嬉しいかというと ... プルリク:コミットの設計意図が読み解ける コミット差分:コードの変更箇所が確認できる コミット差分が見られる プルリクエストが見られる コミットの詳細が見られる

Slide 23

Slide 23 text

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): コメントを読んでみる

Slide 24

Slide 24 text

お勧め(3): 構造体を読む https://godoc.org/k8s.io/kubernetes/pkg/controller/namespace

Slide 25

Slide 25 text

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): 構造体を読む Pod metav1.TypeMeta metav1.ObjectMeta PodSpec PodStatus

Slide 26

Slide 26 text

Piyo Hoge M ( int型 ) Hoge N ( int型 ) 参考: go言語の構造体の埋め込みパターン • 構造体メンバに別の構造体を追加できる ※ 構造体へのポインタ型としても埋め込みが可能 • 以下の場合、Piyo.Hoge.N でもアクセスできるし、埋め込まれた構造名を省略し、 Piyo.N でもアクセスできる ↑構造体名 ↑構造体メンバ

Slide 27

Slide 27 text

APIオブジェクトの実装 https://github.com/kubernetes/apimachinery/blob/533d101be9a6450773bb2829bef282b6b7c4ff6d/pkg/apis/meta/v1/types.go#L41 metav1.TypeMeta metav1.ObjectMeta PodSpec PodStatus

Slide 28

Slide 28 text

APIオブジェクトの実装 https://github.com/kubernetes/apimachinery/blob/533d101be9a6450773bb2829bef282b6b7c4ff6d/pkg/apis/meta/v1/types.go#L113 ※スペースの都合でコメントは削除(内容がすごく充実しているので、一読をお勧めします) metav1.TypeMeta metav1.ObjectMeta PodSpec PodStatus

Slide 29

Slide 29 text

Podの場合のデータ構造 https://github.com/kubernetes/api/blob/0772a1bdf941dcf775fef01dd13d667ea3031902//core/v1/types.go#L2816:6 APIオブジェクトの実装 ※ 実際のkubectl get -oyaml の実行結果 metav1.TypeMeta metav1.ObjectMeta PodSpec PodStatus

Slide 30

Slide 30 text

Podの場合のデータ構造 https://github.com/kubernetes/api/blob/0772a1bdf941dcf775fef01dd13d667ea3031902//core/v1/types.go#L3376:6 APIオブジェクトの実装 ※ 実際のkubectl get -oyaml の実行結果 metav1.TypeMeta metav1.ObjectMeta PodSpec PodStatus

Slide 31

Slide 31 text

アジェンダ 前半: k8sのソースコード入門 - ソースコードを読むと幸せになれますか? - レポジトリの紹介とゆるふわに入門するためのTIPS 後半: k8sぷちDeepdive - kube-schedulerを読もうとしてみる - CustomControllerを題材に学ぶ、k8sのイベント通知

Slide 32

Slide 32 text

初期化処理 メインループ 終了処理 podのeventをwatchし、podの作成イベントを検知したら 以下のループを回す kube-schedulerのバイナリを起動 メインループとして無限ループを開始 Stopシグナルを受け付けると、ハンドラ実行し停止シーケンス開始 目標設定:アプリケーションの起動からメインルーチンまでを読む Processが終了 読 む 範 囲 ※具体的には、以下のscheduling処理の実装内容と、 そこに至るまでの処理の流れをざっくり追いかけたい

Slide 33

Slide 33 text

読み進め方の指針 • エントリポイントから読み始め、処理の流れを関数単位追いかけていく ※コードを全て隅から隅まで全部読むということはしない/必要な所のみ読む • オブジェクトジャンプ/メソッドジャンプ = 構造体やメソッドの定義元に定義部分にジャンプする機能の総称 main() 関数 1 関数 2

Slide 34

Slide 34 text

【参考】GithubをWebで見る場合の お勧めのchomeエクステンション 各種変数や関数のDefinitionを github上で左クリックで表示, 定義元 / 呼び出している関数 へ1クリックでジャンプできるの で、それを使ってタグジャンプ していく 左クリックでポップアップ

Slide 35

Slide 35 text

実際にkube-schedulerのコードを読んでみる • エントリポイントは通常、cmdディレクトリにあるので、 ディレクトリ直下のファイルを開け、まずは main() を探す

Slide 36

Slide 36 text

実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/scheduler.go#L34

Slide 37

Slide 37 text

実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L62

Slide 38

Slide 38 text

参考:cobra.Command.Execute() -> cobra.Command.Run() https://github.com/spf13/cobra

Slide 39

Slide 39 text

struct Command Use string Long string 参考:cobra.Command.Execute() -> cobra.Command.Run() Run func(cmd *Command, args []string) Run (c *Command) Execute() error Run (c *Command) ExecuteC() (cmd *Command, err error) Run (c *Command) execute(a []string) (err error) (1)call ポインタ レシーバ (2)call (3)call

Slide 40

Slide 40 text

実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L62

Slide 41

Slide 41 text

実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L109 *completedConfig struct CompletedConfig //public *Config struct completedConfig //private // CustomControllerが必要とする諸々の // コンテキスト情報 - ComponentConfig - LoopbackClientConfig - InsecureServing - InsecureMetricsServing - Authentication - Authorization - SecureServing - Client - InformerFactory - PodInformer - EventClient - Recorder - Broadcaster - LeaderElection struct Config 埋込 埋込

Slide 42

Slide 42 text

実際にkube-schedulerのコードを読んでみる Scheduler構造体を初期化 https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L159 ※ scheduler構造体の定義

Slide 43

Slide 43 text

実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L254 停止シグナルを受け付けるまで、 無限ループで、scheduleOne()を実行し続ける つまり、以降の処理がメインルーチンとなる 受信専用チャネルにエンキューされると、無限ループが止まる。 つまり、ストップシグナルの受付をキューがしている https://github.com/kubernetes/kubernetes/blob/2d3c76f9 091b6bec110a5e63777c332469e0cba2/pkg/scheduler/fact ory/factory.go#L118 https://github.com/kubernetes/apimachinery/blob/f2f3a405f61d6c2cdc0d00687c1b5d90de91e9f0/pkg/util/wait/wait.go#L87

Slide 44

Slide 44 text

実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L44 2 https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/scheduler.go#L283 ← workqueueからpod名+タイムスタンプをpop()

Slide 45

Slide 45 text

sched.config.Argorithm.Scheduleすると、実際には何が実行されるのか? ※ sched.Config.Algorithm は interface型 なので、実際にどの構造体が入るかは不定(interfaceを満たせば何でも入る) ⇒Schedule() で何が実行されるのかも、その埋め込まれる構造体に依存する https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/factory/factory.go#L82 https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/core/generic_scheduler.go#L128

Slide 46

Slide 46 text

Scheduler構造体を初期化 sched.config.Argorithm.Scheduleすると、実際には何が実行されるのか? config *Config struct Scheduler struct Config struct genericScheduler 埋込 埋込 Algorithm: algo https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/cmd/kube-scheduler/app/server.go#L159 func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister) (result ScheduleResult, err error) ポインタ レシーバ Scheduler構造体 生成時に、2種の 構造体が埋め込 まれている

Slide 47

Slide 47 text

各nodeがpodのデプロイ条件を満たすか判定する処理 条件を満たすノードが複数ある場合の優先度付け 実際にkube-schedulerのコードを読んでみる https://github.com/kubernetes/kubernetes/blob/2d3c76f9091b6bec110a5e63777c332469e0cba2/pkg/scheduler/core/generic_scheduler.go#L184

Slide 48

Slide 48 text

findNodesThatFit() : perdicate判定 Predicate Rule #1 Predicate Rule #2 Predicate Rule #3 ✓ ✓ ✓ • findNodesThatFit() 及び podFitsOnNode() により 各ノードに対しpredicateルールに基づいたチェックが実行される。 • Nodeが全てのpredicateに適合する場合のみ、 ノードが filtered に追加され、次のprioritizeステージに進む filtered failedPredicateMap 全てのルールに適合? Yes No

Slide 49

Slide 49 text

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 ← のようなイメージで ノード毎の総合得点が採点される

Slide 50

Slide 50 text

/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処理

Slide 51

Slide 51 text

構造体関係の情報をまとめると.. struct Scheduler struct Config Algorithm: algo struct genericScheduler func (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() ポインタ レシーバ call call call ・実際のスケジューリングを行うメソッド

Slide 52

Slide 52 text

daemonの実装など、ソースコードを ガッツリ読むときのコツ

Slide 53

Slide 53 text

よく出てくるパターン struct Config Hoge M ( int型 ) struct Hoge N ( int型 ) ① Daemonを生成するために必要な変数や関数を Config として構造体にまとめる ② それをDaemon生成用の関数(例:newDaemon())に引数として渡し、 Daemonを表すインスタンス(構造体)を初期化/生成 struct Config Hoge M ( int型 ) Hoge N ( int型 ) struct Daemon Config ③ Daemonを表すインスタンス(構造体)に付属のRun()メソッドを実行し、Linux上でProcessを起動 例: Daemon.Run()

Slide 54

Slide 54 text

関数を読む際 関数を読むときは、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

Slide 55

Slide 55 text

複数の構造をイメージしながら読む 処理の流れ 構造体とメソッド 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 Scheduler Algorithm: algo func (sched *Scheduler) Run() func (sched *Scheduler) scheduleOne() func (sched *Scheduler) schedule() ポインタ レシーバ call call struct Config

Slide 56

Slide 56 text

その他TIPS これは、構造体.メソッド or パッケージ名.メソッドどっち? ※この場合は、optionsが未定義& NewSchedulerCommad()の引数にも 存在しないので、わかりやすいが、 関数が長くなると混乱しがち.. SourceGraphで確認すると、頭を使わずに答えが得られる

Slide 57

Slide 57 text

kube-schedulerの処理を詳細に追いかけたい場合は、以下の記事がお勧めです! kube-schedulerのソースコードを読みながらPodがNodeにBindされるまでを理解する https://qiita.com/everpeace/items/601dc613a0f424fb5619 The Kubernetes Scheduler https://medium.com/@dominik.tornow/the-kubernetes-scheduler-cd429abac02f

Slide 58

Slide 58 text

アジェンダ 前半: k8sのソースコード入門 - ソースコードを読むと幸せになれますか? - レポジトリの紹介とゆるふわに入門するためのTIPS 後半: k8sぷちDeepdive - kube-schedulerを読もうとしてみる - CustomControllerを題材に学ぶ、k8sのイベント通知

Slide 59

Slide 59 text

Controllerとは何か? • kube-controller-managerにより起動される、各controllerは以下の機能を持つ (1) 管轄するAPIオブジェクト(Resource)の変更有無をWatchする (2) APIオブジェクト(Resource)に作成/更新/削除のイベントが発生したら、対応する処理を行う kube-apiserver controller watch 作成/更新/削除を検知するたびに、 イベントハンドラに従い対応する処理を実行

Slide 60

Slide 60 text

Controllerはイベントドリブンに動く • kube-controller-managerにより起動される、各controllerは以下の機能を持つ (1) 管轄するAPIオブジェクト(Resource)の変更有無をWatchする (2) APIオブジェクト(Resource)に作成/更新/削除のイベントが発生したら、対応する処理を行う kube-apiserver controller watch 作成/更新/削除を検知するたびに、 イベントハンドラに従い対応する処理を実行

Slide 61

Slide 61 text

custom controllerを構成する2つの実装 作成/更新/削除を検知するたびに、 イベントハンドラに従い対応する処理を実行 kube-apiserver custom controller watch ②APIオブジェクト の変更イベントが 通知される方法 client set ①各種controllerが apiserverと通信を 行う方法

Slide 62

Slide 62 text

custom controllerを構成する2つの実装 作成/更新/削除を検知するたびに、イベ ントハンドラに従い対応する処理を実行 kube-apiserver custom controller watch ②APIオブジェクト の変更イベントが 通知される方法 client set ①各種controllerが apiserverと通信を 行う方法

Slide 63

Slide 63 text

• client-goにて定義のClientset構造体を利用することで、kube-apiserverにAPIリクエスト を送信することができるので、controller等APIサーバと通信するdaemonを実装する際はこれを利用する APIサーバとの通信の実装方法 https://github.com/kubernetes/client-go/blob/637fc595d17acea6ce5f5b894b0a3ab301bf674e/kubernetes/clientset.go#L105 corev1 = apiVersion: v1 に相当する ※httpsエンドポイントとしては、/api/v1 ※API の階層構造については kubernetes.io の Kubernetes API Conceptsを 参照 https://bit.ly/2Z8mpxS

Slide 64

Slide 64 text

• 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

Slide 65

Slide 65 text

Client set構造体 • 前頁で得られた構造体に付属のメソッドを実行する事で、APIオブジェクトに対し 各種操作(Verb)が実行できる ←で、前ページで登場のRESTClient構造体が使われる 各種操作

Slide 66

Slide 66 text

※見やすくするために、各種エラーハンドラはばっさり削除しています ※コード全文は以下 https://gist.github.com/shinmatsuzaki/17f6ac8bb905a8e377d24f04d8e0cb7c 実際の実装例 / sample code kind: node のオブジェクトを 全て取得 各オブジェクトに付属の メソッドを使って名前を表示

Slide 67

Slide 67 text

実行結果 • これを動かすと、Nodeの一覧が取得出力されます

Slide 68

Slide 68 text

$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 ★

Slide 69

Slide 69 text

$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 ★

Slide 70

Slide 70 text

$HOME 配下の kubecofngを 元にconfigを生成 3. Clientのメソッド経由でAPI情報を取得 NewForConfig() でclientsetを 生成 CoreV1().Nodes(). List()でAPIオブジェ クトを取得 -> GetName() メソッドで名前を 取得 CoreV1上のKind: Nodeオブジェクトを全て取得 取得したAPIオブジェクトに具備の メソッドを実行し名前を取得

Slide 71

Slide 71 text

custom controllerを構成する2つの実装 kube-apiserver custom controller watch 作成/更新/削除を検知するたびに、イベ ントハンドラに従い対応する処理を実行 ①各種controllerが apiserverと通信を 行う方法 ②APIオブジェクト の変更イベントが 通知される方法 client set

Slide 72

Slide 72 text

informer framework • k8sの各controllerは、APIオブジェクトに対する変更を watch と呼ばれる機能で監視し、 イベントが検知されると、イベントハンドラに従い処理を行う • イベント情報を流通させるパイプラインは、informer framework と呼ばれており、 client-goライブラリにより実装されている 画像の出展 https://github.com/kubernetes/sample-controller/blob/master/docs/controller-client-go.md APIオブジェクト 変更イベントの データフロー celint-goにて、 実装が提供されている 部分 個別の実装が必要な 部分 ※workqueue除く

Slide 73

Slide 73 text

kube-apiserver CustomController

Slide 74

Slide 74 text

①Reflectorと呼ばれる機能が、 定期的にkube-apiserverに アクセスし、APIオブジェクトの変 更をチェックする 変更 イベント

Slide 75

Slide 75 text

②APIオブジェクトの変更が 検知されると、reflectorは DeltfaFIFO queueと 呼ばれるFIFO queueに 変更イベントをenqueueする 変更 イベント

Slide 76

Slide 76 text

- string - Deltas - string - Deltas - string - Deltas - string - Deltas enqueue enqueue type Deltas []Delta Type Object DeltaFIFO.items Delta FIFO Queueの上は何が流れている? https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go Delta.Typeは、 DeltaType型(=string) 型であり、addition, deletion, etc が入る • k8sのAPIオブジェクトは、Delta FIFO と呼ばれるQueueを通じて流通される(※注1) • Delta = APIオブジェクトの変更イベントを指す • キュー内部にDeltas というスライスが用意されており、1つのキューで複数のイベント情報を流通できる Type Object Type Object 注1:実際は、struct DeltaFIFO のメンバのitemsというmapに格納される interface{} 型、 実際のAPIオブジェク トの情報が格納される

Slide 77

Slide 77 text

⑤Informerはdequeueした イベントをObjectのStateという形 式でcache storeに保存する。 この際、Indexerがインデックスを 付与する ③Informerと呼ばれる機能は DeltaFIFO Queueをsubscribeして おり、Refrectorがenqueueすると 反対側でdequeue(Pop())する 変更 イベント ④Informerはdequeue(Pop())の 結果得られたオブジェクトを Indexerに渡す

Slide 78

Slide 78 text

⑥Informer側に、任意のCallBack 関数をイベントハンドラして登録す ると、イベントがDeltaFifoQueue からdequeueされる度に発火をさせ ることができる。 変更 イベント ※イベントハンドラのイメージ ⑦これを利用して、APIオブジェク トからkeyを抽出し、「APIの変更 イベント」としてControllerが持つ 別のQueue(WorkQueue)への enqueueを行う。 ↑ APIオブジェクトから、 / 形式のkeyを生成する ↑ workqueueにpush / 形式

Slide 79

Slide 79 text

⑧Custom controllerの メインルーチーンは、 controller専用のQueueから イベントを取り出し、 処理を行う ※Pop() ⑨APIオブジェクトのStateの取得が 必要な場合、Indexer に対しkeyを使ってオブジェクトの 取得要求を発行 変更 イベント / 形式

Slide 80

Slide 80 text

これらを組み合わせるとcustom controllerができる kube-apiserver custom controller watch 作成/更新/削除を検知するたびに、イベ ントハンドラに従い対応する処理を実行 ①各種controllerが apiserverと通信を 行う方法 ②APIオブジェクト の変更イベントが 通知される方法 client set

Slide 81

Slide 81 text

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行のみで動く! ⇒ 学習に最適

Slide 82

Slide 82 text

main() の処理の流れ(1) • BuildConfigFromFlags() により、kubeconfigをインプットにConfig構造体を生成 • Config構造体をNewForConfig() の引数に渡し、Clientset構造体を生成 • 後段で生成されるreflectorが、kube-apiserverからAPIオブジェクトを取得するための関数を、 ListWatch構造体として生成 • Controller用のWorkqueueを生成 ListWatch Clientset workqueue

Slide 83

Slide 83 text

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-L126 https://github.com/kubernetes/client-go/blob/a240a565b073d748b78933afa3ea43fc922367b3///tools/cache/controller.go#L345 informer indexer DeltaFIFO

Slide 84

Slide 84 text

main() の処理の流れ(3) ① workqueue, indexer, informer をNewController() に渡し、ControllerをController構造体として生成 ② Controllerをworkerスレッド数=1で、起動 ① ②

Slide 85

Slide 85 text

Run以降の処理(1) ① informer / reflectorを起動 ② 1秒毎に run.Worker() を実行し続ける ① ② reflector informer

Slide 86

Slide 86 text

Run以降の処理(2) ① ② ① workqueueからkeyをpop ② 実際のビジネスロジックを実装するメソッドに key を渡す

Slide 87

Slide 87 text

Run以降の処理(3) ① ② ① keyをキーにして、indexerからキャッシュされたAPIオブジェクトを取得 ② APIオブジェクトの GetName() メソッドを実行し、オブジェクト名を出力 podのAPIオブ ジェクトを fetch

Slide 88

Slide 88 text

以上を全て実装すると... • Podの更新系のイベントがCustomControllerに流入する用になります GetName()の結果得られた APIオブジェクト(Pod)の名前

Slide 89

Slide 89 text

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行のみで動く! ⇒ 学習に最適

Slide 90

Slide 90 text

参考:k8sの標準のControllerのArchitecture 出展:https://www.slideshare.net/harryzhang735/kubernetes-beyond-a-black-box-part-1 P28 Reflector ThreadSafeStore EventHandler 各Controllerが 持つ独自の WorkQueue shared infromerは、 各APIリソースの変更を検知するた めのkube-controller-managerによ り起動される共用Infomer, 複数のControllerで別個にinformer を持つことが無駄のだめ、共用I/F として提供されている

Slide 91

Slide 91 text

kube-schedulerも同様の仕組みを採用している pop() event handler informer Delta FIFO Scheduling Queue * 1 スケジューリング処理 • informerでpodをwatch ⇒ add eventが発生の場合のみ、Pod Queueにenqueueし、schedulingを実行 pop() Shedulerのメインルーチン =無限ループ *1 pod priorityが有効な場合は、 PriorityQueue

Slide 92

Slide 92 text

参考書籍 • Operator Patternを主題にした本 • Chapter 3 Basics of clien-go にて、後半で解説の Client-go, Clientset, Informer等の解説がされています。

Slide 93

Slide 93 text

宣伝 • Rancherのスゴ本!

Slide 94

Slide 94 text

ご清聴ありがとうございました