iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
Kubernetes

異世界生存戰記:30天煉成GKE大師系列 第 21

Day21 資源不夠使用了,調用一些魔力給它吧! Kueue(一)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241005/20169017i1yp4j4gCi.png

前言

Kueue 是一個基於配額共享的作業排隊系統,用於管理配額以及作業如何使用配額,尤其適用於像機器學習模型訓練這類資源密集型的工作負載。Kueue 決定何時應該等待 Job,何時應該允許 Job 啟動 ( 可以創建 Pod ) 以及何時應該搶佔 Job ( 刪除活動 Pod )。

接下來,我們將分析 Kueue 的核心概念以及基本配置。

Kueue 介紹

Google Kubernetes Engine (GKE) Kueue 是一個用於原生地協調工作負載在 GKE 叢集中的作業批次工作排程器。它通過一個分層的公平共享佇列系統來管理工作負載,確保各種工作負載類別獲得其資源份額,並根據可用資源,以先進先出的方式處理工作負載。

Kueue 將 K8s 中的工作負載資源請求轉換為抽象資源配額,允許叢集管理員在命名空間或其他任意邊界之間定義和實施容量分配。當工作負載提交到 Kueue 佇列時,它會等待直到與其資源請求匹配的配額可用。這可以防止某些工作負載獨占叢集資源,並確保公平訪問。

Kueue 還支援從核心工作排程器中斷 Pod,允許使用盡可能多的可用資源,並在更高優先級的工作負載到達時搶佔低優先級的工作負載。這對於需要快速啟動或必須滿足特定 SLA 的工作負載而言尤其有用。

通過其簡單的 API 和與現有 Kubernetes 工具的集成,Kueue 簡化了批次工作負載管理,同時在共享叢集中確保公平性和效率。

Kueue 相關組件介紹

ResourceFlavor

Kueue 將 ResourceFlavor 作為異構環境的一項功能。 如下圖所示,可以在 ClusterQueue 中為每種計算資源類型指定 ResourceFlavor,並將 ResourceFlavor 中指定的標籤用作 Workload 的 nodeSelector。

Cluster Queue

ClusterQueue 是一個集群範圍的物件,用於管理資源池,例如 Pod、CPU、記憶體和硬體加速器。ClusterQueue 定義:

  • ClusterQueue 管理 Resource Flavor 的配額,包括使用限制和使用順序。
  • 集群中多個 ClusterQueues 之間的公平共享規則。
  • spec.resourceGroups.coveredResources可管理四種資源:cpumemorynvidia.com/gpu 和 ephemeral-storage

Local Queue

LocalQueue 是一個命名空間物件,接受來自命名空間中使用者的工作負載。 不同命名空間的LocalQueue可以指向同一個ClusterQueue,它們可以在其中共用資源的配額。 LocalQueue 指向一個 ClusterQueue,從中分配資源以運行其 workload(工作負載)。

https://ithelp.ithome.com.tw/upload/images/20241005/20169017aHzAUx3E5a.png

Kueue 使用情境

有兩個使用排隊平台的團隊,teamA 和 teamB,每個團隊有兩個成員,memberA1、memberA2、memberB1 和 memberB2。 此時,如無所示,管理員預定義了 teamA 和 teamB 可以被 ClusterQueue 使用的計算資源量。

使用者(如 memberA1)創建一個 LocalQueue,用於指定他們想要在各自的命名空間中使用的 ClusterQueue。 之後,用戶可以創建一個 Workload,這是一組需要同時執行的 Job,並將 Workload 放在一個 Queue 中並按順序執行它們。

https://ithelp.ithome.com.tw/upload/images/20241005/20169017IvjYpmEexS.png

Kueue 機器配置

為了部署實驗環境所需的機器,可以參考 Day3 的 Terraform 範例,使用 Day3 範例創建的 Cluster。

node-pool-variables.tf

module "gke" {
  node_pools = [
    var.node_pool_cpu.config,
    var.node_pool_cpu-spot.config,
  ]
  node_pools_labels = {
    "${var.node_pool_cpu.config.name}"      = var.node_pool_cpu.kubernetes_label
    "${var.node_pool_cpu-spot.config.name}" = var.node_pool_cpu-spot.kubernetes_label
  }
  node_pools_taints = {
    "${var.node_pool_cpu.config.name}"      = var.node_pool_cpu.taints
    "${var.node_pool_cpu-spot.config.name}" = var.node_pool_cpu-spot.taints
  }
  node_pools_resource_labels = {
    "${var.node_pool_cpu.config.name}"      = var.node_pool_cpu.node_pools_resource_labels
    "${var.node_pool_cpu-spot.config.name}" = var.node_pool_cpu-spot.node_pools_resource_labels
  }
}

### Node pool
variable "node_pool_cpu" {
  default = {
      config = {
        name              = "cpu"
        machine_type      = "n2-standard-8"
        max_pods_per_node = 64
        node_locations    = "us-central1-a"
        autoscaling       = true
        min_count         = 1
        max_count         = 2
        local_ssd_count   = 0
        spot              = false
        disk_size_gb      = 50
        disk_type         = "pd-standard"
        image_type        = "COS_CONTAINERD"
        enable_gcfs       = true
        enable_gvnic      = false
        logging_variant   = "DEFAULT"
        auto_repair       = true
        auto_upgrade      = true
        preemptible       = false
      }
      node_pools_resource_labels = {
        team = "cpu"
      }
      kubernetes_label = {
        role = "cpu"
      }
      taints = []
    } 
}

設定 GKE 集群,在 GKE 上安裝 v0.8.0 版本的 Kueue

$ VERSION=v0.8.0 # 安裝 v0.8.0 版本的 Kueue
$ kubectl apply --server-side -f \
  https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml

創建兩個名為 team-a 和 team-b 的 Namespace,待會要為此 Namespace 創建實驗用的 Job

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

創建 ResourceFlavor

在本教程中,為 CPU、記憶體創建一個 ResourceFlavor,而不使用標籤或污點。

kubectl apply -f ResourceFlavor.yaml

# ResourceFlavor.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: default-flavor # 此 ResourceFlavor 將用於所有資源

創建使用以上 ResourceFlavor 的 ClusterQueue

kubectl apply -f cluster-queue.yaml

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

使用順序由 .spec.queueingStrategy 確定,其中有兩種配置:

  • BestEffortFIFO
    • 默認排隊策略配置。
    • 工作負載准入遵循先進先出 (FIFO) 規則,但如果配額不足以允許佇列頭部的工作負載,則將嘗試佇列中的下一項工作負載。
  • StrictFIFO
    • 保證 FIFO 語義。
    • 佇列頭部的工作負載可以阻止將更多工作負載加入佇列,直到該工作負載獲得准入許可。

以上 cluster-queue.yaml 我們只有設定cpumemory規範請求,請求如下:

  • CPU 請求的總和小於或等於 6
  • 記憶體請求的總和小於或等於 6Gi

創建使用以上 ClusterQueue 的 LocalQueue

命名空間 team-a 和 team-b 中的 LocalQueue 指向 .spec.clusterQueue 下的同一 ClusterQueue cluster-queue

# local-queue.yaml
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-a # LocalQueue 在 team-a namespace 底下
  name: localqueue-team-a
spec:
  clusterQueue: default-cluster-queue # 指向 default-cluster-queue
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-b # LocalQueue 在 team-b namespace 底下
  name: localqueue-team-b
spec:
  clusterQueue: default-cluster-queue # 指向 default-cluster-queue

創建 Job 並觀察允許的工作負載

以下 team-a Job 的 resources.requests.cpu 為 1 Core,resources.requests.memory 為 1 Gi

# sample-job-team-a.yaml
apiVersion: batch/v1
kind: Job
metadata:
  namespace: team-a
  generateName: sample-job-team-a-
  annotations:
    kueue.x-k8s.io/queue-name: localqueue-team-a # 指向 localqueue-team-a LocalQueue
spec:
  ttlSecondsAfterFinished: 60 # Job 將在 60 秒後刪除
  parallelism: 3 # 同時執行 3 個副本
  completions: 3 # 需要完成 3 次
  suspend: true # 設定為 true 以允許 Kueue 控制 Job
  template:
    spec:
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:latest
        args: ["10s"] # Sleep for 10 seconds
        resources:
          requests:
            cpu: "1"
            memory: "1Gi"
          limits:
            cpu: "1"
            memory: "1Gi"
      restartPolicy: Never

以下 team-b Job 的 resources.requests.cpu 為 2 Core,resources.requests.memory 為 2 Gi

# sample-job-team-b.yaml
apiVersion: batch/v1
kind: Job
metadata:
  namespace: team-b # Job 在 team-b namespace 底下
  generateName: sample-job-team-b-
  annotations:
    kueue.x-k8s.io/queue-name: localqueue-team-b # 指向 localqueue-team-b LocalQueue
spec:
  ttlSecondsAfterFinished: 60 # Job 將在 60 秒後刪除
  parallelism: 3 # 同時執行 3 個副本
  completions: 3 # 需要完成 3 次
  suspend: true # 設定為 true 以允許 Kueue 控制 Job
  template:
    spec:
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:latest
        args: ["10s"] # Sleep for 10 seconds
        resources:
          requests:
            cpu: "2"
            memory: "2Gi"
          limits:
            cpu: "2"
            memory: "2Gi"
      restartPolicy: Never

實驗開始

使用以下指令,每秒連續創建 sample-job-team-a 及 sample-job-team-b

$ while :; do kubectl create -f sample-job-team-a.yaml; sleep 1; done
$ while :; do kubectl create -f sample-job-team-b.yaml; sleep 1; done

使用以下指令觀察正在排隊的 Job、在 ClusterQueue 中允許的 Job。

$ watch -n 1 kubectl -n team-a get jobs
$ watch -n 1 kubectl -n team-a get pods
$ watch -n 1 kubectl -n team-b get jobs
$ watch -n 1 kubectl -n team-b get pods
$ watch -n 2 kubectl get clusterqueues -o wide

總結

Kueue 有效地管理了 teamA 和 teamB 的資源共享。通過 clusterQueue: default-cluster-queue,Kueue 限制了總資源使用量在 6 CPU 核心和 6Gi 記憶體內。

由於兩個團隊的 Job 都需要 3 個副本並行運行,Kueue 的 BestEffortFIFO 策略動態地調度資源,使得同一時間可以運行 6 個 teamA 的 Job (每個請求 1 CPU 和 1Gi 內存) 或 3 個 teamB 的 Job (每個請求 2 CPU 和 2Gi 內存)。

實驗結果清晰地展示了 Kueue 如何在資源限制下,公平地在不同團隊間分配資源,並確保 Job 按順序執行,防止資源競爭和饑餓。 這驗證了 Kueue 作為 Kubernetes 批處理工作負載調度器的有效性。

參考文件


上一篇
Day20 Grafana + GCP OAuth API 一鍵登入數據聖殿
下一篇
Day22 資源不夠使用了,調用一些魔力給它吧! Kueue(二)
系列文
異世界生存戰記:30天煉成GKE大師30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言