kubernetes-sigs/kueue
heap
sync.Cond
core/ResourceFlavorReconciler
対象リソース: ResourceFlavor
イベント受信時にキャッシュ更新をしている
Reconcile処理は何もしていない
cores/ClusterQueueReconciler
対象リソース: ClusterQueue
イベント受信元リソース
- ClusterQueue
- Workload
イベント受信時には
- キャッシュ更新処理
- Queue ManagerへのClusterQueueの更新処理
を行っている
Reconcile処理ではStatus更新処理を行っている
core/QueueReconciler
対象リソース: Queue
イベント受信元リソース
- Queue
- Workload
イベント受信時には
- キャッシュ更新処理
- Queue ManagerへのClusterQueueの更新処理
を行っている
Reconcile処理ではStatus更新処理を行っている
core/WorkloadReconciler
対象リソース: Workload
イベント受信元リソース
- Workload
イベント受信時には
- Workloadリソースイベントをwatcherに通知
- Queue ManagerへのWorkload情報のアップデート
- キャッシュの更新
- TODO: その他(r.queues.QueueAssociatedInadmissibleWorkloads(wl)など)
- → BestEffortFIFOをqueue戦略として使ってる場合、nominateに失敗したworkloadを一時除外しているので、workloadがfinishedで成功したので、nominateに失敗したworkloadの再nominateは成功するんじゃね?ということで再度queueingしてるのではないかと
を行っている
Reconcile処理では紐づく
- Queue
- ClusterQueue
を調べ、Queue ManagerにQueue/ClusterQueueが無ければ、Workload conditionの"Admitted' conditionをfalseに設定する
JobReconciler
対象リソース: Job
イベント受信元リソース
- Job
Reconcile
queue設定が無ければ処理しない
対象jobにownerReferenceが紐付けられているworkload一覧を取得
ensureAtMostOneWorkload
- jobと一致するworkloadを探す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/controller/workload/job/job_controller.go#L343-L359
- 一致するjobが無いにも関わらずjobがsuspendでは無い場合はjobをsuspendする(その他NodeSelectorの更新などもあるが何のためにやってるのか?などは不明): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/controller/workload/job/job_controller.go#L361-L374
- 重複するworkloadがあった場合には削除する: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/controller/workload/job/job_controller.go#L376-L397
- 最後にjobと一致したworkloadを返す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/controller/workload/job/job_controller.go#L399
workloadが無い場合は作成する
jobが既に終了している場合は、終了のステータス(成功 or 失敗)をworkloadのconditionに記録して処理終了
jobがsuspend状態の場合、wl.Spec.Admissionが既に設定されていればjobを開始し(suspendをfalseにする)処理を終了し、そうでなければjobのアノテーションから取得したqueue名の更新処理を行い処理を終了する
jobがsuspend状態ではない場合、wl.Spec.Admissionが未設定であればjobを停止する(suspend=trueにする)
要約
- jobのsuspend管理を行いjobを起動するのはこいつ
- jobに紐づくworkloadの作成を行う
- workloadの重複管理を行い、重複があれば削除を行う(reconcilerが同時に動いたケースなど?)
- wl.Spec.Admissionの設定は別のやつが行う
scheduler.Start
queueからworkloadを取得
workloadが空だと
default:
m.cond.Wait()
}
のように sync.Cond.Wait()
により sync.Cond.Broadcast()
の実行まで待機する
つまりschedulerはReconcilerのようにリソースウォッチの代わりに sync.Cond
によって処理対象データが来るのを待機する仕組みになっている
snapshotを生成
Cluster Autoscalerのスケジューリングのシュミレーションのようなことをするためのチェック?
snapshotのデータからシュミレーションをやるためには
- ClusterQueues
- ResourceFlavors
が必要で、あとcacheの持つcohort情報の更新が必要というのがなんとなく予想できる
nominate
渡されたworkloadsからTODOしてフィルタしたentriesを返す
- ClusterQueueのNamespaceSelectorとworkloadの所属するnamespaceのラベルがマッチするかチェック(つまりClusterQueueはNamespaceのラベルでフィルタができる): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L179-L180
- TODO: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L181-L182
assignFlavors
e.TotalRequestsに対して以下の処理を行う
その前にe.TotalRequestsの説明
e.TotalRequestsの生成箇所
- TODO: ここはすでにadmit済であればadmitしたflavorを取り出してるけどまだflavorが入るところは読んでないのでわからない: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/workload/workload.go#L63-L69
-
for _, ps := range spec.PodSets {
でspec.PodSetsをループしてるけどspec.PodSetsは1つじゃないとstartJobされないので1つ以外のケースは基本的に無いはず: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/workload/workload.go#L70 - PodSetResourceを生成してる: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/workload/workload.go#L71-L83
- PodSetResourceを作成: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/workload/workload.go#L71-L73
- initcontainers/containers/overheadのリソース要求(cpu/mem/etc...)の合計/最大値などを計算して並列して作成するpod数(job.spec. parallelism)の分だけリソース要求を倍加する(トータルで必要になるリソースなので): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/workload/workload.go#L74-L75
- 取得できたflavor情報があれば設定: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/workload/workload.go#L76-L82
assignFlavorsの続き
- というわけでpodSetは1つしかないはずなので
for i, podSet := range e.TotalRequests {
は1ループしかしないはず: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L200 - podSetの各リソース要求に対してflavoredRequestsを生成するために以下の処理を実行: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L202-L220
- 要求を満たすresource flavorを探す(探し方は別途書く): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L203
- 見つからなければassign失敗: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L204-L206
- 借用リソース(TODO: これがなにか?について)があれば記録: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L207-L214
- 使用リソースを記録(wUsedは+=されてるけどpodSetが1つにしかならない以上追加されることは無い気がする): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L215-L218
- fitしたflavorを取得: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L219
- 生成したflavoredRequestsをe.TotalRequestsにセット: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L227
- 借用リソースがあればe.borrowsにセット : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L228-L230
- assign成功であればtrueを返す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L231
findFlavorForResource
- flavorSelectorでClusterQueue.LabelKeys[name]に指定したキー以外のlabelをpod specのNodeSelectorとaffinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTermsから取り除いたnodeaffinity.RequiredNodeAffinityを生成: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L290 https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L320-L364
- cq.RequestableResources[name]に対して以下のループ処理(nameはcpuなどのリソース種別が入る): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L291-L316
- 渡されたresourceFlavors(=実質ResourceFlavorリソースのリスト)にflavorLimit.Nameのものが無ければ次のループへ https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L292-L296
- (taintのEffectが"NoSchedule" or "NoExecute"のもののみに絞り込まれた)flavor.Taints全てをpodのspec.Tolerationsが満たさなければ次のループへ(taintのマッチング): https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L297-L302
- flavor.LabelsがClusterQueue.LabelKeys[name]によって絞り込まれたpod specのNodeSelector/affinityをみたすかチェック(label selectorのチェック) : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L303-L309
- fitsFlavorLimitsでClusterQueueにfitするflavorがあるかをチェックして返す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L311-L315
- fitするflavorがなければその結果を返す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L317
fitsFlavorLimits
- flavorの現在の使用量(used)とこれから利用したい量の総量をflavor.Maxが超えれば即fitせずとしてreturn : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L370-L373
- ClusterQueueに紐づくcohortがある場合、cohort単位のused/totalにデータを差し替え: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L374-L379
- 借受分が無ければborrowを0にする : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L380-L383
- 借受分をあわせても合計値を上回ってしまう場合はfitせずとしてreturn: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L384-L388
- fitした場合はborrowと合わせてtrueを返す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L389
CohortのUsedResources/RequestableResourcesはSnapshot生成時の↓のあたりでやってる
(要はどうメインのcohortを持つClusterQueueの各flavoのflavor.Minを足し合わせた値をRequestableResourcesとして設定して、現在の使用値の合計をUsedResourcesとして設定してる)
このcohortの概念だけど、ResourceFlavorがあることで便利かもしれないけど始めの理解をややこしくしてる気がする
sort
ソートはコメントまんまだと思う
注文基準は少なくなります。
1.借りる前に最小割り当ての下で要求します。
2.作成タイムスタンプのFIFO。
admit
各entiryごとにadmit処理
- nominated statusじゃないのはskip: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L107-L109
- 借受(borrow)があるもので、既に同名のcohortがadminされている場合はskipステータスにする(cohortの借受上限を超える可能性があるから?1つだけをadminするなら上限を超えることは無いため安全のためにそうしてる感じ?) : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L111-L115
- admit処理 : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L116-L121
admit処理
- admissionを生成してWorkload.Spec.Admissionに設定 : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L239-L250
- s.cache.AssumeWorkloadでClusterQueueにWorkloadを保存し、ClusterQueueの使用値の情報なども更新する: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L251-L254
- admissionを組み込んだworkloadを保存する(失敗時はClusterQueueを元に戻したり、requeueしたりといった復元処理を行う) : https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L256-L273
requeue
最後にassumedじゃないentryをrequeueする
ClusterQueue戦略の補足
↓の補足
StrictFIFO
多分デフォルト的な立ち位置のQueue戦略
書いてある通り .metadata.creationTimestamp
によりqueueがソートされる
BestEffortFIFO
StrictFIFOに加え↓のnominate処理で
- Label/Taint条件がマッチしない
- Fitするflavorが存在しない
といったworkloadがある場合は一時的に除外し、他のworkloadのadmitを優先するという戦略
個人的には基本こっちなのでは?という気がするんだけど、そうするとケースによっては小さめのworkloadがずっと割り当たって大きめのworkloadの処理が始まらない的な問題が発生したりする??
cacheパッケージについて
始めはなんでわざわざキャッシュデータを用意するんだろうと思ったけど主に以下の理由??
- Snapshotを生成するため
- clusterQueuesデータ生成のため(内部的な処理を行うデータはこのcacheパッケージのclusterQueuesが保持してる)
- cohortsデータ生成のため(内部的な処理を行うデータはこのcacheパッケージのcohortsが保持してる←というよりcohortはAPIデータとしてはcohort名しか保持していない)
- assumedWorkloadsを利用したClusterQueueデータのworkloadデータ管理のため
- resourceFlavorsについてはまんまResourceFlavorリソースのデータだと思うので、わざわざキャッシュすべき理由がよくわからない
Workloadリソースの削除タイミング
workloadリソース作成時にjobにownerReferenceが設定されているため、jobリソースがttlSecondsAfterFinishedなどで消されるタイミングで自動で消えるようになっていると思われる
controller-runtime tips
- (独自のcontroller的なものに)contextを通してloggerを渡す: https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go#L69-L70
- controller-runtimeのGVK形式のconfigファイルに独自の設定を定義して読み込む方法: