Slide 1

Slide 1 text

Kueueアーキテクチャ @bells17

Slide 2

Slide 2 text

▶ @bells17 ▶ Software Engineer ▶ 普段やってること: + Kubernetes 関連コンポーネントの開発 + Kubernetes as a Service開発 ▶ Kubernetes SIG-Docs Japanese localization reviewer ▶ Kubernetes Internal Organizer ▶ #kubenews ▶ @bells17_

Slide 3

Slide 3 text

#kubenews ほぼ毎週⾦曜22:00~YouTubeで配信中 Kubernetes/Cloud Native関連のニュースを中⼼に技術雑談してます

Slide 4

Slide 4 text

今⽇話すこと ▶ Kueueとは? ▶ Kueueのインストール⽅法~使い⽅ ▶ Kueueのアーキテクチャ ▶ Kueueの主な機能 ▶ Kueueの実装 ▶ その他Tips(Kueue実装から学んだこと)

Slide 5

Slide 5 text

このセッションでわかること ▶ ⼤まかなKueueのアーキテクチャ ▶ Kueueの主な機能と基本的な使い⽅

Slide 6

Slide 6 text

注意点 ▶ 対象バージョン: Kueue v0.1.0 + https://github.com/kubernetes-sigs/kueue/tree/v0.1.0 ▶ あくまでKueueの実装を追った結果での理解の説明になるので、 間違いが含まれている可能性があります ▶ 引⽤しているコードはスライドにうまく収めるため省略、⼀部書き換えを ⾏っている箇所があります ▶ コードリーディングメモ: https://zenn.dev/bells17/scraps/16625963e51d23

Slide 7

Slide 7 text

Kueueとは?

Slide 8

Slide 8 text

Kueue ▶ Kubernetesネイティブなバッチ(ジョブ)スケジューラー + バッチ(ジョブ)のキューイングの仕組みを提供してくれる ▶ 機能拡張することでJobリソースにキューイング機能を追加してくれる + KueueはJobリソースのsuspendフィールドのtrue/falseを切り替えることで キューイングしたJobの実⾏をコントロールする ▶ キュー管理者は予めResourceFlavor/ClusterQueue/Queueリソースを設定 しておくことで、バッチユーザーが設定したキューを利⽤することができる

Slide 9

Slide 9 text

Kueue ▶ 以下のようなポイントはKubernetes⾃⾝の仕組みを使いつつ実現している + Jobリソースのマネジメントはkube-controller-managerを利⽤ + Podのスケジューリングはkube-schedulerを利⽤ ▶ Volcano(kube-batch)のJob API、ジョブライフサイクル管理、スケジューラー といったコアKubernetesにすでに存在する多くの機能を 再実装している点を問題に感じたのがKueueを作成した背景の1つのよう ▶ ドキュメントなどから主なユースケースとしてMLワークロードのような ⼤規模バッチを多数同時並列で実⾏したいようなユースケースにおいて バッチの実⾏順序のコントロールやキューイングなどを⾏うため利⽤する想定? + ここらへん詳しい⼈教えてください (Design Docs読んでもなんとなくしか想像できなかった)

Slide 10

Slide 10 text

Kueue ▶ Kubernetesコミュニティの #wg-batch により開発されているよう ▶ 2022/4/12にv0.1.0がリリースされたばかり ▶ リポジトリ: https://github.com/kubernetes-sigs/kueue ▶ Design Docs(controller): https://bit.ly/kueue-controller-design ▶ Design Docs(API): https://bit.ly/kueue-apis ▶ Old Proposal: https://bit.ly/k8s-job-management

Slide 11

Slide 11 text

[デモ] Kueueのインストール⽅法~使い⽅

Slide 12

Slide 12 text

k8sクラスターを構築~Kueueをインストール LJOEDSFBUFDMVTUFSOBNFLVFVFFYBNQMF LVCFDUMBQQMZGIUUQTHJUIVCDPNDFSUNBOBHFSDFSUNBOBHFSSFMFBTFT EPXOMPBEWDFSUNBOBHFSZBNM LVCFDUMXBJUGPSDPOEJUJPOBWBJMBCMFUJNFPVUTODFSUNBOBHFS EFQMPZNFOUTDFSUNBOBHFSXFCIPPL LVCFDUMBQQMZLHJUIVCDPNLVCFSOFUFTTJHTLVFVFDPOpHEFGBVMU W

Slide 13

Slide 13 text

状態チェック LVCFDUMHFUOPEF /".&45"56430-&4"(&7&34*0/ LVFVFFYBNQMFDPOUSPMQMBOF3FBEZDPOUSPMQMBOF NBTUFSNTW LVCFDUMOLVFVFTZTUFNHFUEFQMPZ /".&3&"%:6150%"5&"7"*-"#-&"(& EFQMPZNFOUBQQTLVFVFDPOUSPMMFSNBOBHFSNT LVCFDUMHFUDSEcHSFQLVFVFYLTJP DMVTUFSRVFVFTLVFVFYLTJP5; RVFVFTLVFVFYLTJP5; SFTPVSDFqBWPSTLVFVFYLTJP5; XPSLMPBETLVFVFYLTJP5;

Slide 14

Slide 14 text

Kueueに必要なカスタムリソースを設定 DBU&04TFUUJOHZBNM BQJ7FSTJPOLVFVFYLTJPWBMQIB LJOE3FTPVSDF'MBWPS NFUBEBUB OBNFEFGBVMU BQJ7FSTJPOLVFVFYLTJPWBMQIB LJOE2VFVF NFUBEBUB OBNFTQBDFEFGBVMU OBNFNBJO TQFD DMVTUFS2VFVFDMVTUFSUPUBM &04 DBU&04TFUUJOHZBNM BQJ7FSTJPOLVFVFYLTJPWBMQIB LJOE$MVTUFS2VFVF NFUBEBUB OBNFDMVTUFSUPUBM TQFD OBNFTQBDF4FMFDUPS\^NBUDIBMM SFTPVSDFT OBNFDQV qBWPST OBNFEFGBVMU RVPUB NJO &04 Ϧιʔε࡞੒ LVFCDUMBQQMZa GTFUUJOHTZBNM

Slide 15

Slide 15 text

Jobを作成 DBU&04KPCZBNM BQJ7FSTJPOCBUDIW LJOE+PC NFUBEBUB HFOFSBUF/BNFTBNQMFKPC BOOPUBUJPOT LVFVFYLTJPRVFVFOBNFNBJORVFVF໊Λࢦఆ TQFD QBSBMMFMJTN DPNQMFUJPOT TVTQFOEUSVFUSVFʹ͢Δ͜ͱͰ,VFVFͷॲཧର৅ʹ UFNQMBUF TQFD DPOUBJOFST OBNFEVNNZKPC JNBHFHDSJPLTTUBHJOHQFSGUFTUTTMFFQMBUFTU BSHT<T> SFTPVSDFT SFRVFTUT DQVl SFTUBSU1PMJDZ/FWFS &04 +PC࡞੒ LVCFDUMDSFBUFGKPCZBNM

Slide 16

Slide 16 text

少し時間を置いてからJobを確認する LVCFDUMHFUKPCPZBNM BQJ7FSTJPOW JUFNT BQJ7FSTJPOCBUDIW LJOE+PC NFUBEBUB BOOPUBUJPOT LVFVFYLTJPRVFVFOBNFNBJO HFOFSBUF/BNFTBNQMFKPC MBCFMT DPOUSPMMFSVJEGDFDBBGBFGD KPCOBNFTBNQMFKPCMK OBNFTBNQMFKPCMK OBNFTQBDFEFGBVMU ʜ TQFDTVTQFOEUSVF͕ফ͑ͯΔ CBDLP⒎-JNJU DPNQMFUJPOT QBSBMMFMJTN TFMFDUPS NBUDI-BCFMT DPOUSPMMFSVJEGDFDBBGBFGD ʜ

Slide 17

Slide 17 text

Podが起動する LVCFDUMHFUQPE /".&3&"%:45"5643&45"354"(& TBNQMFKPCMKKGL$PNQMFUFENT TBNQMFKPCMKNGLS$PNQMFUFENT TBNQMFKPCMKWONQ$PNQMFUFENT

Slide 18

Slide 18 text

デモまとめ ▶ Kueueをインストールする ▶ ResourceFlavor/ClusterQueue/QueueリソースをセットアップするとKueueを利 ⽤する準備が完了 ▶ Jobリソースを以下のように設定して作成するとKueueがJobの スケジューリングを管理 ▶ “kueue.x-k8s.io/queue-name”アノテーションで利⽤するQueueを指定 ▶ .spec.suspend: true に設定してJobを作成 ▶ 後はKueueがClusterQueueなどの設定に応じてJobをスケジューリング (Jobの.spec.suspendをfalseに変更する) ▶ 以下のような仕組みはKubernetesの機能をそのまま利⽤ ▶ Job→Podリソースの作成 ▶ NodeへのPodのスケジューリング

Slide 19

Slide 19 text

Kueueのアーキテクチャ

Slide 20

Slide 20 text

Kueue Overview https://bit.ly/kueue-controller-design

Slide 21

Slide 21 text

Kueue Architecture

Slide 22

Slide 22 text

Kueue Architecture

Slide 23

Slide 23 text

Kueue Custom Resource

Slide 24

Slide 24 text

Kueue Custom Resource ▶ Queue: Jobリソースから利⽤するQueue ▶ Jobリソースから指定するためのClusterQueueのエイリアスになっている ▶ ClusterQueue: Kueueが提供するキューの実態となるリソース ▶ ResourceFlavor: ClusterQueueのflavor定義 ▶ Workload: KueueでJobの状態管理を⾏うためのリソース ▶ Kueue内部の処理に利⽤されるのみでユーザーが触ることは基本的に無い

Slide 25

Slide 25 text

Queue Resource type ClusterQueueReference string type QueueSpec struct { ClusterQueue ClusterQueueReference `json:"clusterQueue,omitempty"` } type QueueStatus struct { PendingWorkloads int32 `json:"pendingWorkloads"` } type Queue struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec QueueSpec `json:"spec,omitempty"` Status QueueStatus `json:"status,omitempty"` }

Slide 26

Slide 26 text

ClusterQueue Resource type ClusterQueueSpec struct { Resources []Resource `json:"resources,omitempty"` Cohort string `json:"cohort,omitempty"` QueueingStrategy QueueingStrategy `json:"queueingStrategy,omitempty"` NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` } type Resource struct { Name corev1.ResourceName `json:"name"` Flavors []Flavor `json:"flavors,omitempty"` } type Flavor struct { Name ResourceFlavorReference `json:"name"` Quota Quota `json:"quota"` } type Quota struct { Min resource.Quantity `json:"min,omitempty"` Max *resource.Quantity `json:"max,omitempty"` } type QueueingStrategy string type ResourceFlavorReference string

Slide 27

Slide 27 text

ClusterQueue Resource type ClusterQueueStatus struct { UsedResources UsedResources `json:"usedResources"` PendingWorkloads int32 `json:"pendingWorkloads"` AdmittedWorkloads int32 `json:"admittedWorkloads"` } // map[]map[]Usage type UsedResources map[corev1.ResourceName]map[string]Usage type Usage struct { Total *resource.Quantity `json:"total,omitempty"` Borrowed *resource.Quantity `json:"borrowing,omitempty"` } type ClusterQueue struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec ClusterQueueSpec `json:"spec,omitempty"` Status ClusterQueueStatus `json:"status,omitempty"` }

Slide 28

Slide 28 text

ResourceFlavor Resource type ResourceFlavor struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` // labels associated with this flavor. They are matched against or // converted to node affinity constraints on the workload’s pods. // For example, cloud.provider.com/accelerator: nvidia-tesla-k80. Labels map[string]string `json:"labels,omitempty"` // taints associated with this flavor that workloads must explicitly // “tolerate” to be able to use this flavor. // For example, cloud.provider.com/preemptible="true":NoSchedule Taints []corev1.Taint `json:"taints,omitempty"` }

Slide 29

Slide 29 text

Kueueの主な機能

Slide 30

Slide 30 text

NamespaceSelector

Slide 31

Slide 31 text

NamespaceSelector ▶ ClusterQueue.spec.namespaceSelectorを設定することでClusterQueueリソースは QueueリソースがClusterQueueリソースを利⽤可能なNamespaceを制限することが 可能 ▶ `.spec.NamespaceSelector: {}`: 全てのNamespaceがマッチする ▶ `.spec.NamespaceSelector: nill`: どのNamespaceにもマッチしない ▶ `.spec.NamespaceSelector: { “team”: “team-a" }`: `{ “team”: “team-a" }` の ラベルが設定されているNamespaceからのみ利⽤可能

Slide 32

Slide 32 text

NamespaceSelector apiVersion: kueue.x-k8s.io/v1alpha1 kind: ClusterQueue metadata: name: ns-selector-sample spec: namespaceSelector: "team": "team-a" apiVersion: v1 kind: Namespace metadata: name: team-a labels: team: team-a Match apiVersion: kueue.x-k8s.io/v1alpha1 kind: Queue metadata: namespace: team-a name: main spec: clusterQueue: ns-selector-sample

Slide 33

Slide 33 text

ResourceとResourceFlavor

Slide 34

Slide 34 text

ResourceとResourceFlavor apiVersion: kueue.x-k8s.io/v1alpha1 kind: ClusterQueue metadata: name: cluster-queue spec: namespaceSelector: {} resources: - name: cpu flavors: - name: c2-on-demand min: 1000 - name: c4-on-demand min: 1000 apiVersion: kueue.x-k8s.io/v1alpha1 kind: ResourceFlavor metadata: name: c2-on-demand labels: cloud.provider.com/vm-family: c2 apiVersion: kueue.x-k8s.io/v1alpha1 kind: ResourceFlavor metadata: name: c4-on-demand labels: cloud.provider.com/vm-family: c4 上記の例の場合、”cluster-queue" キューはCPUリソースについて 1. c2-on-demand 2. c4-on-demand の2種類のflavorが設定されており、各flavorごとに1000CPU(計2000CPU)リソースを利⽤することができる、という設定になる Jobの設定がResourceFlavorの • labels • taints 条件にマッチするflavorが利⽤される

Slide 35

Slide 35 text

ResourceとResourceFlavor ▶ ClusterQueueのリソース容量はリソース種類 * flavorで設定される ▶ ResourceFlavorは ▶ Taints ▶ Labels にフィールドによってJobとマッチするflavorを設定することができる

Slide 36

Slide 36 text

Cohort

Slide 37

Slide 37 text

Cohort ▶ `ClusterQueue.spec.cohort` (string)を設定することで、同名のcohortを設定した ClusterQueue(のflavor)からリソースを借りることができるようになる ▶ 借りられるリソースの最⼤値は `ClusterQueue.spec.resources[].flavors[].quota.max`の値となる ▶ cohort=歩兵隊/軍隊/仲間/群などの意味らしい

Slide 38

Slide 38 text

Cohort apiVersion: kueue.x-k8s.io/v1alpha1 kind: ClusterQueue metadata: name: tenantA spec: cohort: borrowing-cohort resources: - name: nvidia.com/gpus flavors: - name: k80 quota: min: 10 max: 20 apiVersion: kueue.x-k8s.io/v1alpha1 kind: ClusterQueue metadata: name: tenantB spec: cohort: borrowing-cohort resources: - name: nvidia.com/gpus flavors: - name: k80 quota: min: 10 max: 20 下記の例の場合 `nvidia.com/gpus` リソースをClusterQueue単体だと最⼤で10までし か同時に利⽤できないが、Cohort機能で借りることで最⼤20まで同時に利⽤すること ができるようになる

Slide 39

Slide 39 text

QueueingStrategy

Slide 40

Slide 40 text

QueueingStrategy 現在Kueueでは以下の2種類のキューイング戦略を選択することが可能 デフォルト: BestEffortFIFO ▶ StrictFIFO: 優先度 > キューイング時刻の順にソートされる ▶ (ClusterQueueのリソース不⾜などにより)実⾏が許可されないJobがある場合: ▶ このJobよりも後からキューイングされたJobが使⽤可能なクォータに収まっている場合 でも、後からキューイングされたJobをブロックしてJobが実⾏可能になるまで待機する ▶ 優先度は `job.Spec.Template.Spec.PriorityClassName` で指定された `PriorityClass` に 設定される値により決定される ▶ BestEffortFIFO: ソート順はStrictFIFOと同様 ▶ しかし実⾏が許可されないJobがある場合は使⽤可能なクォータに収まっている場合他の Jobの実⾏をブロックしない

Slide 41

Slide 41 text

QueueingStrategy apiVersion: kueue.x-k8s.io/v1alpha1 kind: ClusterQueue metadata: name: queue-strict spec: namespaceSelector: {} queueingStrategy: "StrictFIFO" resources: - name: cpu flavors: - name: default min: 50 apiVersion: batch/v1 kind: Job metadata: generateName: sample-job- annotations: kueue.x-k8s.io/queue-name: main spec: parallelism: 1 completions: 1 suspend: true template: spec: priorityClassName: high-priority containers: - name: dummy-job image: sleep:latest args: ["15s"] resources: requests: cpu: “2" apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 Queue

Slide 42

Slide 42 text

Kueueの実装

Slide 43

Slide 43 text

Kueue Architecture(再掲)

Slide 44

Slide 44 text

Kueueはcontroller-runtime(v0.11.1)によって実装されてる func main() { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), options) # managerを⽣成 if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) } queues := queue.NewManager(mgr.GetClient()) // ClusterQueueを処理するQueueManagerを⽣成 cCache := cache.New(mgr.GetClient()) // 計算⽤のリソース使⽤量を管理するCacheManagerを⽣成 if failedCtrl, err := core.SetupControllers(mgr, queues, cCache); err != nil { // 各種Controllerを登録 setupLog.Error(err, "Unable to create controller", "controller", failedCtrl) } if err = job.NewReconciler(mgr.GetScheme(), // Job Controllerを登録 mgr.GetClient(), mgr.GetEventRecorderFor(constants.JobControllerName), job.WithManageJobsWithoutQueueName(config.ManageJobsWithoutQueueName), ).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Job") os.Exit(1) } ctx := ctrl.SetupSignalHandler() go func() { // Queueのクリーンアップ処理を⾏うgo routineを起動 queues.CleanUpOnContext(ctx) }() sched := scheduler.New(queues, cCache, mgr.GetClient(), // スケジューラーを⽣成〜起動 mgr.GetEventRecorderFor(constants.ManagerName)) go func() { sched.Start(ctx) }() ... } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/main.go

Slide 45

Slide 45 text

main.goで起動しているアプリケーション群 NBJOHPͰىಈ͞ΕΔ ΞϓϦέʔγϣϯ܈

Slide 46

Slide 46 text

controller-runtime ▶ controller-runtimeはKubernetes Operator を実装するためのフレームワーク ▶ ControllerやCRDの他Webhookの実装も可能 ▶ controller-runtimeについては以前 ”作って学ぶkubebuilder⼊⾨”というタイトル でLTをしているので興味があればそちらを参照 してください ▶ https://speakerdeck.com/bells17/ kubebuilder-introduction https://github.com/kubernetes-sigs/kubebuilder/blob/master/docs/book/src/kb_concept_diagram.svg

Slide 47

Slide 47 text

Kueue adminがリソースを作成した際に起こること 1. リソースを 作成 2. 各Controllerがリソース作 成イベントをトリガーにして キャッシュや QueueManagerのデータを アップデート

Slide 48

Slide 48 text

Kueueの各CRDのController実装では通常EventFilteringを⾏う箇所を利⽤して キャッシュの更新やキューの作成処理を⾏っている func (r *ClusterQueueReconciler) Create(e event.CreateEvent) bool { cq, match := e.Object.(*kueue.ClusterQueue) if !match { // No need to interact with the cache for other objects. return true } log := r.log.WithValues("clusterQueue", klog.KObj(cq)) log.V(2).Info("ClusterQueue create event") ctx := ctrl.LoggerInto(context.Background(), log) if err := r.cache.AddClusterQueue(ctx, cq); err != nil { // ClusterQueueをキャッシュに追加 log.Error(err, "Failed to add clusterQueue to cache") } if err := r.qManager.AddClusterQueue(ctx, cq); err != nil { // ClusterQueueをQueueManagerに追加 log.Error(err, "Failed to add clusterQueue to queue manager") } return true } func (r *ClusterQueueReconciler) SetupWithManager(mgr ctrl.Manager) error { wHandler := cqWorkloadHandler{ qManager: r.qManager, } return ctrl.NewControllerManagedBy(mgr). For(&kueue.ClusterQueue{}). Watches(&source.Channel{Source: r.wlUpdateCh}, &wHandler). WithEventFilter(r). // 通常イベントをフィルタリングする⽤途で使⽤するEventFilterを利⽤したキャッシュとキューの更新処理 Complete(r) } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/controller/core/clusterqueue_controller.go

Slide 49

Slide 49 text

Job作成時に起きていること 1. Jobを作成 3. WorkloadControllerが キャッシュのアップデートや QueueManager内の対象と なるQueueにジョブを追加 を⾏う 2. JobControllerがJob作成を トリガーにWorkloadリソースを⽣成 4. SchedulerがQueue からジョブを取り出し admit可能かを検証〜OK ならadmin処理を⾏う 5. JobControllerが workloadがadmitされた ことをトリガーにJobを unsuspendする

Slide 50

Slide 50 text

Queueのジョブソートには”container/heap”が利⽤されている func (c *ClusterQueueImpl) pushIfNotPresent(info *workload.Info) bool { item := c.heap.items[workload.Key(info.Obj)] if item != nil { return false } heap.Push(&c.heap, *info) return true } func (c *ClusterQueueImpl) PushOrUpdate(w *kueue.Workload) { item := c.heap.items[workload.Key(w)] info := *workload.NewInfo(w) if item == nil { heap.Push(&c.heap, info) return } item.obj = info heap.Fix(&c.heap, item.index) } func (c *ClusterQueueImpl) Delete(w *kueue.Workload) { item := c.heap.items[workload.Key(w)] if item != nil { heap.Remove(&c.heap, item.index) } } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/queue/cluster_queue_impl.go

Slide 51

Slide 51 text

”container/heap”の実装例 type IntHeap []int func (h IntHeap) Len() int { return len(h) } func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *IntHeap) Push(x any) { *h = append(*h, x.(int)) } func (h *IntHeap) Pop() any { old := *h n := len(old) x := old[n-1] *h = old[0 : n-1] return x } func main() { h := &IntHeap{2, 1, 5} heap.Init(h) heap.Push(h, 3) fmt.Printf("minimum: %d\n", (*h)[0]) for h.Len() > 0 { fmt.Printf("%d ", heap.Pop(h)) } } https://pkg.go.dev/container/heap Output: minimum: 1 1 2 3 5

Slide 52

Slide 52 text

schedulerは以下のようにしてWorkload(Job)がadmin可能かを検証~ OKの場合にadmitを実⾏する func (s *Scheduler) schedule(ctx context.Context) { headWorkloads := s.queues.Heads(ctx) // 1. Queueからworkloadを取得 snapshot := s.cache.Snapshot() // 2. キャッシュからsnaoshotを⽣成 entries := s.nominate(ctx, headWorkloads, snapshot) // 3. admit可能かどうかを検証 sort.Sort(entryOrdering(entries)) // 4. cohortとタイムスタンプに基づいてソート // 5. 対象となるentryに対してadmitを実⾏ for i := range entries { e := &entries[i] ... if err := s.admit(ctrl.LoggerInto(ctx, log), e); err == nil { e.status = assumed } else { e.inadmissibleReason = fmt.Sprintf("Failed to admit workload: %v", err) } ... } // 6. adminされなかったentryをrequeue for _, e := range entries { if e.status != assumed { s.requeueAndUpdate(log, ctx, e) } } } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go

Slide 53

Slide 53 text

JobControllerによるWorkload作成~Jobのsuspend/unsuspend処理の実装 func (r *JobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { ... // workloadが無くてjobが完了した訳ではないのであればworkloadを作成 jobFinishedCond, jobFinished := jobFinishedCondition(&job) if wl == nil { if jobFinished { return ctrl.Result{}, nil } err := r.handleJobWithNoWorkload(ctx, &job) return ctrl.Result{}, err } // admitされていればJobをunsuspendにする if jobSuspended(&job) { if wl.Spec.Admission != nil { err := r.startJob(ctx, wl, &job) return ctrl.Result{}, err } ... return ctrl.Result{}, nil } // admitされて無いにも関わらずJobが動作していればJobをsuspendにする if wl.Spec.Admission == nil { err := r.stopJob(ctx, wl, &job, "Not admitted by cluster queue") return ctrl.Result{}, err } ... return ctrl.Result{}, nil } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/controller/workload/job/job_controller.go

Slide 54

Slide 54 text

その他Tips(Kueue実装から学んだこと)

Slide 55

Slide 55 text

[controller-runtime] contextを通してloggerオブジェクトをやり取りすることができる func (s *Scheduler) Start(ctx context.Context) { log := ctrl.LoggerFrom(ctx).WithName("scheduler") ctx = ctrl.LoggerInto(ctx, log) wait.UntilWithContext(ctx, s.schedule, 0) } func (s *Scheduler) schedule(ctx context.Context) { log := ctrl.LoggerFrom(ctx) ... } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/scheduler/scheduler.go

Slide 56

Slide 56 text

[controller-runtime] option設定をコマンドラインオプション+設定ファイルで⾏う⽅法 func main() { var configFile string flag.StringVar(&configFile, "config", "", "The controller will load its initial configuration from this file. "+ "Omit this flag to use the default configuration values. ") options := ctrl.Options{ Scheme: scheme, HealthProbeBindAddress: ":8081", MetricsBindAddress: ":8080", Port: 9443, LeaderElectionID: "c1f6bfd2.kueue.x-k8s.io", } var err error config := configv1alpha1.Configuration{} if configFile != "" { options, err = options.AndFrom(ctrl.ConfigFile().AtPath(configFile).OfKind(&config)) if err != nil { setupLog.Error(err, "unable to load the config file") os.Exit(1) } cfgStr, err := encodeConfig(&config) if err != nil { setupLog.Error(err, "unable to encode config file") os.Exit(1) } setupLog.Info("Successfully loaded config file", "config", cfgStr) } ... } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/main.go

Slide 57

Slide 57 text

[controller-runtime] 設定ファイル定義 type Configuration struct { metav1.TypeMeta `json:",inline"` // ControllerManagerConfigurationSpec returns the configurations for controllers cfg.ControllerManagerConfigurationSpec `json:",inline"` // ManageJobsWithoutQueueName controls whether or not Kueue reconciles // batch/v1.Jobs that don't set the annotation kueue.x-k8s.io/queue-name. // If set to true, then those jobs will be suspended and never started unless // they are assigned a queue and eventually admitted. This also applies to // jobs created before starting the kueue controller. // Defaults to false; therefore, those jobs are not managed and if they are created // unsuspended, they will start immediately. ManageJobsWithoutQueueName bool `json:"manageJobsWithoutQueueName"` } func init() { SchemeBuilder.Register(&Configuration{}) } https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/apis/config/v1alpha1/configuration_types.go

Slide 58

Slide 58 text

[controller-runtime] intやboolなどをpointer値で設定するためのutility package pointer import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/utils/pointer" ) func Quantity(q resource.Quantity) *resource.Quantity { return &q } var ( Int32 = pointer.Int32 Int64 = pointer.Int64 Bool = pointer.Bool ) https://github.com/kubernetes-sigs/kueue/blob/v0.1.0/pkg/util/pointer/pointer.go

Slide 59

Slide 59 text

まとめ

Slide 60

Slide 60 text

まとめ ▶ Kueueの全体像や各種機能をデモを交えながら簡単に説明しました ▶ まだKueueは開発が始まったばかりなので、本格的な利⽤には機能が⾜りないという可能 性もありそうですが、キューを使った単純なJobリソースのON/OFFを⾏うだけなので、シ ンプルにJobをキューイングしたいくらいのユースケースでは問題なく使えるのでは無いか と思います ▶ controller-runtimeのEventFilteringをQueue管理などの処理に利⽤したり設定ファイルに よるアプリケーション設定⽅法など、その他のController開発に活かせそうな知⾒をKueue のコードリーディングから得ることができました

Slide 61

Slide 61 text

参考資料 ▶ コードリーディングメモ: https://zenn.dev/bells17/scraps/16625963e51d23 ▶ 動作確認⽤manifests: https://github.com/bells17/tmp/tree/main/kueue-example ▶ リポジトリ: https://github.com/kubernetes-sigs/kueue/tree/v0.1.0 ▶ Design Docs(controller): https://bit.ly/kueue-controller-design ▶ Design Docs(API): https://bit.ly/kueue-apis ▶ Old Proposal: https://bit.ly/k8s-job-management

Slide 62

Slide 62 text

Thanks / Question? ▶ @bells17 ▶ Slide: https://speakerdeck.com/bells17 ▶ @bells17_