Implementa un sistema por lotes con Kueue


En este instructivo, se muestra cómo implementar un sistema por lotes mediante Kueue para poner en cola un Job en Google Kubernetes Engine (GKE). Completa este instructivo para aprender a configurar GKE y Kueue a fin de ejecutar trabajos en un modelo primero en entrar, primero en salir (FIFO).

Contexto

Los Jobs son aplicaciones que se ejecutan hasta su finalización, como el aprendizaje automático, la renderización, la simulación, las estadísticas, la CI/CD y las cargas de trabajo similares.

Kueue es un programador de Job nativo de la nube que funciona con el programador predeterminado de Kubernetes, el controlador de Job y el escalador automático del clúster para proporcionar un sistema por lotes de extremo a extremo. Kueue implementa la cola de Jobs, decide cuándo deben esperar los Jobs y cuándo deben iniciarse según las cuotas y una jerarquía para compartir recursos de manera equitativa entre los equipos.

Kueue tiene las siguientes características:

  • Está optimizado para arquitecturas en la nube, en las que los recursos son heterogéneos, intercambiables y escalables.
  • Proporciona un conjunto de APIs para administrar cuotas elásticas y una cola de Jobs.
  • No vuelve a implementar la funcionalidad existente, como el ajuste de escala automático, la programación de Pods o la administración del ciclo de vida de los Jobs.
  • Kueue tiene compatibilidad integrada con la API de batch/v1.Job de Kubernetes.
  • Se puede integrar en otras API de trabajo.

Kueue hace referencia a los trabajos definidos con cualquier API como cargas de trabajo para evitar confusiones con la API de trabajo de Kubernetes específica.

Objetivos

Este instructivo es para operadores de clústeres y otros usuarios que desean implementar un sistema por lotes en Kubernetes. En este instructivo, configurarás un clúster compartido para dos equipos de usuarios. Cada equipo tiene su propio espacio de nombres en el que crea Jobs y comparte los mismos recursos globales que se controlan con las cuotas correspondientes.

En este instructivo, se abarcan los siguientes pasos:

  1. Crea un clúster de GKE
  2. Crea ResourceFlavor
  3. Crea ClusterQueue
  4. Crea LocalQueue
  5. Crea Jobs y observa las cargas de trabajo admitidas

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 termines este instructivo, borra los recursos que creaste para evitar que continúe la facturación. 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. 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. 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. 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. 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
    cd kubernetes-engine-samples/batch/kueue-intro
    
  3. Configura las variables de entorno predeterminadas:

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

    Reemplaza los siguientes valores:

Crea un clúster de GKE

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

    gcloud container clusters create-auto kueue-autopilot \
      --release-channel "rapid" --region COMPUTE_REGION
    

    Los clústeres de Autopilot están completamente administrados y tienen ajuste de escala automático integrado. Obtén más información sobre Autopilot de GKE.

    Kueue también admite GKE Standard con aprovisionamiento automático de nodos y grupos de nodos normales con ajuste de escala automático.

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

      NAME: kueue-autopilot
      LOCATION: us-central1
      MASTER_VERSION: 1.26.2-gke.1000
      MASTER_IP: 35.193.173.228
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.26.2-gke.1000
      NUM_NODES: 3
      STATUS: RUNNING
    

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

  2. Obtener las credenciales de autenticación para el clúster:

    gcloud container clusters get-credentials kueue-autopilot
    
  3. Instalar Kueue en el clúster:

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

    Reemplaza VERSION por la versión más reciente de Kueue. Para obtener más información sobre las versiones de Kueue, consulta las versiones de Kuueue.

  4. Espera hasta que los Pods de Kueue estén listos:

    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-66d8bb946b-wr2l2   2/2     Running   0          3m36s
    
  5. Crear dos espacios de nombres nuevos llamados team-a y team-b:

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

Crea ResourceFlavor

Un ResourceFlavor es un objeto que representa las variaciones en los nodos disponibles en tu clúster mediante la asociación de etiquetas y taints de nodo. Por ejemplo, puedes usar los objetos ResourceFlavor para representar las VMs con diferentes garantías de aprovisionamiento (por ejemplo, puntual y a pedido), arquitecturas (por ejemplo, CPU x86 frente a CPU ARM), marcas y modelos (por ejemplo, GPU Nvidia A100 frente a GPU T4).

En este instructivo, el clúster kueue-autopilot tiene recursos homogéneos. Como resultado, crea un solo objeto ResourceFlavor por CPU, memoria, almacenamiento efímero y GPU, sin etiquetas ni taints.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: default-flavor # This ResourceFlavor will be used for all the resources
Implementa ResourceFlavor:

kubectl apply -f flavors.yaml

Crea ClusterQueue

Un ClusterQueue es un objeto con permisos de clúster que administra un grupo de recursos, como CPU, memoria y GPU. Administra los objetos ResourceFlavor, limita el uso y determina el orden en que se admiten las cargas de trabajo.

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

Implementa ClusterQueue:

kubectl apply -f cluster-queue.yaml

El orden de consumo se determina mediante .spec.queueingStrategy, en el que hay dos parámetros de configuración:

  • BestEffortFIFO

    • La configuración predeterminada de la estrategia de cola.
    • La admisión de carga de trabajo sigue la regla primero en entrar, primero en salir (FIFO), pero si no hay suficiente cuota para admitir la carga de trabajo en el encabezado de la cola, se prueba la siguiente en la línea.
  • StrictFIFO

    • Garantiza la semántica de FIFO.
    • La carga de trabajo que está en el encabezado de la cola puede bloquear la cola hasta que se pueda admitir la carga de trabajo.

En cluster-queue.yaml, debes crear un objeto ClusterQueue nuevo llamado cluster-queue. Este objeto ClusterQueue administra cuatro recursos, cpu, memory, nvidia.com/gpu y ephemeral-storage con la variante creada en flavors.yaml. Las solicitudes en las especificaciones del Pod de la carga de trabajo consumen la cuota.

Cada variante incluye límites de uso representados como .spec.resourceGroups[].flavors[].resources[].nominalQuota. En este caso, ClusterQueue admite cargas de trabajo solo si se cumplen las siguientes condiciones:

  • La suma de las solicitudes de CPU es menor o igual que 10
  • La suma de las solicitudes de memoria es menor o igual que 10Gi
  • La suma de solicitudes de GPU es menor o igual que 10
  • La suma del almacenamiento usado es menor o igual que 10Gi

Crea LocalQueue

Un LocalQueue es un objeto con espacio de nombres que acepta cargas de trabajo de los usuarios en el espacio de nombres. Los objetos LocalQueue de diferentes espacios de nombres pueden apuntar al mismo ClusterQueue en el que pueden compartir la cuota de los recursos. En este caso, el objeto LocalQueue del espacio de nombres team-a y team-b apunta al mismo ClusterQueue cluster-queue en .spec.clusterQueue.

apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-a # LocalQueue under team-a namespace
  name: lq-team-a
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-b # LocalQueue under team-b namespace
  name: lq-team-b
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue

Cada equipo envía sus cargas de trabajo al objeto LocalQueue en su propio espacio de nombres. Luego, ClusterQueue asigna recursos.

Implementa los objetos LocalQueue:

kubectl apply -f local-queue.yaml

Crea Jobs y observa las cargas de trabajo admitidas

apiVersion: batch/v1
kind: Job
metadata:
  namespace: team-a # Job under team-a namespace
  generateName: sample-job-team-a-
  annotations:
    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:
      nodeSelector:
        cloud.google.com/gke-accelerator: "nvidia-tesla-t4" # Specify the GPU hardware
      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"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
      restartPolicy: Never

Los Jobs se crean en el espacio de nombres team-a. Este Job apunta al objeto LocalQueue lq-team-a. Para solicitar recursos de GPU, nodeSelector se establece en nvidia-tesla-t4.

El Job se compone de tres Pods que se suspenden durante 10 segundos en paralelo. Los Jobs se limpian después de 60 segundos según ttlSecondsAfterFinished.

Este Job requiere 1,500 millicores de CPU; 1,536 Mi de memoria; 1,536 Mi de almacenamiento efímero y tres GPUs, ya que hay tres Pods.

Los Jobs también se crean en el archivo job-team-b.yaml, en el que su espacio de nombres pertenece a team-b, con solicitudes para representar diferentes equipos con diferentes necesidades.

Para obtener más información, consulta Implementa cargas de trabajo de GPU en Autopilot.

  1. En una terminal nueva, observa el estado de ClusterQueue que se actualiza cada dos segundos:

    watch -n 2 kubectl get clusterqueue cluster-queue -o wide
    
  2. En una terminal nueva, observa el estado de los nodos:

    watch -n 2 kubectl get nodes -o wide
    
  3. En una terminal nueva, crea Jobs en LocalQueue desde el espacio de nombres team-a y team-b cada 10 segundos:

    ./create_jobs.sh job-team-a.yaml job-team-b.yaml 10
    
  4. Observa cómo se ponen en cola los Jobs, cómo se admiten en ClusterQueue y cómo se activan los nodos con Autopilot de GKE.

  5. Obtén un Job del espacio de nombres team-a:

    kubectl -n team-a get jobs
    

    El resultado es similar al siguiente:

    NAME                      COMPLETIONS   DURATION   AGE
    sample-job-team-b-t6jnr   3/3           21s        3m27s
    sample-job-team-a-tm7kc   0/3                      2m27s
    sample-job-team-a-vjtnw   3/3           30s        3m50s
    sample-job-team-b-vn6rp   0/3                      40s
    sample-job-team-a-z86h2   0/3                      2m15s
    sample-job-team-b-zfwj8   0/3                      28s
    sample-job-team-a-zjkbj   0/3                      4s
    sample-job-team-a-zzvjg   3/3           83s        4m50s
    
  6. Copia el nombre de un Job del paso anterior y observa el estado de admisión y los eventos para un Job a través de la API de cargas de trabajo:

    kubectl -n team-a describe workload JOB_NAME
    
  7. Cuando los Jobs pendientes comiencen a aumentar desde el objeto ClusterQueue, presiona CTRL + C en la secuencia de comandos en ejecución para finalizarla.

  8. Una vez que se hayan completado todos los Jobs, observa que los nodos se reducen.

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 cluster-queue
    kubectl delete resourceflavor default-flavor
    
  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-autopilot --region=COMPUTE_REGION
    

¿Qué sigue?