使用 Kueue 部署批次系統

本教學課程說明如何使用 Kueue 在 Google Kubernetes Engine (GKE) 上排定 Job,以最佳化可用資源。在本教學課程中,您將瞭解如何使用 Kueue 有效管理及排定批次作業、提升資源使用率,以及簡化工作負載管理作業。您為兩個租戶團隊設定共用叢集,每個團隊都有自己的命名空間,且每個團隊建立的工作都會共用全域資源。您也可以設定 Kueue,根據您定義的資源配額排定 Job。

本教學課程的適用對象為有興趣使用 GKE 實作批次系統的雲端架構師和平台工程師。如要進一步瞭解內容中提及的常見角色和範例工作,請參閱「常見的 GKE 使用者角色和工作」。 Google Cloud

閱讀本頁面之前,請先熟悉下列概念:

背景

作業是指會執行完畢的應用程式,例如機器學習、算繪、模擬、分析、CI/CD 和類似工作負載。

Kueue 是雲端原生的工作排程器,可與預設的 Kubernetes 排程器、工作控制器和叢集自動調度器搭配使用,提供端對端的批次系統。Kueue 會實作工作佇列,並根據配額和資源共用階層,決定工作應等待的時間和啟動時間,確保各團隊能公平地共用資源。

Kueue 具有下列特徵:

  • 這項服務專為雲端架構設計,可處理異質、可互換及可擴充的資源。
  • 這項服務提供一組 API,可管理彈性配額和工作排隊。
  • 不會重新實作現有功能,例如自動調度資源、Pod 排程或 Job 生命週期管理。
  • Kueue 內建 Kubernetesbatch/v1.Job API 支援功能。
  • 可與其他工作 API 整合。

為避免與特定 Kubernetes Job API 混淆,Kueue 會將以任何 API 定義的工作稱為「工作負載」。

建立 ResourceFlavor

ResourceFlavor 物件代表叢集中可用節點的變化,方法是將節點與節點標籤和汙點建立關聯。舉例來說,您可以使用 ResourceFlavors 代表具有不同佈建保證的 VM (例如 Spot 與隨選)、架構 (例如 x86 與 ARM CPU)、品牌和型號 (例如 Nvidia A100 與 T4 GPU)。

在本教學課程中,kueue-autopilot 叢集具有同質資源。因此,請為 CPU、記憶體、暫時性儲存空間和 GPU 建立單一 ResourceFlavor,且不含標籤或汙點。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: default-flavor # This ResourceFlavor will be used for all the resources
部署 ResourceFlavor:

kubectl apply -f flavors.yaml

建立 ClusterQueue

ClusterQueue 是叢集範圍的物件,可管理 CPU、記憶體、GPU 等資源集區。負責管理 ResourceFlavor,限制用量,並決定工作負載的許可順序。

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: cluster-queue
spec:
  namespaceSelector: {} # Available to all namespaces
  queueingStrategy: BestEffortFIFO # Default queueing strategy
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu", "ephemeral-storage"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 10
      - name: "memory"
        nominalQuota: 10Gi
      - name: "nvidia.com/gpu"
        nominalQuota: 10
      - name: "ephemeral-storage"
        nominalQuota: 10Gi

部署 ClusterQueue:

kubectl apply -f cluster-queue.yaml

耗用順序取決於 .spec.queueingStrategy,其中有兩項設定:

  • BestEffortFIFO

    • 預設的佇列策略設定。
    • 工作負載會依先進先出 (FIFO) 規則加入佇列,但如果配額不足以允許佇列最前頭的工作負載,系統會嘗試允許佇列中的下一個工作負載。
  • StrictFIFO

    • 保證 FIFO 語意。
    • 佇列開頭的工作負載可能會封鎖佇列,直到工作負載可接受為止。

cluster-queue.yaml 中,您會建立名為 cluster-queue 的新 ClusterQueue。這個 ClusterQueue 會管理四項資源:cpumemorynvidia.com/gpuephemeral-storage,並使用 flavors.yaml 中建立的風味。工作負載 Pod 規格中的要求會耗用配額。

每個變種版本都有用量限制,以 .spec.resourceGroups[].flavors[].resources[].nominalQuota 表示。在本例中,只有在下列情況下,ClusterQueue 才會允許工作負載:

  • CPU 要求總和小於或等於 10
  • 記憶體要求總和小於或等於 10Gi
  • GPU 要求總和小於或等於 10
  • 使用的儲存空間總和必須小於或等於 10 Gi

建立 LocalQueue

LocalQueue 是命名空間物件,可接受命名空間中使用者提供的工作負載。不同命名空間的 LocalQueue 可以指向同一個 ClusterQueue,並共用資源配額。在本例中,命名空間 team-ateam-b 中的 LocalQueue 會指向 .spec.clusterQueue 下的相同 ClusterQueue cluster-queue

apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-a # LocalQueue under team-a namespace
  name: lq-team-a
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-b # LocalQueue under team-b namespace
  name: lq-team-b
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue

每個團隊都會將工作負載傳送至各自命名空間中的 LocalQueue。 然後由 ClusterQueue 分配資源。

部署 LocalQueues:

kubectl apply -f local-queue.yaml

建立工作並觀察允許的工作負載

在本節中,您將在 team-a 命名空間中建立 Kubernetes Job。Kubernetes 中的 Job 控制器會建立一或多個 Pod,並確保這些 Pod 成功執行特定工作。

命名空間 team-a 中的 Job 具有下列屬性:

  • 它會指向 lq-team-a LocalQueue。
  • 方法是將 nodeSelector 欄位設為 nvidia-tesla-t4,要求 GPU 資源。
  • 這三個 Pod 會平行休眠 10 秒。系統會根據 ttlSecondsAfterFinished 欄位中定義的值,在 60 秒後清除工作。
  • 由於有三個 Pod,因此需要 1,500 milliCPU、1536 Mi 的記憶體、1,536 Mi 的臨時儲存空間和三個 GPU。
apiVersion: batch/v1
kind: Job
metadata:
  namespace: team-a # Job under team-a namespace
  generateName: sample-job-team-a-
  annotations:
    kueue.x-k8s.io/queue-name: lq-team-a # Point to the LocalQueue
spec:
  ttlSecondsAfterFinished: 60 # Job will be deleted after 60 seconds
  parallelism: 3 # This Job will have 3 replicas running at the same time
  completions: 3 # This Job requires 3 completions
  suspend: true # Set to true to allow Kueue to control the Job when it starts
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-accelerator: "nvidia-tesla-t4" # Specify the GPU hardware
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:latest
        args: ["10s"] # Sleep for 10 seconds
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
      restartPolicy: Never

系統也會在檔案 job-team-b.yaml 下建立工作,其命名空間屬於 team-b,並根據不同團隊的需求提出要求。

詳情請參閱在 Autopilot 中部署 GPU 工作負載

  1. 在新的終端機中,觀察每兩秒重新整理一次的 ClusterQueue 狀態:

    watch -n 2 kubectl get clusterqueue cluster-queue -o wide
    
  2. 在新的終端機中,觀察節點的狀態:

    watch -n 2 kubectl get nodes -o wide
    
  3. 在新的終端機中,每 10 秒從命名空間 team-ateam-b 建立 Jobs to LocalQueue:

    ./create_jobs.sh job-team-a.yaml job-team-b.yaml 10
    
  4. 觀察排入佇列的 Job、ClusterQueue 中允許的 Job,以及透過 GKE Autopilot 啟動的節點。

  5. 從命名空間 team-a 取得 Job:

    kubectl -n team-a get jobs
    

    結果類似下方:

    NAME                      COMPLETIONS   DURATION   AGE
    sample-job-team-b-t6jnr   3/3           21s        3m27s
    sample-job-team-a-tm7kc   0/3                      2m27s
    sample-job-team-a-vjtnw   3/3           30s        3m50s
    sample-job-team-b-vn6rp   0/3                      40s
    sample-job-team-a-z86h2   0/3                      2m15s
    sample-job-team-b-zfwj8   0/3                      28s
    sample-job-team-a-zjkbj   0/3                      4s
    sample-job-team-a-zzvjg   3/3           83s        4m50s
    
  6. 複製上一個步驟中的 Job 名稱,並透過 Workloads API 觀察 Job 的許可狀態和事件:

    kubectl -n team-a describe workload JOB_NAME
    
  7. 當待處理的工作開始從 ClusterQueue 增加時,請在執行中的指令碼上按 CTRL + C,結束指令碼。

  8. 所有作業完成後,請注意節點縮減。