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

自作して学ぶKubernetes Scheduler入門

sanposhiho
November 05, 2021
680

自作して学ぶKubernetes Scheduler入門

CloudNative Days Tokyo 2021での登壇資料です。
https://event.cloudnativedays.jp/cndt2021/talks/1184

GitHub repository
https://github.com/sanposhiho/mini-kube-scheduler

sanposhiho

November 05, 2021
Tweet

More Decks by sanposhiho

Transcript

  1. 自作して学ぶKubernetes scheduler入門

    Kensei Nakada @sanposhiho

    1

    View Slide

  2. 自己紹介

    中田 健誠 / Kyoto Uni (4th year)

    普段はバックエンドエンジニアをしてます


    Twitter: さんぽし (@sanpo_shiho)

    GitHub: @sanposhiho


    Google Summer of Code 2021 @ Kubernetes

    member of Kubernetes / Kubernetes SIGs


    View Slide

  3. もくじ 

    0. Kubernetes schedulerの簡単な概要を知る

    1. 今回の自作に関して

    2. 適当なスケジュールをするSchedulerを作る 🌟

    3. Filter/Score Pluginを実装する 🌟

    4. CycleStateについて知る

    5. Binding Cycle の並列化について知る

    6. Scheduling Cache について知る

    7. Scheduling Queue をざっくり実装する 🌟

    8. Event Handlerについて再度考える 🌟          🌟: 実装する章

    
 3

    View Slide

  4. このトークの目指すところ

    内部実装の概要をつかむことを目標とします

    → Schedulerが実際に内部的に行っていることを、Schedulerが自作されていく様子を見
    ながら、機能ごとに段階的に学んでいきます


    View Slide

  5. Kubernetes schedulerの簡単な概要を知る

    5

    View Slide

  6. Kubernetes scheduler

    PodをどのNodeに割り当てるかを決定するコンポーネント


    様々なリソースの状況からPodに最適なNodeを決定する


    6

    View Slide

  7. Scheduling Framework

    公式Doc: 「Scheduling Framework | Kubernetes」より引用

    7

    View Slide

  8. Scheduling Framework

    Scheduling Framework (の Scheduling Cycle) のうち、

    ● Filter (+ PreFilter)

    ● Score (+ PreScore + Normalized Score )

    の結果が主にNodeの決定に影響している 

    ※ Extenderについては今回は割愛して説明します。 


    8

    View Slide

  9. Scheduling Framework

    Filter

    Podを実行できない(したくない)Nodeを候補から除外する


    例えば…

    ・リソース不足でPodが実行できないNode

    ・nodeSelectorの条件に一致しないNode

    を除外する

    9

    View Slide

  10. Scheduling Framework

    Score

    残った候補のNodeをスコアリングする。


    例えば…

    ・全体のNodeのリソースの使用量のバランスがちょうど良くなるNodeを優先

    ・Podを実行するコンテナイメージをすでに持っているNodeを優先

    10

    View Slide

  11. 11

    View Slide

  12. 12
    ×

    ×
 何かしらの観点でNodeを
    フィルタリングする


    View Slide

  13. 13
    ×

    ×
 何かしらの観点でNodeを
    フィルタリングする

    ×

    ×

    ×

    ×

    もしこの段階で候補のNodeが全滅した場合 

    → Preemption(Podの追い出し) を試みる

    → 諦める (Unscheduled) 


    View Slide

  14. 14
    何かしらの観点でNodeを
    スコアリングする


    View Slide

  15. 15
    Score B プラグインの
    Weightが1

    → 10

    → 50

    Score A プラグインの
    Weightが10

    → 300

    → 500


    View Slide

  16. 16
    → 10

    → 50

    Node1 が最終的に選ばれる 

    → 300

    → 500

    350

    510


    View Slide

  17. Scheduling Framework

    公式Doc: 「Scheduling Framework | Kubernetes」より引用

    17

    View Slide

  18. 仕組みざっくりまとめ

    Scheduler は Scheduling Framework の流れに沿って動作

    ● Filter で実行できない/したくないNodeを弾き、

    ● ScoreでNodeをスコアリングし、

    ● 最後にPlugin Weightを加味して、

    ● どのNodeでPodを実行するか決定する

    ● その後Binding CycleでPodをそのNodeにBindする

    18

    View Slide

  19. 今回の自作Schedulerに関して

    19

    View Slide

  20. 自作Schedulerに関して

    Kubernetes Schedulerのversion:v1.22.0


    リポジトリを公開しています

    → https://github.com/sanposhiho/mini-kube-scheduler/

    この発表の流れに沿ってブランチを切っています


    View Slide

  21. 自作Schedulerに関して

    kubernetes-sigs/kube-scheduler-simulator の仕組みを流用しており、

    コマンドライン上からSchedulerの振る舞いを確認することができます。




    View Slide

  22. 自作Schedulerに関して

    scenario という形で試したいシナリオを定義することができます。


    シナリオを変更したい場合は、

    /sched.go のscenario関数の中身を変更してください


    make start でその時点のSchedulerでシナリオを実行することができます。


    View Slide

  23. 自作Schedulerに関して


    View Slide

  24. 自作Schedulerに関して

    基本的には発表を見ていただくだけでなんとなく要点の実装がわかるように解説してい
    きます。


    時間に余裕がある方は、適宜一時停止しながら、

    実際のコードを見たり、変更してみたり、実際に手元で動作させてみたりしてみると理解
    が深まると思います。


    View Slide

  25. 適当なスケジュールをするSchedulerを作る

    25
    ブランチ: initial-random-scheduler


    View Slide

  26. 適当なScheduler

    26
    全てのNodeからランダムに選んだNodeに対してPodをBindする


    View Slide

  27. やらなければいけない事

    1. ScheduleされていないPodを見つける

    2. そのPodのScheduleを始める

    3. 全てのNodeを取得する

    4. ランダムにNodeを一つ選ぶ

    5. 選んだNodeに対してPodをBindする

    6. 1に戻る


    View Slide

  28. ScheduleされていないPodを見つける


    
Scheduler は内部のQueueにスケジュールされていないPodを持っている

    ↓

    “ScheduleされていないPodを見つけてQueueに貯める仕組み” が必要


    View Slide

  29. ScheduleされていないPodを見つける


    
Queue はとりあえず Slice でシンプルに実装

    ● Queue に Pod を追加するメソッドを実装

    ● Queue からPodを取り出すメソッドを実装


    View Slide

  30. すごいシンプルなQueue

    /minisched/queue/queue.go 


    View Slide

  31. Queue に Pod を追加するメソッド

    Slice にappendしてるだけの関数



    /minisched/queue/queue.go 


    View Slide

  32. Queue の先頭を取り出すメソッド

    /minisched/queue/queue.go 


    View Slide

  33. ScheduleされていないPodを見つける


    
EventHandler 

    → リソースの変更をトリガーにあらかじめ登録した関数を実行してくれる仕組み


    1. EventHandlerを用いて新しいPodの作成を検知する

    2. 新たなPodをQueueに入れる


    View Slide

  34. /minisched/eventhandler.go 

    スケジュールされてないPodのみ 

    をFilter

    追加されたときのみ 

    この関数を実行   → 


    View Slide

  35. Pod が作られたときに呼ばれるメソッド

    /minisched/eventhandler.go 


    View Slide

  36. やらなければいけない事 (再掲)

    1. ScheduleされていないPodを見つける

    2. そのPodのScheduleを始める

    3. 全てのNodeを取得する

    4. ランダムにNodeを一つ選ぶ

    5. 選んだNodeに対してPodをBindする

    6. 1に戻る


    View Slide

  37. PodのScheduleを始める

    sched.scheduleOne を実行し続ける

    /minisched/minisched.go 


    View Slide

  38. PodのScheduleを始める

    scheduleOne がスケジューラーのメインロジック

    Queue の先頭のPodを取り出す

    /minisched/minisched.go 


    View Slide

  39. 全てのNodeを取得する

    API からNodeを全て取得

    /minisched/minisched.go 


    View Slide

  40. ランダムにNodeを一つ選ぶ

    /minisched/minisched.go 


    View Slide

  41. 選んだNodeに対してPodをBindする



    /minisched/minisched.go 


    View Slide

  42. 選んだNodeに対してPodをBindする


    
Bind しているメソッド本体

    /minisched/minisched.go 


    View Slide

  43. 適当な Scheduler おさらい

    1. EventHandlerを用いて新しいPodの作成を検知する

    2. 新たなPodが作成されたら、それをQueueに入れる


    View Slide

  44. 適当な Scheduler おさらい

    1. Queue のPodを取り出す

    2. そのPodのScheduleを始める

    3. 全てのNodeを取得する

    4. ランダムにNodeを一つ選ぶ

    5. 選んだNodeに対してPodをBindする

    6. 1に戻る


    View Slide

  45. 動くか試そう

    node0 ~ node9 を作った後に pod1を作成

    ↓動いた 👌 


    View Slide

  46. Filter/Score Pluginを作る

    46
    ブランチ: filter-plugin, score-plugin


    View Slide

  47. Scheduling Framework

    公式Doc: 「Scheduling Framework | Kubernetes」より引用

    47

    View Slide

  48. 適当な Scheduler += Filter/Score Plugin

    1. Queue のPodを取り出す

    2. そのPodのScheduleを始める

    3. 全てのNodeを取得する

    4. Filter Plugin で Node の候補を絞る

    5. Score Plugin で 残った候補をスコアリングする

    6. 残った候補の中からスコアが高いNodeを選ぶ

    7. 選んだNodeに対してPodをBindする

    8. 1に戻る


    View Slide

  49. 例として使用するFilter Plugin

    NodeUnschedulable Plugin

    Nodeの.Spec.Unschedulable を見て、trueのNodeを候補から外す

    (実際にSchedulerに標準で搭載されているPluginです)


    View Slide

  50. Filter Pluginの実行

    RunFilterPlugins というメソッドでFilter Pluginを実行。

    エラーが返った場合はスケジュールをその時点で諦める

    /minisched/minisched.go 


    View Slide

  51. /minisched/minisched.go 


    View Slide

  52. ループでFilter PluginsをNode一つ一
    つに対して実行

    全てのFilter PluginをpassしたNodeの
    みがfeasibleNodeとして 

    返される

    /minisched/minisched.go 


    View Slide

  53. Filter Pluginの実行

    /minisched/minisched.go 

    ひとつも候補がない場合はerrorを返
    す


    View Slide

  54. 動くか試そう

    Unschedulable: true のNodeを10個(node0 ~ node9)作成


    View Slide

  55. 動くか試そう

    Unschedulableの指定がないNodeを1個(node10)作成


    View Slide

  56. 動くか試そう

    ↓動いた 👌 

    Unschedulableの指定がないnode10にbindされている


    View Slide

  57. 例として使用するScore Plugin

    NodeNumber Plugin

    Nodeの名前の最後の数字がPodの名前の最後の数字と一致するNodeに高い点数をつ
    ける


    例: pod1と言うPodのスケジュール時にはnode1に高いスコアをつける


    View Slide

  58. NodeNumber Pluginを実装する

    interfaceを満たす構造体を作成することでPluginを実装できる




    https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/framework/interface.go#L413 


    View Slide

  59. /minisched/plugins/score/nodenumber/nodenumber.go 

    Pod の名前の最後の数字を取得 

    例: pod1 → 1

    Node の名前の最後の数字を取得 

    例: node1 → 1

    一致していた場合10点を返す 


    View Slide

  60. Score Pluginの実行


    RunScorePlugins というメソッドで実行

    /minisched/minisched.go 


    View Slide

  61. Score Pluginの実行


    /minisched/minisched.go 

    ループでScore PluginsをNode一つ一つ
    に対して実行

    別の形に加工して返却 


    View Slide

  62. 動くか試そう

    node0 ~ node9 を作った後に pod1 と pod3 を作成

    (Nodeを作る際、先程のspec.Unschedulableの設定は消してます) 

    ↓動いた 👌 (pod1はnode1へ、pod3はnode3へ)


    View Slide

  63. 動くか試そう

    ↓score pluginの実行後のスコアをlogに出してみると…

    pod3のスケジュール時はちゃんとnode3が一番スコアが高くなっている


    View Slide

  64. 適当な Scheduler += Filter/Score Plugin

    1. Queue のPodを取り出す

    2. そのPodのScheduleを始める

    3. 全てのNodeを取得する

    4. Filter Plugin で Node の候補を絞る

    5. Score Plugin で 残った候補をスコアリングする

    6. (残った候補の中から) ランダムにNodeを一つ選ぶ

    7. 選んだNodeに対してPodをBindする

    8. 1に戻る


    View Slide

  65. CycleStateについて知る

    65

    View Slide

  66. CycleStateについて

    一つの Pod の スケジュール でのデータを保持している。

    → Podのスケジュール開始時に毎回新しく作成される


    Plugin(等)はCycleStateにデータを読み書きすることができる。



    View Slide

  67. PreFilter/PreScore Pluginについて

    Filter/Score pluginの実行の準備をできる拡張点



    PreFilter / PreScore において事前計算を行い、CycleStateに結果を保存。Filter /
    Score ではその結果を使用し、実行パフォーマンスをあげる


    View Slide

  68. 先程のScorePluginのinterface

    interfaceを満たす構造体を作成することでPluginを実装できる




    https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/framework/interface.go#L413 


    View Slide

  69. CycleState を使ってみる

    NodeNumber pluginにPreScoreを実装してCycleStateを使ってみる実装があります

    → ブランチ: prescore-plugin


    View Slide

  70. Binding Cycle の並列化について知る

    70

    View Slide

  71. Scheduling Framework (おさらい)

    71
    ## Scheduling Cycle

    PodをどのNodeで実行するかを決定する

    ## Binding Cycle

    Scheduling Cycle での決定を実際にクラスターに適応する


    View Slide

  72. Scheduling Cycle



    72
    他のPodのスケジュール結果が確定していないと正しくスケジュールできない

    → 並列に実行できず、Podを一つ一つスケジュールしていくしかない



    並列に実行するとダメな例: 

    2つのPodが同時にスケジュールされ、”1つのPodしかリソース的に実行できないNode”
    に2つのPodがBindされる


    View Slide

  73. Binding Cycle



    73
    スケジュールの結果は確定しているのであとはBindするだけ

    → 並列に実行できる!!効率化!!


    View Slide

  74. Binding Cycle の並列化に関係する仕組み

    ● Node を予約する仕組み

    ● Bindのタイミングを管理できる仕組み

    ○ Permit Plugin / wait on permit 

    ○ 実装済(発表時間が足りず削った) → ブランチ: permit-plugin 


    View Slide

  75. Scheduling Cache について知る

    75

    View Slide

  76. 全てのNodeを取得する (おさらい)

    API からNodeを全て取得

    /minisched/minisched.go 


    View Slide

  77. Scheduling Cache について知る

    Schedulerは実際にはAPI からNodeを取得しているわけではない

    → Scheduling Cache + snapshot の仕組み


    Scheduling Cache に全てのNodeの最新の状態をおいておく


    View Slide

  78. Scheduling Cache について知る

    EventHandlerでNodeの作成/更新/削除 を検知してCacheを更新する




    (おさらい) EventHandler 

    → リソースの変更をトリガーにあらかじめ登録した関数を実行してくれる仕組み


    View Slide

  79. Scheduling Cache によるメリット

    ● 毎回APIで問い合わせないので効率が良い 

    ● snapshotと組み合わせることでPodのスケジュール中に参照するNodeの状態が変
    化しない

    ● Schedulerに都合が良い形でNodeのデータを加工できる


    View Slide

  80. Scheduling Cache によるメリット

    ● 毎回APIで問い合わせないので効率が良い 

    ● snapshotと組み合わせることでPodのスケジュール中に参照するNodeの状態が変
    化しない

    ● Schedulerに都合が良い形でNodeのデータを加工できる


    View Slide

  81. snapshot に関して

    Cacheと同様にNodeの状態を保持している

    ● スケジュール中はNodeを取得する際にsnapshotに問い合わせる

    ○ (= APIから取得しない) 

    ○ (= scheduling cacheからも直接取得しない )

    ● スケジュールの開始時にCacheのデータを用いて更新されるようになっている

    ○ 逆に言うとスケジュールの開始時以外は更新されない 


    View Slide

  82. snapshot に関して

    snapshotと組み合わせることでPodのスケジュール中に参照するNodeの状態が変化しな
    い

    → もし、取得するNodeの状態がスケジュール中に変わってしまうと、同一スケジュール中
    のプラグインの結果などがブレてしまうことにつながる


    View Slide

  83. Scheduling Cache によるメリット

    ● 毎回APIで問い合わせないので効率が良い (自明)

    ● snapshotと組み合わせることで一つのCycleを実行中にNodeの状態が変化しない

    ● Schedulerに都合が良い形でNodeのデータを加工できる


    View Slide

  84. Node を予約する仕組みが必要な理由

    Binding Cycle を並行に実行

    → 次のPodのスケジュール時に前のPodのBindが終了していない場合におかしなことに


    View Slide

  85. Node を予約する仕組みが必要な理由

    次のPodのスケジュール時に前のPodのBindが終了していない場合におかしなことに

    → Pod がNodeを予約する仕組みを作り、Binding Cycleの開始前に予約を行う

    → 他のPodのスケジュールの際には、

    予約されているPodもすでにBindされているPodかのようにして扱う



    View Slide

  86. Scheduling Cache によるメリット 

    ● 毎回APIで問い合わせないので効率が良い (自明)

    ● snapshotと組み合わせることで一つのCycleを実行中にNodeの状態が変化しない

    ● Schedulerに都合が良い形でNodeのデータを加工できる

    ← 👀


    View Slide

  87. Node を予約する仕組みとCache

    Binding Cycle に進む前に、Node を予約する 

    → Cache を操作し、PodがNodeにBindされていることにする 

    (もし、Binding Cycleが失敗した場合はCacheを元に戻す)


    View Slide

  88. Scheduling Queue をざっくり実装する

    88
    ブランチ: scheduling-queue


    View Slide

  89. すごいシンプルなQueue (再掲)

    /minisched/queue/queue.go 


    View Slide

  90. すごいシンプルなQueue

    スケジューリングに失敗した後のPodのことなどを考えていない

    → スケジューリングに失敗したPodはまた後でスケジューリングし直すために

    Queueに戻したい


    View Slide

  91. Scheduling Queue をざっくり実装する



    /minisched/queue/queue.go 


    View Slide

  92. Scheduling Queueが持つ3つのQueueの種類

    ● activeQ: Schedule待ちのPodのQueue

    ● podBackoffQ backoff中のPodのQueue。

    ● unschedulableQ 一度スケジュールしようとして失敗したPodのQueue

             Unschedulableとなった原因のPluginなども保存


    View Slide

  93. Queue への追加/Queueからの取り出し


    View Slide

  94. Queue への追加/Queueからの取り出し

    追加

    ● Add: 新たにPodが作成された時

    ● AddUnschedulableIfNotPresent: スケジュールに失敗した時

    ○ この際にUnschedulableとなった原因のPluginなども保存 

    取り出し

    ● スケジューリング開始時にPopを使用してactiveQからPodが取り出される


    View Slide

  95. スケジュールに失敗した時にPodを戻す

    スケジュールに失敗した際にはsched.ErrorFunc を実行する


    View Slide

  96. unschedulableQにPodを追加 

    unschedulableの原因となったpluginの記録 


    View Slide

  97. backoffQ → activeQ


    View Slide

  98. backoffQ → activeQ

    ● backoffの待ちが終了したPodをactiveQに移動


    View Slide

  99. unschedulableQ → backoffQ/activeQ


    View Slide

  100. unschedulableQ → backoffQ/activeQ


    ● 60秒以上unschedulableQに存在するPodはbackoffQ/activeQに移動

    ● 特定のイベントの発行時にbackoffQ/activeQに移動


    View Slide

  101. EventHandler について再度考える

    101
    ブランチ: event-handler


    View Slide

  102. unschedulableQ → backoffQ/activeQ


    特定のイベントの発行時にbackoffQ/activeQへ移動

    unschedulableQはPodがどのPluginのせいでスケジュールできなかった(Unschedulableになった)のか
    を記録している 


    例: Filterで候補0となりUnschedulableとなった場合は、Filterで一つでもNodeを除外したPluginが
    Unschedulableの原因となったPluginとして記録される

    View Slide

  103. EnqueueExtensions

    EnqueueExtensions を満たすことで「プラグインのスケジュールの結果が変わり得るイベ
    ント」の定義ができる


    View Slide

  104. NodeUnschedulable Plugin(おさらい)

    Nodeの.Spec.Unschedulable を見て、trueのNodeを候補から外す


    View Slide

  105. NodeUnschedulable Pluginの例

    ● Nodeの.spec.Unschedulableの変更

    ● 新たなNodeの追加

    → このプラグインのスケジュールの結果が変わり得る


    View Slide

  106. EventHandler(おさらい) 

    EventHandler 

    → リソースの変更をトリガーにあらかじめ登録した関数を実行してくれる仕組み


    これまでの登場箇所

    ● PodのQueueへの追加

    ● Cacheの更新



    View Slide

  107. EventHandler と Queue の更新

    EnqueueExtensions.EventsToRegister の情報から、

    EventHandlerにイベント+Queueの更新を行う関数を動的に登録する


    View Slide

  108. EventHandlerの動的な登録


    View Slide

  109. EventHandlerの動的な登録

    /minisched/eventhandler.go 

    細かい…


    View Slide

  110. EventHandlerの動的な登録

    新しいPodをQueueに入れるEventHandlerの
    登録(前述)

    gvkMap: EventsToRegisterの情報が集められ
    たmap


    View Slide

  111. EventHandlerの動的な登録

    新しいPodをQueueに入れるEventHandlerの
    登録(前述)

    要約: 「イベントが実行された時に
    MoveAllToActiveOrBackoffQueueが実行されるように
    登録する関数


    View Slide

  112. EventHandlerの動的な登録

    新しいPodをQueueに入れるEventHandlerの
    登録(前述)

    登録され得るリソースイベントから動的に設定 


    View Slide

  113. Queue の更新

    MoveAllToActiveOrBackoffQueue メソッド(引数: 発生したイベント)


    unschedulableQのPodを見て、

    引数のイベントが、unschedulableの原因となったpluginの「スケジュールの結果が変わり
    うるイベント」だった場合に

    unschedulableQ から backoffQ/activeQ へと移動させる


    View Slide

  114. まとめ/終わりに

    114

    View Slide

  115. まとめ

    本日のトークで実装の対象とした機能のみで

    ● Filter/Score pluginsによるスケジュール

    ● Scheduling QueueによるBindされていないPodの管理

    ● EventHandlerによるQueueの操作

    が実装され、最低限のスケジュールを行うことができるようになりました


    View Slide

  116. Next step....

    時間の関係上今回触れなかった機能が多くあります

    ● Preemption

    ● Permit Plugin / wait on permit

    ● Reserve/UnReserve Plugin

    ● QueueSort Plugin

    ● Extender全般

    ● PodsToActivate によるCycleStateを通したQueue内の操作

    ● その他リソースの変更によるQueue内の操作

    など 


    View Slide

  117. ご静聴ありがとうございました 

    質問などはTwitterに投げてくださっても○です↓

    (「いや、これ違くね?」ってところもあれば、是非教えてください )


    Twitter: さんぽし(@sanpo_shiho)

    GitHub: @sanposhiho

    117

    View Slide