Implementa un sistema de colas de trabajos con uso compartido de cuotas entre espacios de nombres en GKE


En este instructivo, se usa Kueue para mostrarte cómo implementar un sistema de colas de trabajos, configurar el recurso de carga de trabajo y el uso compartido de cuotas entre diferentes espacios de nombres en Google Kubernetes Engine (GKE), y maximizar el uso de tu clúster.

Segundo plano

Como ingeniero de infraestructura o administrador de clústeres, es muy importante maximizar el uso entre espacios de nombres. Es posible que un lote de trabajos en un espacio de nombres no use por completo la cuota completa asignada al espacio de nombres, mientras que otro espacio de nombres puede tener varios trabajos pendientes. Para utilizar de manera eficiente los recursos del clúster entre trabajos en diferentes espacios de nombres y aumentar la flexibilidad de la administración de cuotas, puedes configurar cohortes en Kueue. Una cohorte es un grupo de ClusterQueues que pueden tomar prestadas la cuota sin usar entre sí. Una ClusterQueue rige un grupo de recursos como CPU, memoria y aceleradores de hardware.

Puedes encontrar una definición más detallada de todos estos conceptos en la documentación de Kueue.

Objetivos

Este instructivo es para ingenieros de infraestructura o administradores de clústeres que deseen implementar un sistema de cola de trabajos en Kubernetes mediante Kueue con uso compartido de cuotas.

En este instructivo, se imitan dos equipos en dos espacios de nombres diferentes, en los que cada equipo tiene sus recursos dedicados, pero pueden tomar recursos prestados entre sí. Un tercer conjunto de recursos se puede usar como desbordamiento cuando se acumulan los trabajos.

Usa el operador de Prometheus para supervisar los trabajos y la asignación de recursos en diferentes espacios de nombres.

En este instructivo, se abarcan los siguientes pasos:

  1. Crear un clúster de GKE
  2. Crea los ResourceFlavors.
  3. Para cada equipo, crea una ClusterQueue y una LocalQueue
  4. Implementa kube-prometheus y supervisa cargas de trabajo con Prometheus (opcional)
  5. Crea Jobs y observa las cargas de trabajo admitidas
  6. Toma prestada la cuota sin usar con cohortes
  7. Agrega una ClusterQueue de desbordamiento que rija las VM Spot.

Costos

En este instructivo, se usan los siguientes componentes facturables de Google Cloud:

Usa la calculadora de precios para generar una estimación de los costos según el uso previsto.

Cuando finalices este instructivo, puedes borrar los recursos creados para evitar que se te siga facturando. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

Configura tu proyecto

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. En la consola de Google Cloud, en la página del selector de proyectos, haz clic en Crear proyecto para comenzar a crear un proyecto de Google Cloud nuevo.

    Ir al selector de proyectos

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  4. Habilita la API de GKE.

    Habilita la API

  5. En la consola de Google Cloud, en la página del selector de proyectos, haz clic en Crear proyecto para comenzar a crear un proyecto de Google Cloud nuevo.

    Ir al selector de proyectos

  6. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  7. Habilita la API de GKE.

    Habilita la API

Establece valores predeterminados para Google Cloud CLI

  1. En la consola de Google Cloud, inicia una instancia de Cloud Shell:
    Abrir Cloud Shell

  2. Descarga el código fuente para esta app de ejemplo:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. Configura las variables de entorno predeterminadas:

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

    Reemplaza los siguientes valores:

Crear un clúster de GKE

  1. Crea un clúster de GKE llamado kueue-cohort:

    Crearás un clúster con 6 nodos (2 por zona) en el grupo predeterminado y sin ajuste de escala automático. Esos serán todos los recursos disponibles para los equipos al principio, por lo que tendrán que competir por ellos.

    Más adelante, verás cómo Kueue administra las cargas de trabajo que ambos equipos enviarán a las colas correspondientes.

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

    El resultado es similar al siguiente una vez que se crea el clúster:

      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
    

    En el ejemplo anterior, STATUS es RUNNING para kueue-cluster.

  2. Crea un grupo de nodos llamado spot.

    Este grupo de nodos usa VM Spot y tiene habilitado el ajuste de escala automático. Comienza con 0 nodos, pero, luego, lo pondrás a disposición de los equipos para que los usen como capacidad de desbordamiento.

    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. Instala la versión de actualización de Kueue en el clúster:

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

    Reemplaza VERSION por la letra v seguida de la versión más reciente de Kueue, por ejemplo, v0.4.0. Para obtener más información sobre las versiones de Kueue, consulta Versiones de Kueue.

    Espera hasta que el controlador de Kueue esté listo:

    watch kubectl -n kueue-system get pods
    

    El resultado debería ser similar al siguiente antes de que puedas continuar:

    NAME                                        READY   STATUS    RESTARTS   AGE
    kueue-controller-manager-6cfcbb5dc5-rsf8k   2/2     Running   0          3m
    
  4. Crear dos espacios de nombres nuevos llamados team-a y team-b:

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

    Los trabajos se generarán en cada espacio de nombres.

Crea los ResourceFlavors

Un ResourceFlavor representa las variaciones de recursos en los nodos de tu clúster, como diferentes VMs (por ejemplo, Spot frente a bajo demanda), arquitecturas (por ejemplo, CPU x86 frente a CPU de ARM), marcas y modelos (por ejemplo, Nvidia A100) en comparación con las GPU T4).

ResourceFlavors usa etiquetas de nodo y taints para hacer coincidir un conjunto de nodos en el clúster.

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

En el manifiesto se muestra lo siguiente:

  • El ResourceFlavor on-demand tiene la etiqueta configurada en cloud.google.com/gke-provisioning: standard.
  • El ResourceFlavor spot tiene la etiqueta configurada en cloud.google.com/gke-provisioning: spot.

Cuando se asigna un ResourceFlavor a una carga de trabajo, Kueue asigna los Pods de la carga de trabajo a los nodos que coincidan con las etiquetas de nodo definidas para el ResourceFlavor.

Implementa ResourceFlavor:

kubectl apply -f flavors.yaml

Crea la ClusterQueue y la LocalQueue

Crea dos ClusterQueues cq-team-a y cq-team-b, y sus LocalQueues correspondientes lq-team-a y lq-team-b, respectivamente, con espacios de nombres en team-a y team-b.

Las ClusterQueues son objetos con permisos de clúster que rigen un grupo de recursos como los aceleradores de CPU, memoria y hardware. Los administradores de lotes pueden restringir la visibilidad de estos objetos a los usuarios por lotes.

LocalQueues son objetos con espacio de nombres que los usuarios pueden agrupar. Apuntan a CluterQueues, desde las cuales se asignan los recursos para ejecutar las cargas de trabajo de 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 permite que los recursos tengan múltiples variantes. En este caso, ambas ClusterQueues tienen dos variantes, on-demand y spot, cada una de las cuales proporciona recursos cpu. La cuota de spot de ResourceFlavor está configurada como 0 y no se usará por ahora.

Ambas ClusterQueues comparten la misma cohorte llamada all-teams, definida en .spec.cohort. Cuando dos o más ClusterQueues comparten la misma cohorte, pueden tomar prestada la cuota sin usar entre sí.

Puedes obtener más información sobre el funcionamiento de las cohortes y la semántica del préstamo en la doumentación de la UE.

Implementa las ClusterQueues y las LocalQueues:

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

Implementa kube-prometheus y supervisa cargas de trabajo con Prometheus (opcional)

Puedes usar Prometheus para supervisar las cargas de trabajo pendientes y las cargas de trabajo activas de Kueue. Para supervisar las cargas de trabajo que se activan y observar la carga en cada ClusterQueue, configura Prometheus en el clúster bajo la supervisión del espacio de nombres.

  1. Descarga el código fuente para el operador de Prometheus de Monitoring:

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

    kubectl create -f kube-prometheus/manifests/setup
    
  3. Crea los componentes de supervisión:

    kubectl create -f kube-prometheus/manifests
    
  4. Permite que el operador de Prometheus recopile métricas de los componentes de Kueue:

    kubectl apply -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/prometheus.yaml
    
  5. Cambia al directorio de trabajo:

    cd kubernetes-engine-samples/batch/kueue-cohort
    
  6. Inicia una terminal nueva para acceder a Prometheus mediante la redirección de puertos:

    kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090
    
  7. Abre Prometheus en localhost:9090 en el navegador

  8. Si usas Cloud Shell, haz clic en Vista previa en la Web, selecciona el puerto de cambio, establece el número de puerto en 9090 y selecciona Change and Preview.

  9. Ingresa la consulta para el primer panel que supervisa la ClusterQueue cq-team-a activa:

    kueue_pending_workloads{cluster_queue="cq-team-a", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-a"}
    
  10. Agrega otro panel y, luego, ingresa la consulta que supervisa la cq-team-b de ClusterQueue activa:

    kueue_pending_workloads{cluster_queue="cq-team-b", status="active"} or kueue_admitted_active_workloads{cluster_queue="cq-team-b"}
    
  11. Agrega otro panel y, luego, ingresa la consulta que supervisa la cantidad de nodos del clúster:

    count(kube_node_info)
    

Crea Jobs y observa las cargas de trabajo admitidas

Genera trabajos en ambas ClusterQueues que quedarán en espera durante 10 segundos, con tres trabajos paralelos y se completarán con tres finalizaciones. Luego, se limpiará después de 60 segundos.

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 trabajos en el espacio de nombres team-a y apunta a la LocalQueue lq-team-a y la ClusterQueue cq-team-a.

De manera similar, job-team-b.yaml crea trabajos en el espacio de nombres team-b y apunta a la LocalQueue lq-team-b y la ClusterQueue cq-team-b.

  1. Inicia una terminal nueva y ejecuta esta secuencia de comandos para generar un trabajo cada segundo:

    ./create_jobs.sh job-team-a.yaml 1
    
  2. Inicia otra terminal y crea trabajos para el espacio de nombres team-b:

    ./create_jobs.sh job-team-b.yaml 1
    
  3. Observa los trabajos que se ponen en cola en Prometheus. O con este comando:

    watch -n 2 kubectl get clusterqueues -o wide
    

El resultado debería ser similar al siguiente ejemplo:

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

Préstamo de cuotas sin uso con cohortes

Es posible que las ClusterQueues no estén a máxima capacidad en todo momento. El uso de cuotas no se maximiza cuando las cargas de trabajo no se distribuyen de manera uniforme entre las ClusterQueues. Si las ClusterQueues comparten la misma cohorte entre sí, las ClusterQueues pueden tomar prestadas las cuotas de otras ClusterQueues para maximizar el uso de la cuota.

  1. Una vez que hay trabajos en cola para ambas ClusterQueuescq-team-a ycq-team-b, detén la secuencia de comandos parateam-b espacio de nombres presionandoCTRL+c en la terminal correspondiente.

  2. Una vez que se procesen todos los trabajos pendientes del espacio de nombres team-b, los trabajos del espacio de nombres team-a pueden tomar prestados los recursos disponibles en cq-team-b:

    kubectl describe clusterqueue cq-team-a
    

    Debido a que cq-team-a y cq-team-b comparten la misma cohorte llamada all-teams, estas ClusterQueues pueden compartir recursos que no se usan.

      Flavors Usage:
        Name:  on-demand
        Resources:
          Borrowed:  5
          Name:      cpu
          Total:     15
          Borrowed:  5Gi
          Name:      memory
          Total:     15Gi
    
  3. Reanuda la secuencia de comandos para el espacio de nombres team-b.

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

    Observa cómo los recursos prestados de cq-team-a vuelven a 0, mientras que los recursos de cq-team-b se usan para sus propias cargas de trabajo:

    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 cuota con las VM Spot

Cuando la cuota deba aumentarse de forma temporal, por ejemplo, para satisfacer la alta demanda de las cargas de trabajo pendientes, puedes configurar Kueue a fin de que se adapte a la demanda. Para ello, agrega más ClusterQueues a la cohorte. Las ClusterQueues con recursos sin usar pueden compartir esos recursos con otras ClusterQueues que pertenezcan a la misma cohorte.

Al comienzo del instructivo, creaste un grupo de nodos llamado spot mediante las VM Spot y un ResourceFlavor llamado spot con la etiqueta configurada como cloud.google.com/gke-provisioning: spot. Crea una ClusterQueue para usar este grupo de nodos y el ResourceFlavor que lo representa:

  1. Crea una ClusterQueue nueva llamada cq-spot con la cohorte configurada como 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

    Debido a que ClusterCluster comparte la misma cohorte con cq-team-a y cq-team-b, tanto ClusterQueue cq-team-a y cq-team-b pueden tomar prestados recursos hasta 15 solicitudes de CPU y 15 Gi de memoria.

    kubectl apply -f cq-spot.yaml
    
  2. En Prometheus, observa cómo aumentan las cargas de trabajo admitidas para cq-team-a y cq-team-b, gracias a la cuota agregada de cq-spot que comparte la misma cohorte. O con este comando:

    watch -n 2 kubectl get clusterqueues -o wide
    
  3. En Prometheus, observa la cantidad de nodos en el clúster. O con este comando:

    watch -n 2 kubectl get nodes -o wide
    
  4. Para detener ambas secuencias de comandos, presiona CTRL+c para los espacios de nombres team-a y team-b.

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

Borra el proyecto

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra el recurso individual

  1. Borra el sistema de cuotas de 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. Borra el manifiesto de Kueue:

    VERSION=VERSION
    kubectl delete -f \
      https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    
  3. Borra el clúster:

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

¿Qué sigue?