Kubernetes Scheduler deep dive
1st handler: progrunner17.icon
2nd handler: musaprg.icon
1. そもそもKubernetes Schedulerとは
新規作成された(Nodeに割り当てられてない)Podを探してきて、一番最適なNodeに割り当てる担当。
割り当て <=> Podのspec.nodeNameを更新する
デフォルトの実装が kube-scheduler
kube-apiserver, kube-controller-managerなどとは独立したコマンドとして実行される
https://scrapbox.io/files/62ea16741e38290021bfd886.png
2. kube-scheduler 概要
大まかな流れ
Podを1つずつスケジュールする
各Podにつき、NodeをFiltering & Scoringする
Filtering: PodをscheduleできないNodeを除外
Scoring: 各Nodeにスコアをつける
最大ScoreのNodeの中からランダムに割り当てる
3. Scheduling Framework概要
Scheduling Framework + 各種Pluginという構成
各Pluginが実際にFiltering, Scoringを行う
例 NodeAffinity
node selectorや nodeaffinityの機能を実現している
Pluginはスケジューリングの各ステージで動作できる
Extension points という
https://scrapbox.io/files/62e9422c3a4b29001f3fe29b.png
In this picture "Filter" is equivalent to "Predicate" and "Scoring" is equivalent to "Priority function".
各pluginの有効・無効化、重みなども調整できる
musaprg.icon たぶんweightを設定できるのはscore pluginだけっぽい
自分でPluginを実装、利用したい場合は こんな感じ 4. kube-scheduler deep dive
全てはここから始まるkube-schedulerのentrypoint
(なんなら各自でこれ読めば十分という説がある)
musaprg.icon 雑に雰囲気だけ書き殴っておきました、あとはgotanda-sanがなんとかしてくれるはず!!!
Scheduling
どのPodをどのNodeに割り当てるか決める処理
Binding
実際にNodeへPodを割り当てる処理
Pod Preemption
High PriorityなPodをスケジューリングするためにLow PriorityなPodをterminateする処理
Pod Eviction
Podをterminateする処理
Schedulingの手順
1. Filtering
Podの動作条件に合うNodeを絞り込む(逆に言えばPodの実行に適さないものを除外していく)
PodFitsResource: resource requestsで指定している分のリソースがあるNodeだけに絞る
1つも引っ掛からなかった場合はスケジュールされない
2. Scoring
残ったNodeをスコアリングして、一番高い点数のものに割り当てる
Scheduling Frameworkという仕組みで拡張が可能(v1.19以降でstable)
自前のPluginを組み込むにはkube-schedulerをcloneしてビルドし直す必要はある
extenderというWebhookベースのdynamic configurationの仕組みも存在するけど問題があるとスケジュールが止まってしまうのでちょっと微妙
にしてはドキュメントが見当たらない(単に見落としてるだけ?)
Pluginのinterfaceはここにある
plugins/以下にいくつか既存で用意されてる実装が存在する。
ImageLocality: Favors nodes that already have the container images that the Pod runs. Extension points: score.
TaintToleration: Implements taints and tolerations. Implements extension points: filter, preScore, score.
NodeName: Checks if a Pod spec node name matches the current node. Extension points: filter.
NodePorts: Checks if a node has free ports for the requested Pod ports. Extension points: preFilter, filter.
NodeAffinity: Implements node selectors and node affinity. Extension points: filter, score.
PodTopologySpread: Implements Pod topology spread. Extension points: preFilter, filter, preScore, score.
NodeUnschedulable: Filters out nodes that have .spec.unschedulable set to true. Extension points: filter.
NodeResourcesFit: Checks if the node has all the resources that the Pod is requesting. The score can use one of three strategies: LeastAllocated (default), MostAllocated and RequestedToCapacityRatio. Extension points: preFilter, filter, score.
NodeResourcesBalancedAllocation: Favors nodes that would obtain a more balanced resource usage if the Pod is scheduled there. Extension points: score.
VolumeBinding: Checks if the node has or if it can bind the requested volumes. Extension points: preFilter, filter, reserve, preBind, score.
VolumeRestrictions: Checks that volumes mounted in the node satisfy restrictions that are specific to the volume provider. Extension points: filter.
VolumeZone: Checks that volumes requested satisfy any zone requirements they might have. Extension points: filter.
NodeVolumeLimits: Checks that CSI volume limits can be satisfied for the node. Extension points: filter.
EBSLimits: Checks that AWS EBS volume limits can be satisfied for the node. Extension points: filter.
GCEPDLimits: Checks that GCP-PD volume limits can be satisfied for the node. Extension points: filter.
AzureDiskLimits: Checks that Azure disk volume limits can be satisfied for the node. Extension points: filter.
InterPodAffinity: Implements inter-Pod affinity and anti-affinity. Extension points: preFilter, filter, preScore, score.
PrioritySort: Provides the default priority based sorting. Extension points: queueSort.
DefaultBinder: Provides the default binding mechanism. Extension points: bind.
DefaultPreemption: Provides the default preemption mechanism. Extension points: postFilter.
Pluginのconfigurationは kube-scheduler --config scheduler-config.yaml みたいな感じで渡す
extension points(PreFilterとかScoreとか)ごとに、どのPluginを有効/無効にするか、みたいに設定できる。
Podの.spec.schedulerNameでschedulerを指定できるが、そのschedulerの設定も複数ここで設定する
scoreのpluginについてはweightを設定できる。
(musaprg.icon これが実際にどう使われてるのかよくわからない)
以下の図は、より細かく一連の処理を図解したもの。
PreFilter/PreScoreは、Filter/Scoreで使用する指標等の計算を行い、CycleStateへ書き込む。
Filter/ScoreはCycleState経由で事前計算された指標を読み込んで処理を行う。
PostFilterはスケジュールできなかったPodに対してかかるやつ。
Reserve(実装されてるmethodにはReserve/Unreserveの2つがある)はPodがNodeにBindされる前にあらかじめ諸々確保しておくやつ。
基本的にはBindされるまでの間でrace conditionが発生しないようにするため。
volume_binding.goがいっちゃんわかりやすいかも?
musaprg.icon このへんgotanda-sanのほうが詳しい気がする
FilterでひっかけたPV・PVCを先にassumeする
内部でvolumeBinder.AssumePodVolumesを呼ぶ
i. If PVC binding is required, cache in-memory only:
* For manual binding: update PV objects for prebinding to the corresponding PVCs.
* For dynamic provisioning: update PVC object with a selected node from score phase)
* For the pod, which PVCs and PVs need API updates.
ii. Afterwards, the main scheduler caches the Pod->Node binding in the scheduler's pod cache. This is handled in the scheduler.
https://scrapbox.io/files/62e9422c3a4b29001f3fe29b.png
内部にqueueがあって、そこにscheduleをまつPodがenqueueされていく。
/icons/hr.icon
musaprg.icon なんかpluginの実装どれか読んでみるか〜〜〜やっていきという気持ちになり、完
/icons/hr.icon
メモ
KVS
Extender
Extender is an interface for external processes to influence scheduling decisions made by Kubernetes. This is typically needed for resources not directly managed by Kubernetes.
実装はHTTPExtenderぐらい
各stage/extention pointについて
QueueSort
podのschedule順序を決める
比較関数を提供するといい感じにpriority queue でソートしてくれる
デフォルト
code:go
p1 := corev1helpers.PodPriority(pInfo1.Pod)
p2 := corev1helpers.PodPriority(pInfo2.Pod)
return (p1 > p2) || (p1 == p2 && pInfo1.Timestamp.Before(pInfo2.Timestamp))↲
PreFilter
These plugins are used to pre-process info about the Pod, or to check certain conditions that the cluster or the Pod must meet. If a PreFilter plugin returns an error, the scheduling cycle is aborted.
kanata.icon Filter の計算では node の数だけ goroutine を立てるので、その必要のない filter 条件は PreFilter 使ったほうがいいよねという話っぽい
Filter
設定順に呼ばれる
1つでもダメならfilter outされる
PostFilter
全てのnodeがスケジュール不能な時に呼ばれる
Preemptionが典型例
PreScore
informational extension pointらしい
update internal state or to generate logs/metrics
Score
NormalizeScore
ScorePluginのExtensionとして実装
Reserve
セマフォを取って bind 先を決定するフェーズ
Reserve フェーズがないと、複数 node で PV を確保するなどが起こり得る
reserve に失敗した場合、bind に進まずに unreserve する
Permit
各pluginの呼ばれ方について
scheduleOne
sched.schedulePod
findNodesThatFitPod
fwk.RunPreFilterPlugins
sched.evaluateNominatedNode
sched.findNodesThatPassFilters
fwk.RunFilterPluginsWithNominatedPods
findNodesThatPassExtenders
extender.Filter(pod, feasibleNodes)
prioritizeNodes
fwk.RunPreScorePlugins
fwk.RunScorePlugins
selectHost
fwk.RunReservePluginsReserve
fwk.RunPermitPlugins
fwk.RunPreBindPlugins
sched.bind
fwk.RunPostBindPlugins
scheduler-plugins (third-party) のとこにもKEPがある
NodePorts
Filter で Pod の ports と引数から来た nodeInfo の ports 情報を比較している Filter は Node 毎に呼ばれるっぽい
scheduling framework 側でどう呼んでるのか確認
musaprg.icon 独自実装みたいなやつの中では一番面白そう。paypalの人が作ってるらしい。
こっちに Prometheus とか kubernetes metrics-server とかを読む機能がある
musaprg.icon Trimaranのために作ったまであるらしい(?)
scheduling frameworkは使ってない
PodがunschedulableだったらよしなにNodeを追加してくれる君(いらなくなったらdrainしてくれる君)
仮に独自のスケジューラをオープンにするにはここにPR上げるしかない…?
co-schedulerみたいな感じでデプロイするのはあまり現実的ではないから、結局single schedulerにするしかない