Escala a cero con KEDA


En este instructivo, se muestra cómo reducir la escala de tus cargas de trabajo de GKE a cero Pods con KEDA. Escalar las implementaciones a cero Pods ahorra recursos durante los períodos de inactividad (como los fines de semana y los horarios no laborales) o para cargas de trabajo intermitentes, como los trabajos periódicos.

Objetivos

En este instructivo, se describen los siguientes casos de uso:

  • Escala tu carga de trabajo de Pub/Sub a cero: Escala la cantidad de Pods en proporción a la cantidad de mensajes en cola en el tema de Pub/Sub. Cuando la cola está vacía, la carga de trabajo se reduce automáticamente a cero Pods.
  • Escala tu carga de trabajo de LLM a cero. Implementa tus servidores de modelos LLM en nodos con GPU. Cuando el servicio está inactivo, la carga de trabajo se reduce automáticamente a cero Pods.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios.

Es posible que los usuarios Google Cloud nuevos cumplan con los requisitos para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar 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

En este instructivo, usarás Cloud Shell para ejecutar comandos. Cloud Shell es un entorno de shell que se usa para administrar recursos alojados en Google Cloud. Viene preinstalado con las herramientas de línea de comandos de Google Cloud CLI, kubectl, Helm y Terraform. Si no usas Cloud Shell, debes instalar Google Cloud CLI y Helm.

  1. Para ejecutar los comandos de esta página, configura gcloud CLI en uno de los siguientes entornos de desarrollo:

    Cloud Shell

    Para usar una terminal en línea con gcloud CLI ya configurada, activa Cloud Shell:

    En la parte inferior de esta página, se inicia una sesión de Cloud Shell y se muestra una ventana de línea de comandos. La sesión puede tardar unos segundos en inicializarse.

    Shell local

    Para usar un entorno de desarrollo local, sigue estos pasos:

    1. Instala la CLI de gcloud
    2. Inicializa la CLI de gcloud.
    3. Instala Helm, una herramienta de administración de paquetes de Kubernetes.
  2. 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.
  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  4. Verify that billing is enabled for your Google Cloud project.

  5. Enable the Resource Manager, Compute Engine, GKE, Pub/Sub APIs.

    Enable the APIs

  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  7. Verify that billing is enabled for your Google Cloud project.

  8. Enable the Resource Manager, Compute Engine, GKE, Pub/Sub APIs.

    Enable the APIs

  9. Configura tu entorno

    Para configurar tu entorno con Cloud Shell, sigue estos pasos:

    1. Establece las variables de entorno:

      export PROJECT_ID=PROJECT_ID
      export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format 'get(projectNumber)')
      export LOCATION=LOCATION
      

      Reemplaza PROJECT_ID por tu Google Cloud ID del proyecto y LOCATION por las regiones o zonas en las que se debe crear tu clúster de GKE.

      Si no sigues todo el instructivo en una sola sesión o si tus variables de entorno no se establecen por algún motivo, asegúrate de volver a ejecutar este comando para establecer las variables nuevamente.

    2. Crea un clúster de GKE Estándar con el ajuste de escala automático del clúster y la federación de identidades para cargas de trabajo en GKE habilitados:

      gcloud container clusters create scale-to-zero \
          --project=${PROJECT_ID} --location=${LOCATION} \
          --machine-type=n1-standard-2 \
          --enable-autoscaling --min-nodes=1 --max-nodes=5 \
          --workload-pool=${PROJECT_ID}.svc.id.goog
      

      Instala KEDA

      KEDA es un componente que complementa el escalador automático de Pod horizontal de Kubernetes. Con KEDA, puedes ajustar la escala de una Deployment a cero Pods y de cero Pods a un Pod. Un Deployment es un objeto de la API de Kubernetes que te permite ejecutar varias réplicas de Pods que se distribuyen entre los nodos de un clúster. El algoritmo estándar del Horizontal Pod Autoscaler se aplica después de que GKE crea al menos un Pod.

      Después de que GKE ajusta la escala de la Deployment a cero Pods, debido a que no se ejecutan Pods, el ajuste de escala automático no puede depender de las métricas de Pod, como el uso de CPU. Como consecuencia, KEDA permite recuperar métricas que se originan fuera del clúster con una implementación de la API de External Metrics de Kubernetes. Puedes usar esta API para ajustar la escala automáticamente en función de métricas como la cantidad de mensajes pendientes en una suscripción a Pub/Sub. Consulta la documentación de KEDA para obtener una lista de todas las fuentes de métricas admitidas.

      Instala KEDA en tu clúster con Helm o con kubectl.

      Helm

      Ejecuta los siguientes comandos para agregar el repositorio de Helm de KEDA, instalar el gráfico de Helm de KEDA y otorgar a la cuenta de servicio de KEDA acceso de lectura a Cloud Monitoring:

      helm repo add kedacore https://kedacore.github.io/charts
      helm repo update
      helm install keda kedacore/keda --create-namespace --namespace keda
      
      gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
           --role roles/monitoring.viewer \
           --member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda/sa/keda-operator
      

      Ten en cuenta que este comando también configura reglas de autorización que requieren que el clúster se configure con la federación de identidades para cargas de trabajo para GKE.

      kubectl

      Ejecuta los siguientes comandos para instalar KEDA con kubectl apply y otorgar a la cuenta de servicio de KEDA acceso de lectura a Cloud Monitoring:

      kubectl apply --server-side  -f https://github.com/kedacore/keda/releases/download/v2.15.1/keda-2.15.1.yaml
      
      gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
           --role roles/monitoring.viewer \
           --member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda/sa/keda-operator
      

      Ten en cuenta que este comando también configura reglas de autorización que requieren que el clúster se configure con la federación de identidades para cargas de trabajo para GKE.

      Confirma que todos los recursos de KEDA aparezcan en el espacio de nombres keda:

      kubectl get all -n keda
      

      Para obtener más información sobre el diseño y los recursos de KEDA, consulta la documentación de KEDA.

      Cómo reducir la escala de tu carga de trabajo de Pub/Sub a cero

      En esta sección, se describe una carga de trabajo que procesa mensajes de una suscripción a Pub/Sub, controla cada mensaje y confirma su finalización. La carga de trabajo se ajusta de forma dinámica: a medida que aumenta la cantidad de mensajes no confirmados, el ajuste de escala automático crea más Pods para garantizar el procesamiento oportuno.

      El ajuste a cero garantiza que no se creen instancias de Pods cuando no se hayan recibido mensajes durante un tiempo. Esto ahorra recursos, ya que ningún Pod permanece inactivo durante períodos prolongados.

      Implementa una carga de trabajo de Pub/Sub

      Implementa una carga de trabajo de muestra que procese los mensajes en cola en un tema de Pub/Sub. Para simular una carga de trabajo realista, este programa de muestra espera tres segundos antes de confirmar la recepción de un mensaje. La carga de trabajo está configurada para ejecutarse con la cuenta de servicio keda-pubsub-sa.

      Ejecuta los siguientes comandos para crear el tema y la suscripción de Pub/Sub, configurar sus permisos y crear la Deployment que inicia la carga de trabajo en el espacio de nombres keda-pubsub.

      gcloud pubsub topics create keda-echo
      gcloud pubsub subscriptions create keda-echo-read --topic=keda-echo
      gcloud projects add-iam-policy-binding projects/${PROJECT_ID}  \
          --role=roles/pubsub.subscriber \
        --member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda-pubsub/sa/keda-pubsub-sa
      
      kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/cloud-pubsub/deployment/keda-pubsub-with-workload-identity.yaml
      

      Configura la reducción de escala a cero

      Para configurar tu carga de trabajo de Pub/Sub de modo que se escale a cero, usa KEDA para definir un recurso ScaledObject y especificar cómo se debe escalar la implementación. Luego, KEDA creará y administrará automáticamente el objeto HorizontalPodAutoscaler (HPA) subyacente.

      1. Crea el recurso ScaledObject para describir el comportamiento esperado del ajuste de escala automático:

        curl https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/cloud-pubsub/deployment/keda-pubsub-scaledobject.yaml | envsubst | kubectl apply -f -
        

        Esto crea el siguiente objeto:

        apiVersion: keda.sh/v1alpha1
        kind: ScaledObject
        metadata:
          name: keda-pubsub
          namespace: keda-pubsub
        spec:
          maxReplicaCount: 5
          scaleTargetRef:
            name: keda-pubsub
          triggers:
            - type: gcp-pubsub
              authenticationRef:
                name: keda-auth
              metadata:
                subscriptionName: "projects/${PROJECT_ID}/subscriptions/keda-echo-read"
        
      2. Inspecciona el objeto HorizontalPodAutoscaler (HPA) que KEDA crea en función del objeto ScaledObject:

        kubectl get hpa keda-hpa-keda-pubsub -n keda-pubsub -o yaml
        

        Puedes leer más sobre el ajuste de escala automático en la documentación de Kubernetes.

      3. Espera hasta que KEDA confirme que la suscripción a Pub/Sub está vacía y ajuste la Deployment a cero réplicas.

        Inspecciona el escalador automático de la carga de trabajo:

        kubectl describe hpa keda-hpa-keda-pubsub -n keda-pubsub
        

        Observa que, en la respuesta del comando, la condición ScalingActive es falsa. El mensaje asociado muestra que el Horizontal Pod Autoscaler reconoce que KEDA redujo la implementación a cero, momento en el que deja de funcionar hasta que la Deployment vuelve a aumentar a un Pod.

        Name:                                                  keda-hpa-keda-pubsub
        Namespace:                                             keda-pubsub
        Metrics:                                               ( current / target )
          "s0-gcp-ps-projects-[...]]" (target average value):  0 / 10
        Min replicas:                                          1
        Max replicas:                                          5
        Deployment pods:                                       5 current / 5 desired
        Conditions:
          Type            Status  Reason               Message
          ----            ------  ------               -------
          AbleToScale     True    ScaleDownStabilized  recent recommendations were higher than current one [...]
          ScalingActive   False   ScalingDisabled      scaling is disabled since the replica count of the target is zero
          ScalingLimited  True    TooManyReplicas      the desired replica count is more than the maximum replica count
        

      Activa el escalamiento vertical

      Para estimular el aumento de la escala de la Deployment, haz lo siguiente:

      1. Pon en cola mensajes en el tema de Pub/Sub:

        for num in {1..20}
        do
          gcloud pubsub topics publish keda-echo --project=${PROJECT_ID} --message="Test"
        done
        
      2. Verifica que el Deployment esté escalando verticalmente:

        kubectl get deployments -n keda-pubsub
        

        En el resultado, observa que la columna "Ready" muestra una réplica:

        NAME          READY   UP-TO-DATE   AVAILABLE   AGE
        keda-pubsub   1/1     1            1           2d
        

      KEDA aumenta la escala de la Deployment después de observar que la fila no está vacía.

      Cómo reducir a cero la escala de tu carga de trabajo de LLM

      En esta sección, se describe una carga de trabajo de modelo de lenguaje grande (LLM) que implementa un servidor de Ollama con una GPU adjunta. Ollama permite ejecutar LLMs populares, como Gemma y Llama 2, y expone sus funciones principalmente a través de HTTP.

      Instala el complemento KEDA-HTTP

      Reducir la cantidad de Pods de un servicio HTTP a cero durante los períodos de inactividad provoca errores en las solicitudes, ya que no hay un backend que las controle.

      En esta sección, se muestra cómo resolver este problema con el complemento KEDA-HTTP. KEDA-HTTP inicia un proxy HTTP que recibe solicitudes de los usuarios y las reenvía a los servicios configurados para escalar a cero. Cuando el Service no tiene ningún Pod, el proxy activa el Service para que se escale verticalmente y almacena en búfer la solicitud hasta que el Service se haya escalado verticalmente a al menos un Pod.

      Instala el complemento de KEDA-HTTP con Helm. Para obtener más información, consulta la documentación de KEDA-HTTP.

      helm repo add ollama-helm https://otwld.github.io/ollama-helm/
      helm repo update
      
      # Set the proxy timeout to 120s, giving Ollama time to start.
      helm install http-add-on kedacore/keda-add-ons-http  \
        --create-namespace --namespace keda \
        --set interceptor.responseHeaderTimeout=120s
      

      Implementa una carga de trabajo de LLM de Ollama

      Para implementar una carga de trabajo de LLM de Ollama, haz lo siguiente:

      1. Crea un grupo de nodos que contenga nodos g2-standard-4 con GPUs conectadas y configura el ajuste de escala automático del clúster para que proporcione entre cero y dos nodos:

        gcloud container node-pools create gpu --machine-type=g2-standard-4 \
            --location=${LOCATION} --cluster=scale-to-zero \
            --min-nodes 0 --max-nodes 2 --num-nodes=1 --enable-autoscaling
        
      2. Agrega el repositorio oficial de gráficos de Helm de Ollama y actualiza el repositorio del cliente de Helm local:

        helm repo add ollama-helm https://otwld.github.io/ollama-helm/
        helm repo update
        
      3. Implementa el servidor de Ollama con el gráfico de Helm:

        helm install ollama ollama-helm/ollama --create-namespace --namespace ollama \
          -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/ollama/helm-values-ollama.yaml
        

        La configuración de helm-values-ollama.yaml especifica los modelos de LLM que se cargarán, los requisitos de GPU y el puerto TCP para el servidor de Ollama.

      Configura la reducción de escala a cero

      Para configurar tu carga de trabajo de Ollama de modo que se escale a cero, KEDA-HTTP usa un HTTPScaledObject.

      1. Crea el recurso HTTPScaledObject para describir el comportamiento esperado del ajuste de escala automático:

        kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/ollama/keda-ollama-httpscaledobject.yaml
        

        Esto crea el objeto HTTPScaledObject que define los siguientes campos:

        • scaleTargetRef: Especifica el servicio al que KEDA-HTTP debe reenviar las solicitudes. En este ejemplo, todas las solicitudes con el host ollama.ollama se enrutan al servidor de Ollama.
        • scaledownPeriod: Especifica (en segundos) qué tan rápido se debe reducir la escala cuando no se reciben solicitudes.
        • replicas: Especifica la cantidad mínima y máxima de Pods que se deben mantener para la implementación de Ollama.
        • scalingMetric: Especifica las métricas que se usan para controlar el ajuste de escala automático, como la tasa de solicitudes en este ejemplo. Para obtener más opciones de métricas, consulta la documentación de KEDA-HTTP.
        kind: HTTPScaledObject
        apiVersion: http.keda.sh/v1alpha1
        metadata:
            namespace: ollama
            name: ollama
        spec:
            hosts:
            - ollama.ollama
            scaleTargetRef:
                name: ollama
                kind: Deployment
                apiVersion: apps/v1
                service: ollama
                port: 11434
            replicas:
                min: 0
                max: 2
            scaledownPeriod: 3600
            scalingMetric:
                requestRate:
                    targetValue: 20
        
      2. Ejecuta el siguiente comando para verificar que KEDA-HTTP haya procesado correctamente el HTTPScaledObject creado en el paso anterior:

        kubectl get hpa,scaledobject -n ollama
        

        El resultado muestra los recursos HorizontalPodAutoscaler (creado por KEDA) y ScaledObject (creado por KEDA-HTTP):

        NAME                                                  REFERENCE           TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
        horizontalpodautoscaler.autoscaling/keda-hpa-ollama   Deployment/ollama   0/100 (avg)   1         2         1          2d
        
        NAME                          SCALETARGETKIND      SCALETARGETNAME   MIN   MAX   TRIGGERS        AUTHENTICATION   READY   ACTIVE   FALLBACK   PAUSED    AGE
        scaledobject.keda.sh/ollama   apps/v1.Deployment   ollama            0     2     external-push                    True    False    False      Unknown   2d
        
      3. Verifica que el Deployment se reduzca a cero Pods.

        Espera el período establecido en el campo scaledownPeriod y ejecuta el comando:

        kubectl get deployments -n ollama
        

        El resultado muestra que KEDA redujo la implementación de Ollama y que no se está ejecutando ningún Pod:

        NAME     READY   UP-TO-DATE   AVAILABLE   AGE
        ollama   0/0     0            0           2d
        

      Activa el escalamiento vertical

      Para estimular el aumento de la Deployment, llama al servicio de Ollama con el proxy configurado por el complemento de KEDA-HTTP. Esto hace que aumente el valor de la métrica tasa de solicitudes y activa la creación de un primer Pod.

      Usa las capacidades de reenvío de puertos de kubectl para acceder al proxy, ya que este no se expone de forma externa.

      kubectl port-forward svc/keda-add-ons-http-interceptor-proxy -n keda 8080:8080 &
      
      # Set the 'Host' HTTP header so that the proxy routes requests to the Ollama server.
      curl -H "Host: ollama.ollama" \
        http://localhost:8080/api/generate \
        -d '{ "model": "gemma:7b", "prompt": "Hello!" }'
      

      El comando curl envía la instrucción "¡Hola!" a un modelo de Gemma. Observa los tokens de respuesta que se muestran en la respuesta. Para conocer la especificación de la API, consulta la guía de Ollama.

      Limpia

      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.

      1. Limpia la suscripción y el tema de Pub/Sub:

        gcloud pubsub subscriptions delete keda-echo-read
        gcloud pubsub topics delete keda-echo
        
      2. Borra tu clúster de GKE:

        gcloud container clusters delete scale-to-zero --location=${LOCATION}
        

      ¿Qué sigue?