本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kueue 簡介

無論是內部部署還是雲端,叢集都面臨資源使用、配額和成本管理方面的實際限制。無論自動擴展能力如何,叢集都具有有限的容量。因此,使用者希望有一種簡單的方法來公平且有效率地共享資源。

在本文中,我們介紹 Kueue,這是一個開源工作佇列控制器,旨在將批次工作作為單個單元進行管理。Kueue 將 Pod 層級的協調留給 Kubernetes 的現有穩定組件。Kueue 原生支援 Kubernetes Job API,並提供掛鉤以整合其他用於批次工作的客製化 API。

為什麼選擇 Kueue?

工作佇列是在內部部署和雲端環境中大規模運行批次工作負載的關鍵功能。工作佇列的主要目標是管理多個租戶共享的有限資源池的存取權。工作佇列決定哪些工作應該等待、哪些可以立即開始以及它們可以使用哪些資源。

最需要的工作佇列需求包括

  • 配額和預算,以控制誰可以使用什麼以及達到什麼限制。這不僅在具有靜態資源(如內部部署)的叢集中是必需的,而且在雲端環境中也是必需的,以控制支出或稀缺資源的使用。
  • 租戶之間公平共享資源。為了最大限度地利用可用資源,應允許在活動租戶之間公平共享分配給非活動租戶的任何未使用配額。
  • 根據可用性在不同資源類型之間靈活放置工作。這在具有異質資源(例如不同的架構(GPU 或 CPU 型號)和不同的佈建模式(現貨與隨需))的雲端環境中非常重要。
  • 支援可以按需佈建資源的自動擴展環境。

普通的 Kubernetes 無法滿足上述需求。在正常情況下,一旦建立 Job,job-controller 會立即建立 Pod,而 kube-scheduler 會持續嘗試將 Pod 指派給節點。在大規模情況下,這種情況可能會使控制平面不堪負荷。目前也沒有好的方法可以在 Job 層級控制哪些 Job 應該首先獲得哪些資源,也沒有辦法表達順序或公平共享。目前的 ResourceQuota 模型並不適合這些需求,因為配額是在資源建立時強制執行的,並且沒有請求佇列。ResourceQuota 的目的是提供內建的可靠性機制,以及管理員保護叢集免於故障轉移所需的原則。

在 Kubernetes 生態系統中,有幾種用於工作排程的解決方案。但是,我們發現這些替代方案有一個或多個以下問題

  • 它們取代了 Kubernetes 的現有穩定組件,例如 kube-scheduler 或 job-controller。這不僅從操作的角度來看是有問題的,而且 Job API 中的重複也會導致生態系統的碎片化並降低可移植性。
  • 它們不與自動擴展整合,或
  • 它們缺乏對資源彈性的支援。

Kueue 的運作方式

透過 Kueue,我們決定採用不同的 Kubernetes 工作佇列方法,該方法圍繞以下幾個方面

  • 不重複現有 Kubernetes 組件已提供的現有功能,用於 Pod 排程、自動擴展和工作生命週期管理。
  • 新增現有組件缺少的關鍵功能。例如,我們在 Job API 中投入了更多資源,以涵蓋更多使用案例,例如 IndexedJob修復與 Pod 追蹤相關的長期存在的問題。雖然此路徑需要更長的時間才能實現功能,但我們相信這是更永續的長期解決方案。
  • 確保與運算資源具有彈性和異質性的雲端環境相容。

為了使這種方法可行,Kueue 需要旋鈕來影響那些已建立組件的行為,以便它可以有效地管理何時以及何處開始工作。我們以兩個功能的形式將這些旋鈕新增至 Job API

  • Suspend 欄位,允許 Kueue 向 job-controller 發出訊號,指示何時啟動或停止 Job。
  • 可變排程指令,允許 Kueue 在啟動 Job 之前更新 Job 的 .spec.template.spec.nodeSelector。這樣,Kueue 可以在控制 Pod 放置的同時,仍然將實際的 Pod 到節點排程委派給 kube-scheduler。

請注意,如果任何客製化 Job API 提供上述兩個功能,則可以由 Kueue 管理。

資源模型

Kueue 定義了新的 API,以滿足本文開頭提到的需求。三個主要的 API 是

  • ResourceFlavor:叢集範圍的 API,用於定義可用於消耗的資源類型,例如 GPU 型號。ResourceFlavor 的核心是一組標籤,反映了提供這些資源的節點上的標籤。
  • ClusterQueue:叢集範圍的 API,用於透過為一個或多個 ResourceFlavor 設定配額來定義資源池。
  • LocalQueue:命名空間範圍的 API,用於分組和管理單一租戶工作。在其最簡單的形式中,LocalQueue 是指向 ClusterQueue 的指標,租戶(以命名空間建模)可以使用該指標來啟動其工作。

如需更多詳細資訊,請查看 API 概念文件。雖然這三個 API 看起來可能令人難以承受,但 Kueue 的大多數操作都以 ClusterQueue 為中心;ResourceFlavor 和 LocalQueue API 主要是組織包裝器。

範例使用案例

想像以下在雲端上的 Kubernetes 叢集上運行批次工作負載的設定

  • 您的叢集中安裝了 cluster-autoscaler,以自動調整叢集的大小。
  • 有兩種自動擴展節點群組,它們的佈建原則不同:現貨和隨需。每個群組的節點都透過標籤 instance-type=spotinstance-type=ondemand 區分。此外,由於並非所有 Job 都能容忍在現貨節點上運行,因此節點被 spot=true:NoSchedule 污染。
  • 為了在成本和資源可用性之間取得平衡,假設您希望 Job 使用最多 1000 個隨需節點核心,然後使用最多 2000 個現貨節點核心。

作為批次系統的管理員,您定義了兩個 ResourceFlavor 來表示兩種節點類型

---
apiVersion: kueue.x-k8s.io/v1alpha2
kind: ResourceFlavor
metadata:
  name: ondemand
  labels:
    instance-type: ondemand 
---
apiVersion: kueue.x-k8s.io/v1alpha2
kind: ResourceFlavor
metadata:
  name: spot
  labels:
    instance-type: spot
taints:
- effect: NoSchedule
  key: spot
  value: "true"

然後,您透過建立如下所示的 ClusterQueue 來定義配額

apiVersion: kueue.x-k8s.io/v1alpha2
kind: ClusterQueue
metadata:
  name: research-pool
spec:
  namespaceSelector: {}
  resources:
  - name: "cpu"
    flavors:
    - name: ondemand
      quota:
        min: 1000
    - name: spot
      quota:
        min: 2000

請注意,ClusterQueue 資源中類型的順序很重要:除非 Job 明確偏好特定類型,否則 Kueue 將嘗試根據順序將 Job 安裝到可用配額中。

對於每個命名空間,您定義一個指向上述 ClusterQueue 的 LocalQueue

apiVersion: kueue.x-k8s.io/v1alpha2
kind: LocalQueue
metadata:
  name: training
  namespace: team-ml
spec:
  clusterQueue: research-pool

管理員一次建立上述設定。批次使用者可以透過列出其命名空間中的 LocalQueue 來找到他們被允許提交到的佇列。命令類似於以下內容:kubectl get -n my-namespace localqueues

若要提交工作,請建立 Job 並設定 kueue.x-k8s.io/queue-name 註釋,如下所示

apiVersion: batch/v1
kind: Job
metadata:
  generateName: sample-job-
  annotations:
    kueue.x-k8s.io/queue-name: training
spec:
  parallelism: 3
  completions: 3
  template:
    spec:
      tolerations:
      - key: spot
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: example-batch-workload
        image: registry.example/batch/calculate-pi:3.14
        args: ["30s"]
        resources:
          requests:
            cpu: 1
      restartPolicy: Never

Kueue 會介入以在 Job 建立後立即暫停它。一旦 Job 位於 ClusterQueue 的頭部,Kueue 就會評估它是否可以啟動,方法是檢查 Job 請求的資源是否符合可用配額。

在上面的範例中,Job 可以容忍現貨資源。如果先前已允許的 Job 消耗了所有現有的隨需配額,但並非所有現貨配額,則 Kueue 會使用現貨配額允許 Job 進入。Kueue 透過向 Job 物件發出單個更新來執行此操作,該更新

  • .spec.suspend 標誌變更為 false
  • 將術語 instance-type: spot 新增至 Job 的 .spec.template.spec.nodeSelector,以便當 Pod 由 Job 控制器建立時,這些 Pod 只能排程到現貨節點上。

最後,如果有可用的空節點且具有相符的節點選取器術語,則 kube-scheduler 將直接排程 Pod。否則,kube-scheduler 最初會將 Pod 標記為不可排程,這將觸發 cluster-autoscaler 佈建新節點。

未來工作和參與

上面的範例簡要介紹了 Kueue 的一些功能,包括對配額、資源彈性和與叢集自動擴展器的整合的支援。Kueue 還支援公平共享、工作優先順序和不同的佇列策略。查看 Kueue 文件,以瞭解有關這些功能以及如何使用 Kueue 的更多資訊。

我們計劃向 Kueue 新增許多功能,例如階層式配額、預算和對動態調整大小的工作的支援。在更近期的未來,我們專注於新增對工作搶佔的支援。

最新的 Kueue 版本可在 Github 上取得;如果您在 Kubernetes 上運行批次工作負載,請試用它(需要 v1.22 或更新版本)。我們正處於這個專案的早期階段,我們正在尋求各種層級的回饋,無論是主要的還是次要的,所以請隨時與我們聯繫。我們也歡迎更多貢獻者,無論是修復或報告錯誤,還是協助新增新功能或編寫文件。您可以透過我們的 repo郵件列表Slack 與我們聯繫。

最後但並非最不重要的一點,感謝所有 我們的貢獻者,他們使這個專案成為可能!