在 GKE 上實作工作佇列系統,並在命名空間之間共用配額


本教學課程使用 Kueue,說明如何實作工作佇列系統、在 Google Kubernetes Engine (GKE) 上設定不同命名空間之間的工作負載資源和配額共用,以及如何盡量提高叢集使用率。

背景

身為基礎架構工程師或叢集管理員,盡量提高命名空間之間的資源用量非常重要。一個命名空間中的工作批次可能無法充分使用指派給該命名空間的完整配額,而另一個命名空間可能有多個待處理工作。如要在不同命名空間的工作之間有效運用叢集資源,並提高配額管理彈性,可以在 Kueue 中設定同類群組。同類群組是一組 ClusterQueue,可互相借用未使用的配額。 ClusterQueue 會控管 CPU、記憶體和硬體加速器等資源集區。

如要進一步瞭解這些概念,請參閱 Kueue 說明文件

目標

本教學課程的適用對象為基礎架構工程師或叢集管理員,他們想在 Kubernetes 上使用 Kueue 實作工作佇列系統,並共用配額。

本教學課程會模擬兩個不同命名空間中的兩個團隊,每個團隊都有專屬資源,但可以互相借用。當工作累積時,第三組資源可做為溢出資源。

使用 Prometheus 運算子監控不同命名空間中的工作和資源分配。

本教學課程包含下列必要步驟:

  1. 建立 GKE 叢集
  2. 建立 ResourceFlavors
  3. 為每個團隊建立 ClusterQueueLocalQueue
  4. 建立工作並觀察允許的工作負載
  5. 透過同類群組借用未使用的配額
  6. 新增控管 Spot VM 的溢出 ClusterQueue

費用

本教學課程使用下列 Google Cloud的計費元件:

使用 Pricing Calculator 可根據您的預測使用量來產生費用預估。

完成本教學課程後,您可以刪除建立的資源以避免繼續計費。詳情請參閱「清除」。

事前準備

設定專案

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the GKE API.

    Enable the API

  5. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  6. Verify that billing is enabled for your Google Cloud project.

  7. Enable the GKE API.

    Enable the API

  8. 設定 Google Cloud CLI 的預設值

    1. 在 Google Cloud 控制台中啟動 Cloud Shell 執行個體:
      開啟 Cloud Shell

    2. 下載這個範例應用程式的原始碼:

      git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
      
    3. 設定預設環境變數:

      gcloud config set project PROJECT_ID
      gcloud config set compute/region COMPUTE_REGION
      

      替換下列值:

    建立 GKE 叢集

    1. 建立名為 kueue-cohort 的 GKE 叢集:

      您將在預設集區中建立 6 個節點 (每個區域 2 個),且不啟用自動調度資源功能。這些就是團隊一開始可用的所有資源,因此他們必須爭奪這些資源。

      稍後您會看到 Kueue 如何管理這兩個團隊傳送至各自佇列的工作負載。

        gcloud container clusters create kueue-cohort --location COMPUTE_REGION \
        --release-channel rapid --machine-type e2-standard-4 --num-nodes 2
      

      叢集建立完成後,結果會類似如下:

        kubeconfig entry generated for kueue-cohort.
        NAME: kueue-cohort
        LOCATION: us-central1
        MASTER_VERSION: 1.26.2-gke.1000
        MASTER_IP: 35.224.108.58
        MACHINE_TYPE: e2-medium
        NODE_VERSION: 1.26.2-gke.1000
        NUM_NODES: 6
        STATUS: RUNNING
      

      STATUSkueue-clusterRUNNING

    2. 建立名為 spot 的節點集區。

      這個節點集區使用 Spot VM,且已啟用自動調度資源功能。一開始有 0 個節點,但稍後您會提供給團隊,做為溢出容量使用。

      gcloud container node-pools create spot --cluster=kueue-cohort --location COMPUTE_REGION  \
      --spot --enable-autoscaling --max-nodes 20 --num-nodes 0 \
      --machine-type e2-standard-4
      
    3. 將 Kueue 發布版本安裝至叢集:

      VERSION=VERSION
      kubectl apply -f \
        https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
      

      VERSION 替換為最新版 Kueue 後的字母 v,例如 v0.4.0。如要進一步瞭解 Kueue 版本,請參閱「Kueue 版本」。

      等待 Kueue 控制器準備就緒:

      watch kubectl -n kueue-system get pods
      

      輸出內容應與以下內容類似,您才能繼續:

      NAME                                        READY   STATUS    RESTARTS   AGE
      kueue-controller-manager-6cfcbb5dc5-rsf8k   2/2     Running   0          3m
      
    4. 建立兩個名為 team-ateam-b 的新命名空間:

      kubectl create namespace team-a
      kubectl create namespace team-b
      

      系統會在每個命名空間中產生工作。

    建立 ResourceFlavors

    ResourceFlavor 代表叢集節點中的資源變體,例如不同的 VM (例如 Spot 與隨選)、架構 (例如 x86 與 ARM CPU)、品牌和型號 (例如 Nvidia A100 與 T4 GPU)。

    ResourceFlavor 會使用節點標籤和汙點,與叢集中的一組節點相符。

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ResourceFlavor
    metadata:
      name: on-demand # This ResourceFlavor will be used for the CPU resource
    spec:
      nodeLabels:
        cloud.google.com/gke-provisioning: standard # This label was applied automatically by GKE
    ---
    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ResourceFlavor
    metadata:
      name: spot # This ResourceFlavor will be used as added resource for the CPU resource
    spec:
      nodeLabels:  
        cloud.google.com/gke-provisioning: spot # This label was applied automatically by GKE

    在這個資訊清單中:

    • ResourceFlavor on-demand 的標籤已設為 cloud.google.com/gke-provisioning: standard
    • ResourceFlavor spot 的標籤已設為 cloud.google.com/gke-provisioning: spot

    工作負載獲派 ResourceFlavor 時,Kueue 會將工作負載的 Pod 指派給符合 ResourceFlavor 所定義節點標籤的節點。

    部署 ResourceFlavor:

    kubectl apply -f flavors.yaml
    

    建立 ClusterQueue 和 LocalQueue

    建立兩個 ClusterQueue cq-team-acq-team-b,以及對應的 LocalQueue lq-team-alq-team-b,分別命名空間為 team-ateam-b

    ClusterQueue 是叢集範圍的物件,可控管 CPU、記憶體和硬體加速器等資源集區。批次管理員可以限制批次使用者查看這些物件。

    LocalQueue 是命名空間物件,批次使用者可以列出這些物件。這些指標會指向 ClusterQueues,資源會從中分配,以執行 LocalQueue 工作負載。

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ClusterQueue
    metadata:
      name: cq-team-a
    spec:
      cohort: all-teams # cq-team-a and cq-team-b share the same cohort
      namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: team-a #Only team-a can submit jobs direclty to this queue, but will be able to share it through the cohort
      resourceGroups:
      - coveredResources: ["cpu", "memory"]
        flavors:
        - name: on-demand
          resources:
          - name: "cpu"
            nominalQuota: 10
            borrowingLimit: 5
          - name: "memory"
            nominalQuota: 10Gi
            borrowingLimit: 15Gi
        - name: spot # This ClusterQueue doesn't have nominalQuota for spot, but it can borrow from others
          resources:
          - name: "cpu"
            nominalQuota: 0
          - name: "memory"
            nominalQuota: 0
    ---
    apiVersion: kueue.x-k8s.io/v1beta1
    kind: LocalQueue
    metadata:
      namespace: team-a # LocalQueue under team-a namespace
      name: lq-team-a
    spec:
      clusterQueue: cq-team-a # Point to the ClusterQueue team-a-cq

    ClusterQueues 允許資源有多種風味。在本例中,兩個 ClusterQueue 都有兩種變種版本,分別是 on-demandspot,各自提供 cpu 資源。ResourceFlavor spot 的配額設為 0,目前不會使用。

    兩個 ClusterQueue 共用同一個群組 (稱為 all-teams),定義於 .spec.cohort 中。 如果兩個以上的 ClusterQueue 共用相同群組,即可互相借用未使用的配額。

    如要進一步瞭解同類群組的運作方式和借用語意,請參閱 Kueue 說明文件

    部署 ClusterQueue 和 LocalQueue:

    kubectl apply -f cq-team-a.yaml
    kubectl apply -f cq-team-b.yaml
    

    (選用) 使用 kube-prometheus 監控工作負載

    您可以使用 Prometheus 監控有效和待處理的 Kueue 工作負載。如要監控啟動的工作負載,並觀察每個 ClusterQueue 的負載,請將 kube-prometheus 部署至命名空間 monitoring 下的叢集:

    1. 下載 Prometheus 運算子原始碼:

      cd
      git clone https://github.com/prometheus-operator/kube-prometheus.git
      
    2. 建立 CustomResourceDefinitions(CRD):

      kubectl create -f kube-prometheus/manifests/setup
      
    3. 建立監控元件:

      kubectl create -f kube-prometheus/manifests
      
    4. 允許 prometheus-operator 從 Kueue 元件擷取指標:

      kubectl apply -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/prometheus.yaml
      
    5. 變更為工作目錄:

      cd kubernetes-engine-samples/batch/kueue-cohort
      
    6. 將通訊埠轉送設定為在 GKE 叢集中執行的 Prometheus 服務:

      kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090
      
    7. 在瀏覽器中開啟 localhost:9090 的 Prometheus 網頁 UI。

      在 Cloud Shell 中:

      1. 按一下「網頁預覽」

      2. 按一下「變更通訊埠」,然後將通訊埠編號設為 9090

      3. 按一下「變更並預覽」

      系統會顯示下列 Prometheus 網頁版 UI。

      Prometheus 網頁 UI 的螢幕截圖

    8. 在「Expression」(運算式) 查詢方塊中輸入下列查詢,建立第一個面板,監控 cq-team-a ClusterQueue 的現行工作負載:

      kueue_pending_workloads{cluster_queue="cq-team-a", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-a"}
      
    9. 按一下「新增面板」

    10. 在「Expression」(運算式) 查詢方塊中輸入下列查詢,建立另一個面板來監控 cq-team-b ClusterQueue 的現行工作負載:

      kueue_pending_workloads{cluster_queue="cq-team-b", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-b"}
      
    11. 按一下「新增面板」

    12. 在「Expression」(運算式) 查詢方塊中,輸入下列查詢,建立監控叢集中節點數量的面板:

      count(kube_node_info)
      

    (選用) 使用 Google Cloud Managed Service for Prometheus 監控工作負載

    您可以使用 Google Cloud Managed Service for Prometheus 監控作用中和待處理的 Kueue 工作負載。如需完整的指標清單,請參閱 Kueue 說明文件

    1. 設定身分和 RBAC,以存取指標:

      下列設定會建立 4 個 Kubernetes 資源,為 Google Cloud Managed Service for Prometheus 收集器提供指標存取權。

      • 存取 Kueue 指標時,系統會使用 kueue-system 命名空間中的 kueue-metrics-reader ServiceAccount 進行驗證。

      • kueue-metrics-reader 服務帳戶相關聯的 Secret 會儲存驗證權杖,收集器會使用該權杖向 Kueue 部署作業公開的指標端點進行驗證。

      • kueue-system 命名空間中名為 kueue-secret-reader 的角色,可讀取含有服務帳戶權杖的密鑰。

      • 授予 kueue-metrics-reader 服務帳戶 kueue-metrics-reader ClusterRole 的 ClusterRoleBinding。

      apiVersion: v1
      kind: ServiceAccount
      metadata:
       name: kueue-metrics-reader
       namespace: kueue-system
      ---
      apiVersion: v1
      kind: Secret
      metadata:
       name: kueue-metrics-reader-token
       namespace: kueue-system
       annotations:
         kubernetes.io/service-account.name: kueue-metrics-reader
      type: kubernetes.io/service-account-token
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
       name: kueue-secret-reader
       namespace: kueue-system
      rules:
      -   resources:
       -   secrets
       apiGroups: [""]
       verbs: ["get", "list", "watch"]
       resourceNames: ["kueue-metrics-reader-token"]
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRoleBinding
      metadata:
       name: kueue-metrics-reader
      subjects:
      -   kind: ServiceAccount
       name: kueue-metrics-reader
       namespace: kueue-system
      roleRef:
       kind: ClusterRole
       name: kueue-metrics-reader
       apiGroup: rbac.authorization.k8s.io
      
    2. 為 Google Cloud Managed Service for Prometheus 設定 RoleBinding:

      視您使用的是 Autopilot 或標準叢集而定,您需要在 gke-gmp-systemgmp-system 命名空間中建立 RoleBinding。這個資源可讓收集器服務帳戶存取 kueue-metrics-reader-token 密鑰,以驗證及擷取 Kueue 指標。

      Autopilot

        apiVersion: rbac.authorization.k8s.io/v1
        kind: RoleBinding
        metadata:
          name: gmp-system:collector:kueue-secret-reader
          namespace: kueue-system
        roleRef:
          name: kueue-secret-reader
          kind: Role
          apiGroup: rbac.authorization.k8s.io
        subjects:
        -   name: collector
          namespace: gke-gmp-system
          kind: ServiceAccount
      

      標準

        apiVersion: rbac.authorization.k8s.io/v1
        kind: RoleBinding
        metadata:
          name: gmp-system:collector:kueue-secret-reader
          namespace: kueue-system
        roleRef:
          name: kueue-secret-reader
          kind: Role
          apiGroup: rbac.authorization.k8s.io
        subjects:
        -   name: collector
          namespace: gmp-system
          kind: ServiceAccount
      
    3. 設定 PodMonitoring 資源:

      下列資源會設定 Kueue 部署作業的監控功能,並指定透過 HTTPS 在 /metrics 路徑公開指標。在擷取指標時,系統會使用 kueue-metrics-reader-token 密鑰進行驗證。

      apiVersion: monitoring.googleapis.com/v1
      kind: PodMonitoring
      metadata:
      name: kueue
      namespace: kueue-system
      spec:
      selector:
       matchLabels:
         control-plane: controller-manager
      endpoints:
      -   port: https
       interval: 30s
       path: /metrics
       scheme: https
       tls:
         insecureSkipVerify: true
       authorization:
         type: Bearer
         credentials:
           secret:
             name: kueue-metrics-reader-token
             key: token
      

    查詢匯出的指標

    監控以 Kueue 為基礎的系統時,可使用的 PromQL 查詢範例

    您可以透過這些 PromQL 查詢監控重要的 Kueue 指標,例如工作輸送量、佇列的資源用量和工作負載等待時間,瞭解系統效能並找出潛在瓶頸。

    工作處理量

    這會計算每個 cluster_queue 在 5 分鐘內允許的工作負載每秒速率。這項指標有助於按佇列細分,找出瓶頸,加總後即可得出整體系統輸送量。

    查詢:

    sum(rate(kueue_admitted_workloads_total[5m])) by (cluster_queue)

    資源使用率

    前提是 metrics.enableClusterQueueResources 已啟用。系統會計算每個佇列的目前 CPU 使用量與名義 CPU 配額比率。值越接近 1,表示用量越高。您可以變更資源標籤,將此範例用於記憶體或其他資源。

    如要在叢集中安裝自訂設定的 Kueue 發布版本,請參閱 Kueue 說明文件

    查詢:

    sum(kueue_cluster_queue_resource_usage{resource="cpu"}) by (cluster_queue) / sum(kueue_cluster_queue_nominal_quota{resource="cpu"}) by (cluster_queue)

    佇列等待時間

    這項指標會顯示特定佇列中工作負載的第 90 百分位等待時間。您可以修改分位數值 (例如中位數為 0.5,第 99 個百分位數為 0.99),瞭解等待時間分布情形。

    查詢:

    histogram_quantile(0.9, kueue_admission_wait_time_seconds_bucket{cluster_queue="QUEUE_NAME"})

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

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

    產生要傳送至兩個 ClusterQueue 的工作,這些工作會休眠 10 秒,並以三個平行工作完成。然後在 60 秒後清除。

    apiVersion: batch/v1
    kind: Job
    metadata:
      namespace: team-a # Job under team-a namespace
      generateName: sample-job-team-a-
      labels:
        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:
          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"
          restartPolicy: Never

    job-team-a.yaml 會在 team-a 命名空間下建立 Job,並指向 LocalQueue lq-team-a 和 ClusterQueue cq-team-a

    同樣地,job-team-b.yaml 會在 team-b 命名空間下建立 Job,並指向 LocalQueue lq-team-b 和 ClusterQueue cq-team-b

    1. 啟動新的終端機,然後執行這個指令碼,每秒產生一個 Job:

      ./create_jobs.sh job-team-a.yaml 1
      
    2. 啟動另一個終端機,並為 team-b 命名空間建立 Job:

      ./create_jobs.sh job-team-b.yaml 1
      
    3. 觀察 Prometheus 中排入佇列的工作。或使用以下指令:

      watch -n 2 kubectl get clusterqueues -o wide
      

    畫面會顯示如下的輸出內容:

        NAME        COHORT      STRATEGY         PENDING WORKLOADS   ADMITTED WORKLOADS
        cq-team-a   all-teams   BestEffortFIFO   0                   5
        cq-team-b   all-teams   BestEffortFIFO   0                   4
    

    透過同類群組借用未使用的配額

    ClusterQueues 可能無法隨時達到最大容量。如果工作負載未平均分配到 ClusterQueues,配額用量就不會達到上限。如果 ClusterQueue 之間共用相同同類群組,ClusterQueue 可以從其他 ClusterQueue 借用配額,盡量提高配額使用率。

    1. 當 ClusterQueues cq-team-acq-team-b 都有排隊等候的作業時,請在對應的終端機上按下 CTRL+c,停止 team-b 命名空間的指令碼。

    2. 處理完命名空間 team-b 的所有待處理工作後,命名空間 team-a 的工作即可借用 cq-team-b 中的可用資源:

      kubectl describe clusterqueue cq-team-a
      

      由於 cq-team-acq-team-b 共用名為 all-teams 的相同同類群組,因此這些 ClusterQueue 可以共用未使用的資源。

        Flavors Usage:
          Name:  on-demand
          Resources:
            Borrowed:  5
            Name:      cpu
            Total:     15
            Borrowed:  5Gi
            Name:      memory
            Total:     15Gi
      
    3. 繼續執行 team-b 命名空間的指令碼。

      ./create_jobs.sh job-team-b.yaml 3
      

      觀察從 cq-team-a 借用的資源如何返回 0,而 cq-team-b 的資源則用於自身的工作負載:

      kubectl describe clusterqueue cq-team-a
      
        Flavors Usage:
          Name:  on-demand
          Resources:
            Borrowed:  0
            Name:      cpu
            Total:     9
            Borrowed:  0
            Name:      memory
            Total:     9Gi
      

    使用 Spot VM 增加配額

    如果需要暫時增加配額 (例如因應待處理工作負載的高需求),您可以將更多 ClusterQueue 新增至同類群組,藉此設定 Kueue 來滿足需求。如果 ClusterQueue 有未使用的資源,可以與屬於相同群組的其他 ClusterQueue 共用這些資源。

    在本教學課程的開頭,您使用 Spot VM 建立名為 spot 的節點集區,並建立名為 spot 的 ResourceFlavor,標籤設為 cloud.google.com/gke-provisioning: spot。建立 ClusterQueue,以使用這個節點集區和代表該節點集區的 ResourceFlavor:

    1. 建立名為 cq-spot 的新 ClusterQueue,並將同類群組設為 all-teams

      apiVersion: kueue.x-k8s.io/v1beta1
      kind: ClusterQueue
      metadata:
        name: spot-cq
      spec:
        cohort: all-teams # Same cohort as cq-team-a and cq-team-b
        resourceGroups:
        - coveredResources: ["cpu", "memory"]
          flavors:
          - name: spot
            resources:
            - name: "cpu"
              nominalQuota: 40
            - name: "memory"
              nominalQuota: 144Gi

      由於這個 ClusterQueue 與 cq-team-acq-team-b 共用相同群組,因此 ClusterQueue cq-team-acq-team-b 最多可借用 15 個 CPU 要求和 15 Gi 的記憶體。

      kubectl apply -f cq-spot.yaml
      
    2. 在 Prometheus 中,觀察 cq-team-acq-team-b 的已允許工作負載如何因 cq-spot 新增配額而大幅增加,因為 cq-spot 與兩者共用相同群組。或使用以下指令:

      watch -n 2 kubectl get clusterqueues -o wide
      
    3. 在 Prometheus 中,觀察叢集中的節點數量。或使用以下指令:

      watch -n 2 kubectl get nodes -o wide
      
    4. 針對 team-ateam-b 命名空間按下 CTRL+c,停止兩個指令碼。

    清除所用資源

    如要避免系統向您的 Google Cloud 帳戶收取本教學課程中所用資源的相關費用,請刪除含有該項資源的專案,或者保留專案但刪除個別資源。

    刪除專案

    1. In the Google Cloud console, go to the Manage resources page.

      Go to Manage resources

    2. In the project list, select the project that you want to delete, and then click Delete.
    3. In the dialog, type the project ID, and then click Shut down to delete the project.

    刪除個別資源

    1. 刪除 Kueue 配額系統:

      kubectl delete -n team-a localqueue lq-team-a
      kubectl delete -n team-b localqueue lq-team-b
      kubectl delete clusterqueue cq-team-a
      kubectl delete clusterqueue cq-team-b
      kubectl delete clusterqueue cq-spot
      kubectl delete resourceflavor default
      kubectl delete resourceflavor on-demand
      kubectl delete resourceflavor spot
      
    2. 刪除 Kueue 資訊清單:

      VERSION=VERSION
      kubectl delete -f \
        https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
      
    3. 刪除叢集:

      gcloud container clusters delete kueue-cohort --location=COMPUTE_REGION
      

    後續步驟