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

Kubernetesのソースコードリーディング入門

 Kubernetesのソースコードリーディング入門

「APC勉強会#34 Kubernetesのソースコードリーディング入門」の発表資料です。

ShinMatsuzaki

August 23, 2019
Tweet

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. あなたもLet's try!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. View Slide

  17. 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)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. 初期化処理
    メインループ
    終了処理
    podのeventをwatchし、podの作成イベントを検知したら
    以下のループを回す
    kube-schedulerのバイナリを起動
    メインループとして無限ループを開始
    Stopシグナルを受け付けると、ハンドラ実行し停止シーケンス開始
    目標設定:アプリケーションの起動からメインルーチンまでを読む
    Processが終了




    ※具体的には、以下のscheduling処理の実装内容と、
    そこに至るまでの処理の流れをざっくり追いかけたい

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  39. 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

    View Slide

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

    View Slide

  41. 実際に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
    埋込
    埋込

    View Slide

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

    View Slide

  43. 実際に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

    View Slide

  44. 実際に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()

    View Slide

  45. 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

    View Slide

  46. 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種の
    構造体が埋め込
    まれている

    View Slide

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

    View Slide

  48. findNodesThatFit() : perdicate判定
    Predicate Rule #1
    Predicate Rule #2
    Predicate Rule #3



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

    View Slide

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

    View Slide

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

    View Slide

  51. 構造体関係の情報をまとめると..
    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
    ・実際のスケジューリングを行うメソッド

    View Slide

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

    View Slide

  53. よく出てくるパターン
    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()

    View Slide

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

    View Slide

  55. 複数の構造をイメージしながら読む
    処理の流れ 構造体とメソッド
    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

    View Slide

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

    View Slide

  57. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  63. • 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

    View Slide

  64. • 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  68. $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

    View Slide

  69. $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

    View Slide

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

    View Slide

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

    View Slide

  72. 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除く

    View Slide

  73. kube-apiserver
    CustomController

    View Slide

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

    View Slide

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

    View Slide

  76. - 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オブジェク
    トの情報が格納される

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  83. 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

    View Slide

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


    View Slide

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


    reflector
    informer

    View Slide

  86. Run以降の処理(2)


    ① workqueueからkeyをpop
    ② 実際のビジネスロジックを実装するメソッドに key を渡す

    View Slide

  87. Run以降の処理(3)


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

    View Slide

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

    View Slide

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

    View Slide

  90. 参考: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
    として提供されている

    View Slide

  91. 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

    View Slide

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

    View Slide

  93. 宣伝
    • Rancherのスゴ本!

    View Slide

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

    View Slide