Mengimplementasikan sistem antrean Tugas dengan berbagi kuota antar-namespace di GKE

Tutorial ini menggunakan Kueue untuk menunjukkan cara menerapkan sistem antrean Tugas, mengonfigurasi resource workload, dan pembagian kuota antar-namespace yang berbeda di Google Kubernetes Engine (GKE), serta untuk memaksimalkan pemanfaatan cluster Anda.

Latar belakang

Sebagai engineer infrastruktur atau administrator cluster, memaksimalkan pemanfaatan antar-namespace sangatlah penting. Batch Tugas dalam satu namespace mungkin tidak sepenuhnya menggunakan seluruh kuota yang ditetapkan ke namespace, sementara namespace lain mungkin memiliki beberapa Tugas yang tertunda. Untuk menggunakan resource cluster secara efisien di antara Tugas dalam berbagai namespace dan untuk meningkatkan fleksibilitas pengelolaan kuota, Anda dapat mengonfigurasi kelompok di Kueue. Kelompok adalah grup ClusterQueue yang dapat meminjam kuota yang tidak digunakan dari satu sama lain. ClusterQueue mengatur kumpulan resource seperti CPU, memori, dan akselerator hardware.

Anda dapat menemukan definisi yang lebih mendetail dari semua konsep ini di dokumentasi Kueue

Membuat ResourceFlavors

ResourceFlavor mewakili variasi resource di node cluster Anda, seperti VM yang berbeda (misalnya spot versus on-demand), arsitektur (misalnya, CPU x86 vs ARM), merek, dan model (misalnya, GPU Nvidia A100 versus T4).

ResourceFlavors menggunakan label dan taint node agar cocok dengan set node dalam cluster.

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

Dalam manifes ini:

  • on-demand ResourceFlavor memiliki label yang ditetapkan ke cloud.google.com/gke-provisioning: standard.
  • spot ResourceFlavor memiliki label yang ditetapkan ke cloud.google.com/gke-provisioning: spot.

Saat workload diberi ResourceFlavor, Kueue menetapkan Pod workload ke node yang cocok dengan label node yang ditentukan untuk ResourceFlavor.

Men-deploy ResourceFlavor:

kubectl apply -f flavors.yaml

Membuat ClusterQueue dan LocalQueue

Membuat dua ClusterQueue cq-team-a dan cq-team-b, serta LocalQueues lq-team-a dan lq-team-b yang sesuai, dengan namespace masing-masing team-a dan team-b.

ClusterQueues adalah objek cakupan cluster yang mengatur kumpulan resource seperti CPU, memori, dan akselerator hardware. Administrator batch dapat membatasi visibilitas objek ini untuk pengguna batch.

LocalQueue adalah objek dengan namespace yang dapat dicantumkan oleh pengguna batch. Antrean mengarah ke CluterQueues, tempat resource dialokasikan untuk menjalankan workload 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 memungkinkan resource memiliki beberapa ragam. Dalam hal ini, kedua ClusterQueues memiliki dua ragam, on-demand dan spot, masing-masing menyediakan resource cpu. Kuota spot ResourceFlavor ditetapkan ke 0, dan tidak akan digunakan untuk saat ini.

Kedua ClusterQueues memiliki kelompok yang sama bernama all-teams, yang ditentukan di .spec.cohort. Jika dua atau beberapa ClusterQueue berbagi kelompok yang sama, cluster tersebut dapat meminjam kuota yang tidak digunakan dari satu sama lain.

Anda dapat mempelajari lebih lanjut cara kerja kelompok dan semantik pinjaman di dokumentasi Kueue

Men-deploy ClusterQueues dan LocalQueues:

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

(Opsional) Memantau Workload menggunakan kube-prometheus

Anda dapat menggunakan Prometheus untuk memantau workload Kueue aktif dan tertunda. Untuk memantau workload yang ditampilkan dan mengamati muatan di setiap ClusterQueue, deploy kube-prometheus ke cluster di bagian namespace monitoring:

  1. Download kode sumber untuk operator Prometheus:

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

    kubectl create -f kube-prometheus/manifests/setup
    
  3. Buat komponen pemantauan:

    kubectl create -f kube-prometheus/manifests
    
  4. Izinkan prometheus-operator untuk melakukan scraping metrik dari komponen Kueue:

    kubectl apply -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/prometheus.yaml
    
  5. Ubah ke direktori kerja:

    cd kubernetes-engine-samples/batch/kueue-cohort
    
  6. Siapkan penerusan port ke layanan Prometheus yang berjalan di cluster GKE Anda:

    kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090
    
  7. Buka UI web Prometheus di localhost:9090 pada browser.

    Di Cloud Shell:

    1. Klik Pratinjau Web.

    2. Klik Ubah port dan tetapkan nomor port ke 9090.

    3. Klik Ubah dan Pratinjau.

    UI web Prometheus berikut akan muncul.

    Screenshot UI web Prometheus

  8. Di kotak kueri Expression, masukkan kueri berikut untuk membuat panel pertama yang memantau beban kerja aktif untuk 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. Klik Tambahkan panel.

  10. Di kotak kueri Expression, masukkan kueri berikut untuk membuat panel lain yang memantau beban kerja aktif untuk ClusterQueue cq-team-b:

    kueue_pending_workloads{cluster_queue="cq-team-b", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-b"}
    
  11. Klik Tambahkan panel.

  12. Di kotak kueri Expression, masukkan kueri berikut untuk membuat panel yang memantau jumlah node di cluster:

    count(kube_node_info)
    

(Opsional) Memantau Workload menggunakan Google Cloud Managed Service for Prometheus

Anda dapat menggunakan Google Cloud Managed Service for Prometheus untuk memantau workload Kueue yang aktif dan tertunda. Daftar lengkap metrik dapat ditemukan di dokumentasi Kueue.

  1. Siapkan Identity dan RBAC untuk akses metrik:

    Konfigurasi berikut membuat 4 resource Kubernetes, yang menyediakan akses metrik untuk pengumpul Google Cloud Managed Service for Prometheus.

    • ServiceAccount bernama kueue-metrics-reader dalam namespace kueue-system akan digunakan untuk melakukan autentikasi saat mengakses metrik Kueue.

    • Secret yang terkait dengan akun layanan kueue-metrics-reader, menyimpan token autentikasi, yang digunakan oleh pengumpul, untuk melakukan autentikasi dengan endpoint metrik yang diekspos oleh deployment Kueue.

    • Peran bernama kueue-secret-reader di namespace kueue-system, yang memungkinkan pembacaan secret yang berisi token akun layanan.

    • ClusterRoleBinding yang memberikan peran ClusterRole kueue-metrics-reader ke akun layanan kueue-metrics-reader.

    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. Konfigurasi RoleBinding untuk Google Cloud Managed Service for Prometheus:

    Bergantung pada apakah Anda menggunakan cluster Autopilot atau Standard, Anda harus membuat RoleBinding di namespace gke-gmp-system atau gmp-system. Resource ini memungkinkan akun layanan pengumpul mengakses rahasia kueue-metrics-reader-token untuk melakukan autentikasi dan meng-scrape metrik 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
    

    Standar

      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. Konfigurasi resource Pod Monitoring:

    Resource berikut mengonfigurasi pemantauan untuk deployment Kueue, dan menentukan bahwa metrik diekspos di jalur /metrics melalui HTTPS. Agen ini menggunakan secret kueue-metrics-reader-token untuk autentikasi saat meng-scraping metrik.

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

Kueri metrik yang diekspor

Contoh kueri PromQL untuk memantau sistem berbasis Kueue

Kueri PromQL ini memungkinkan Anda memantau metrik utama Kueue seperti throughput tugas, penggunaan resource menurut antrean, dan waktu tunggu beban kerja untuk memahami performa sistem dan mengidentifikasi potensi hambatan.

Throughput Tugas

Formula ini menghitung kecepatan per detik workload yang diterima selama 5 menit untuk setiap cluster_queue. Metrik ini dapat membantu memecahnya berdasarkan antrean untuk membantu menunjukkan hambatan dan menjumlahkannya akan memberikan throughput sistem secara keseluruhan.

Kueri:

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

Pemanfaatan Sumber Daya

Hal ini mengasumsikan bahwa metrics.enableClusterQueueResources diaktifkan. Metrik ini menghitung rasio penggunaan CPU saat ini terhadap kuota CPU nominal untuk setiap antrean. Nilai yang mendekati 1 menunjukkan pemakaian yang tinggi. Anda dapat menyesuaikannya untuk memori atau resource lainnya dengan mengubah label resource.

Untuk menginstal versi rilis Kueue yang dikonfigurasi secara kustom di cluster Anda, ikuti dokumentasi Kueue.

Kueri:

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

Waktu Tunggu Antrean

Hal ini memberikan waktu tunggu persentil ke-90 untuk beban kerja dalam antrean tertentu. Anda dapat mengubah nilai kuantil (misalnya, 0,5 untuk median, 0,99 untuk persentil ke-99) untuk memahami distribusi waktu tunggu.

Kueri:

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

Membuat Job dan mengamati workload yang diterima

Di bagian ini, Anda akan membuat Tugas Kubernetes di namespace team-a dan team-b. Pengontrol Tugas di Kubernetes membuat satu atau beberapa Pod dan memastikan bahwa Pod tersebut berhasil menjalankan tugas tertentu.

Hasilkan Tugas ke kedua ClusterQueue yang akan tidur selama 10 detik, dengan tiga Tugas yang setara dan akan diselesaikan dengan tiga penyelesaian. Kemudian, data akan dibersihkan setelah 60 detik.

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 membuat Tugas pada namespace team-a dan mengarah ke LocalQueue lq-team-a dan ClusterQueue cq-team-a.

Demikian pula, job-team-b.yaml membuat Tugas pada namespace team-b, dan menunjuk ke LocalQueue lq-team-b dan ClusterQueue cq-team-b.

  1. Mulai terminal baru dan jalankan skrip ini untuk menghasilkan Tugas setiap detik:

    ./create_jobs.sh job-team-a.yaml 1
    
  2. Mulai terminal lain dan buat Tugas untuk namespace team-b:

    ./create_jobs.sh job-team-b.yaml 1
    
  3. Amati Tugas yang mengantre di Prometheus. Atau dengan perintah ini:

    watch -n 2 kubectl get clusterqueues -o wide
    

Outputnya akan mirip dengan berikut ini:

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

Meminjam kuota yang tidak digunakan dengan kelompok

ClusterQueues mungkin tidak selalu memiliki kapasitas penuh. Penggunaan kuota tidak akan dimaksimalkan saat workload tidak tersebar secara merata di antara ClusterQueue. Jika ClusterQueues berbagi kelompok yang sama satu sama lain, ClusterQueues dapat meminjam kuota dari ClusterQueues lain untuk memaksimalkan penggunaan kuota.

  1. Setelah ada Tugas yang diantrekan untuk ClusterQueues cq-team-a dan cq-team-b, hentikan skrip untuk namespace team-b dengan menekan CTRL+c di terminal yang sesuai.

  2. Setelah semua Tugas yang tertunda dari namespace team-b diproses, tugas dari namespace team-a dapat meminjam resource yang tersedia di cq-team-b:

    kubectl describe clusterqueue cq-team-a
    

    Karena cq-team-a dan cq-team-b memiliki kelompok yang sama yang disebut all-teams, ClusterQueues ini dapat berbagi resource yang tidak digunakan.

      Flavors Usage:
        Name:  on-demand
        Resources:
          Borrowed:  5
          Name:      cpu
          Total:     15
          Borrowed:  5Gi
          Name:      memory
          Total:     15Gi
    
  3. Melanjutkan skrip untuk namespace team-b.

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

    Amati bagaimana resource yang dipinjam dari cq-team-a kembali ke 0, sedangkan resource dari cq-team-b digunakan untuk workloadnya sendiri:

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

Meningkatkan kuota dengan Spot VM

Ketika kuota perlu ditingkatkan sementara, misalnya untuk memenuhi permintaan tinggi dalam workload yang tertunda, Anda dapat mengonfigurasi Kueue untuk mengakomodasi permintaan dengan menambahkan lebih banyak ClusterQueues ke kelompok. ClusterQueue dengan resource yang tidak digunakan dapat berbagi resource tersebut dengan ClusterQueues lain yang termasuk dalam kelompok yang sama.

Di awal tutorial, Anda telah membuat node pool bernama spot menggunakan Spot VM dan ResourceFlavor bernama spot dengan label yang ditetapkan ke cloud.google.com/gke-provisioning: spot. Buat ClusterQueue untuk menggunakan node pool ini dan ResourceFlavor yang mewakilinya:

  1. Buat ClusterQueue baru bernama cq-spot dengan kelompok yang ditetapkan ke 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

    Karena ClusterQueue ini berbagi kelompok yang sama dengan cq-team-a dan cq-team-b, ClusterQueue cq-team-a dan cq-team-b dapat meminjam resource hingga 15 permintaan CPU, dan 15 Gi memori.

    kubectl apply -f cq-spot.yaml
    
  2. Di Prometheus, amati lonjakan workload yang diterima untuk cq-team-a dan cq-team-b berkat kuota tambahan oleh cq-spot yang berbagi kelompok yang sama. Atau dengan perintah ini:

    watch -n 2 kubectl get clusterqueues -o wide
    
  3. Di Prometheus, amati jumlah node dalam cluster. Atau dengan perintah ini:

    watch -n 2 kubectl get nodes -o wide
    
  4. Hentikan kedua skrip dengan menekan CTRL+c untuk namespace team-a dan team-b.