Implementa un sistema di accodamento dei job con condivisione della quota tra gli spazi dei nomi su GKE


Questo tutorial utilizza Kueue per mostrarti come implementare un sistema di coda dei job, configurare la condivisione delle risorse e delle quote dei carichi di lavoro tra diversi spazi dei nomi su Google Kubernetes Engine (GKE) e massimizzare l'utilizzo del cluster.

Sfondo

In qualità di tecnico dell'infrastruttura o amministratore del cluster, massimizzare l'utilizzo tra gli spazi dei nomi è molto importante. Un batch di job in uno spazio dei nomi potrebbe non utilizzare completamente la quota assegnata allo spazio dei nomi, mentre un altro spazio dei nomi potrebbe avere più job in attesa. Per utilizzare in modo efficiente le risorse del cluster tra i job in spazi dei nomi diversi e per aumentare la flessibilità della gestione delle quote, puoi configurare i coorti in Kueue. Una coorte è un gruppo di ClusterQueue che può prendere in prestito la quota inutilizzata l'uno dall'altro. Un ClusterQueue regola un pool di risorse come CPU, memoria e acceleratori hardware.

Puoi trovare una definizione più dettagliata di tutti questi concetti nella documentazione di Kueue.

Obiettivi

Questo tutorial è rivolto agli ingegneri dell'infrastruttura o agli amministratori di cluster che vogliono implementare un sistema di accodamento dei job su Kubernetes utilizzando Kueue con la condivisione della quota.

Questo tutorial imita due team in due spazi dei nomi diversi, dove ogni team dispone di risorse dedicate, ma possono prendere in prestito l'uno dall'altro. Un terzo insieme di risorse può essere utilizzato come riserva quando i job si accumulano.

Utilizza l'operatore Prometheus per monitorare i job e l'allocazione delle risorse in spazi dei nomi diversi.

Questo tutorial illustra i seguenti passaggi:

  1. Crea un cluster GKE
  2. Crea ResourceFlavors
  3. Per ogni team, crea un oggetto ClusterQueue e LocalQueue
  4. (Facoltativo) Esegui il deployment di kube-prometheus e monitora i carichi di lavoro utilizzando Prometheus
  5. Creare job e osservare i carichi di lavoro ammessi
  6. Prendere in prestito la quota non utilizzata con le coorti
  7. Aggiungi un ClusterQueue di spillover che regola le VM spot

Costi

Questo tutorial utilizza i seguenti componenti fatturabili di Google Cloud:

Utilizza il Calcolatore prezzi per generare una stima dei costi in base all'utilizzo previsto.

Al termine di questo tutorial, puoi evitare la fatturazione continua eliminando il le risorse che hai creato. Per maggiori informazioni, vedi Pulizia.

Prima di iniziare

Configura il progetto

  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. Make sure 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. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the GKE API.

    Enable the API

Impostare i valori predefiniti per Google Cloud CLI

  1. Nella console Google Cloud, avvia un'istanza di Cloud Shell:
    Apri Cloud Shell

  2. Scarica il codice sorgente di questa app di esempio:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. Imposta le variabili di ambiente predefinite:

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

    Sostituisci i seguenti valori:

Crea un cluster GKE

  1. Crea un cluster GKE denominato kueue-cohort:

    Creerai un cluster con 6 nodi (2 per zona) nel pool predefinito e senza scalabilità automatica. Queste saranno tutte le risorse disponibili per i team all'inizio, quindi dovranno competere per ottenerle.

    Vedrai più avanti come Kueue gestisce i carichi di lavoro che entrambi i team invieranno alle rispettive code.

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

    Una volta creato il cluster, il risultato è simile al seguente:

      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
    

    Dove STATUS è RUNNING per kueue-cluster.

  2. Crea un pool di nodi denominato spot.

    Questo pool di nodi utilizza una VM spot e ha la scalabilità automatica abilitata. Inizia con 0 nodi, ma in seguito le renderai disponibili ai team per utilizzarle come capacità di overspill.

    gcloud container node-pools create spot --cluster=kueue-cohort --region COMPUTE_REGION  \
    --spot --enable-autoscaling --max-nodes 20 --num-nodes 0 \
    --machine-type e2-standard-4
    
  3. Installa la versione di release di Kueue nel cluster:

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

    Sostituisci VERSION con la lettera v che segue l'ultima versione di Kueue, ad esempio v0.4.0. Per scoprire di più sulle versioni di Kueue, consulta Release di Kueuue.

    Attendi che il controller Kueue sia pronto:

    watch kubectl -n kueue-system get pods
    

    Per poter continuare, l'output dovrebbe essere simile al seguente:

    NAME                                        READY   STATUS    RESTARTS   AGE
    kueue-controller-manager-6cfcbb5dc5-rsf8k   2/2     Running   0          3m
    
  4. Crea due nuovi spazi dei nomi chiamati team-a e team-b:

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

    I job verranno generati per ogni spazio dei nomi.

Crea ResourceFlavors

Un ResourceFlavor rappresenta le variazioni delle risorse nei nodi del cluster, come diverse VM (ad esempio spot e on demand), architetture (ad es. CPU x86 o ARM), brand e modelli (ad esempio GPU Nvidia A100 rispetto a T4).

ResourceFlavors utilizza le etichette e gli elementi taint dei nodi per trovare una corrispondenza con un insieme di nodi nel 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

In questo manifest:

  • L'etichetta di ResourceFlavor on-demand è impostata su cloud.google.com/gke-provisioning: standard.
  • L'etichetta di ResourceFlavor spot è impostata su cloud.google.com/gke-provisioning: spot.

Quando a un carico di lavoro viene assegnato un ResourceFlavor, Kueue assegna i pod del carico di lavoro ai nodi che corrispondono alle etichette dei nodi definite per ResourceFlavor.

Esegui il deployment di ResourceFlavor:

kubectl apply -f flavors.yaml

Crea ClusterQueue e LocalQueue

Crea due ClusterQueues cq-team-a e cq-team-b e i rispettivi valori LocalQueues lq-team-a e lq-team-b con spazio dei nomi rispettivamente su team-a e team-b.

ClusterQueues è un oggetto con ambito cluster che regola un pool di risorse come CPU, memoria e acceleratori hardware. Gli amministratori batch possono limitare la visibilità di questi oggetti agli utenti batch.

Le LocalQueues sono oggetti con spazio dei nomi che gli utenti batch possono elencare. Indicano CluterQueues da cui vengono allocate le risorse per eseguire i carichi di lavoro 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 consente alle risorse di avere più versioni. In questo caso, ClusterQueues ha due versioni, on-demand e spot, che forniscono cpu risorse ciascuna. La quota dell'elemento ResourceFlavor spot è impostata su 0 e non verrà utilizzata per per ora.

Entrambe le ClusterQueue condividono la stessa coorte denominata all-teams, definita in .spec.cohort. Quando due o più ClusterQueue condividono la stessa coorte, possono prendere in prestito la quota inutilizzata da e l'altro.

Per saperne di più su come funzionano le coorti e sulla semantica del prestito, consulta la documentazione di Kuueue

Esegui il deployment di ClusterQueues e LocalQueues:

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

(Facoltativo) Esegui il deployment di kube-prometheus e monitora i carichi di lavoro utilizzando Prometheus

Puoi utilizzare Prometheus per monitorare i carichi di lavoro in attesa e attivi di Kueue. al fine di monitorare i carichi di lavoro attivati e osservare il carico su ClusterQueue, configura Prometheus nel cluster nel monitoraggio dello spazio dei nomi.

  1. Scarica il codice sorgente per l'operatore Prometheus per il monitoraggio:

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

    kubectl create -f kube-prometheus/manifests/setup
    
  3. Crea i componenti di monitoraggio:

    kubectl create -f kube-prometheus/manifests
    
  4. Consenti a prometheus-operator di eseguire lo scraping delle metriche dai componenti Kueue:

    kubectl apply -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/prometheus.yaml
    
  5. Passa alla directory di lavoro:

    cd kubernetes-engine-samples/batch/kueue-cohort
    
  6. Avvia un nuovo terminale per accedere a Prometheus eseguendo il port forwarding del servizio:

    kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090
    
  7. Apri Prometheus su localhost:9090 nel browser

  8. Se utilizzi Cloud Shell, fai clic su Anteprima web, seleziona Cambia porta, imposta il numero di porta su 9090 e seleziona Change and Preview.

  9. Inserisci la query per il primo riquadro che monitora il ClusterQueue cq-team-a attivo:

    kueue_pending_workloads{cluster_queue="cq-team-a", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-a"}
    
  10. Aggiungi un altro riquadro e inserisci la query che monitora l'oggetto ClusterQueue cq-team-b attivo:

    kueue_pending_workloads{cluster_queue="cq-team-b", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-b"}
    
  11. Aggiungi un altro riquadro e inserisci la query che monitora il numero di nodi nel cluster:

    count(kube_node_info)
    

Creare job e osservare i carichi di lavoro ammessi

Genera job in entrambi i ClusterQueues che risulteranno in sospensione per 10 secondi, con tre di job in parallelo e sarà completato con tre completamenti. Sarà quindi dopo 60 secondi.

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 crea job nello spazio dei nomi team-a e punta alla coda locale lq-team-a e alla coda del cluster cq-team-a.

Analogamente, job-team-b.yaml crea job nello spazio dei nomi team-b e punta alla coda locale lq-team-b e alla coda del cluster cq-team-b.

  1. Avvia un nuovo terminale ed esegui questo script per generare un job ogni secondo:

    ./create_jobs.sh job-team-a.yaml 1
    
  2. Avvia un altro terminale e crea job per lo spazio dei nomi team-b:

    ./create_jobs.sh job-team-b.yaml 1
    
  3. Osserva i job in coda in Prometheus. Oppure con questo comando:

    watch -n 2 kubectl get clusterqueues -o wide
    

L'output dovrebbe essere simile al seguente:

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

Prendi in prestito la quota non utilizzata con le coorti

ClusterQueues potrebbe non raggiungere sempre la capacità massima. L'utilizzo delle quote non è massimizzata quando i carichi di lavoro non sono distribuiti uniformemente tra ClusterQueues. Se ClusterQueues condivide la stessa coorte tra loro, ClusterQueues prendere in prestito le quote da altri ClusterQueues per massimizzare l'utilizzo della quota.

  1. Quando i job sono in coda sia per ClusterQueues cq-team-a che cq-team-b, interrompi lo script per lo spazio dei nomi team-b premendo CTRL+c sul terminale corrispondente.

  2. Una volta elaborati tutti i job in attesa dallo spazio dei nomi team-b, i job dallo spazio dei nomi team-a può prendere in prestito risorse in cq-team-b:

    kubectl describe clusterqueue cq-team-a
    

    Poiché cq-team-a e cq-team-b condividono la stessa coorte denominata all-teams, possono condividere le risorse non utilizzate.

      Flavors Usage:
        Name:  on-demand
        Resources:
          Borrowed:  5
          Name:      cpu
          Total:     15
          Borrowed:  5Gi
          Name:      memory
          Total:     15Gi
    
  3. Riprendi lo script per lo spazio dei nomi team-b.

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

    Osserva come le risorse prese in prestito da cq-team-a tornano a 0, mentre Le risorse di cq-team-b vengono utilizzate per i propri carichi di lavoro:

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

Aumenta la quota con le VM spot

Quando la quota deve essere temporaneamente aumentata, ad esempio per soddisfare una domanda elevata nei carichi di lavoro in attesa, puoi configurare Kueue per soddisfare la domanda aggiungendo più ClusterQueues alla coorte. I ClusterQueues con risorse inutilizzate possono condividere queste risorse con altre ClusterQueues che appartengono alla stessa coorte.

All'inizio del tutorial hai creato un pool di nodi denominato spot utilizzando VM spot e un oggetto ResourceFlavor denominato spot con l'etichetta impostata su cloud.google.com/gke-provisioning: spot. Crea un ClusterQueue per utilizzare questo pool di nodi e il valore ResourceFlavor che lo rappresenta:

  1. Crea un nuovo ClusterQueue denominato cq-spot con la coorte impostata su 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

    Poiché questo ClusterQueue condivide la stessa coorte con cq-team-a e cq-team-b, sia ClusterQueue cq-team-a che cq-team-b possono prendere in prestito risorse fino a 15 richieste di CPU e 15 Gi di memoria.

    kubectl apply -f cq-spot.yaml
    
  2. In Prometheus, osserva il picco dei carichi di lavoro ammessi per cq-team-a e cq-team-b grazie alla quota aggiunta di cq-spot che condivide la stessa della coorte. Oppure con questo comando:

    watch -n 2 kubectl get clusterqueues -o wide
    
  3. In Prometheus, osserva il numero di nodi nel cluster. Oppure con questo comando:

    watch -n 2 kubectl get nodes -o wide
    
  4. Interrompi entrambi gli script premendo CTRL+c per team-a e lo spazio dei nomi team-b.

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina il progetto

  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.

Elimina la singola risorsa

  1. Elimina il sistema di quote 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. Elimina il file manifest di Kueue:

    VERSION=VERSION
    kubectl delete -f \
      https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    
  3. Elimina il cluster:

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

Passaggi successivi