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

Kueueアーキテクチャ/Kueue Architecture

Kueueアーキテクチャ/Kueue Architecture

参考リンク一覧:
コードリーディングメモ: 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

---

https://youtu.be/CFUfw3cMNI8?t=724
にてこのスライドを使ったKueueの解説セッションを行いましたので動画で見たい方はこちらでどうぞ

bells17

May 24, 2022
Tweet

More Decks by bells17

Other Decks in Programming

Transcript

  1. Kueueアーキテクチャ
    @bells17

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. Kueueとは?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. 少し時間を置いてから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
    ʜ

    View Slide

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

    View Slide

  18. デモまとめ
    ▶ 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のスケジューリング

    View Slide

  19. Kueueのアーキテクチャ

    View Slide

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

    View Slide

  21. Kueue Architecture

    View Slide

  22. Kueue Architecture

    View Slide

  23. Kueue Custom Resource

    View Slide

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

    View Slide

  25. 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"`
    }

    View Slide

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

    View Slide

  27. 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"`
    }

    View Slide

  28. 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"`
    }

    View Slide

  29. Kueueの主な機能

    View Slide

  30. NamespaceSelector

    View Slide

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

    View Slide

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

    View Slide

  33. ResourceとResourceFlavor

    View Slide

  34. 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が利⽤される

    View Slide

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

    View Slide

  36. Cohort

    View Slide

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

    View Slide

  38. 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まで同時に利⽤すること
    ができるようになる

    View Slide

  39. QueueingStrategy

    View Slide

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

    View Slide

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

    View Slide

  42. Kueueの実装

    View Slide

  43. Kueue Architecture(再掲)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. ”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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. [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

    View Slide

  56. [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

    View Slide

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

    View Slide

  58. [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

    View Slide

  59. まとめ

    View Slide

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

    View Slide

  61. 参考資料
    ▶ コードリーディングメモ: 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

    View Slide

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

    View Slide