Escalar a cero con KEDA

En este tutorial se muestra cómo reducir el escalado de tus cargas de trabajo de GKE a cero pods mediante KEDA. Al reducir las implementaciones a cero pods, se ahorran recursos durante los periodos de inactividad (como los fines de semana y las horas no lectivas) o para cargas de trabajo intermitentes, como los trabajos periódicos.

Objetivos

En este tutorial se describen los siguientes casos prácticos:

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos pueden disfrutar de una prueba gratuita. Google Cloud

Cuando termines las tareas que se describen en este documento, puedes evitar que se te siga facturando eliminando los recursos que hayas creado. Para obtener más información, consulta la sección Limpiar.

Antes de empezar

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

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

    Cloud Shell

    Para usar un terminal online con la CLI de gcloud ya configurada, activa Cloud Shell:

    En la parte inferior de esta página, se inicia una sesión de Cloud Shell y se muestra un mensaje de la 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 gcloud CLI.
    2. Inicializa gcloud CLI.
    3. Instala Helm, una herramienta de gestió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.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    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.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

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

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    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.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  9. Configurar un entorno

    .

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

    1. Define las variables de entorno:

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

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

      Si no sigues todo el tutorial en una sola sesión o si las variables de entorno no se han definido por algún motivo, asegúrate de volver a ejecutar este comando para definirlas de nuevo.

    2. Crea un clúster de GKE estándar con el autoescalado de clústeres y Workload Identity Federation for 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
      

    Instalar KEDA

    KEDA es un componente que complementa el autoescalado horizontal de pods de Kubernetes. Con KEDA, puedes escalar una implementación 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 distribuidas entre los nodos de un clúster. El algoritmo de autoescalado horizontal de pods estándar se aplica después de que GKE cree al menos un pod.

    Después de que GKE escale el Deployment a cero pods, como no se ejecuta ningún pod, el autoescalado no puede basarse en métricas de pods, como el uso de CPU. Por lo tanto, KEDA permite obtener métricas procedentes de fuera del clúster mediante una implementación de la API de métricas externas de Kubernetes. Puede usar esta API para autoescalar en función de métricas como el número de mensajes pendientes en una suscripción de Pub/Sub. Consulta la documentación de KEDA para ver 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 añadir el repositorio de Helm de KEDA, instalar el gráfico de Helm de KEDA y conceder 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 Workload Identity Federation para GKE.

    kubectl

    Ejecuta los siguientes comandos para instalar KEDA con kubectl apply y para dar 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 Workload Identity Federation para GKE.

    Confirma que todos los recursos de KEDA aparecen 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.

    Escalar a cero tu carga de trabajo de Pub/Sub

    En esta sección se describe una carga de trabajo que procesa mensajes de una suscripción de Pub/Sub, gestiona cada mensaje y confirma que se ha completado. La carga de trabajo se escala de forma dinámica: a medida que aumenta el número de mensajes no confirmados, el autoescalado crea más pods para asegurar que se procesen a tiempo.

    El escalado a cero asegura que no se instancien pods cuando no se hayan recibido mensajes durante un tiempo. De esta forma, se ahorran recursos, ya que ningún pod permanece inactivo durante largos periodos.

    Desplegar una carga de trabajo de Pub/Sub

    Despliega una carga de trabajo de ejemplo que procesa los mensajes puestos en cola en un tema de Pub/Sub. Para simular una carga de trabajo realista, este programa de ejemplo espera tres segundos antes de confirmar un mensaje. La carga de trabajo se ha configurado para que se ejecute 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 implementación 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
    

    Configurar el escalado a cero

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

    1. Crea el recurso ScaledObject para describir el comportamiento de autoescalado esperado:

      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 -
      

      De esta forma, se 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 crea KEDA en función del objeto ScaledObject:

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

      Puedes consultar más información sobre el autoescalado en la documentación de Kubernetes.

    3. Espera hasta que KEDA confirme que la suscripción de Pub/Sub está vacía y escale la implementación 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 escalado automático horizontal de pods reconoce que KEDA ha escalado el despliegue a cero, momento en el que deja de funcionar hasta que el despliegue vuelve a escalar 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
      

    Activar el aumento de escala

    Para estimular la implementación y aumentar la escala, sigue estos pasos:

    1. Poner en cola los 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 la implementación se esté ampliando:

      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 del Deployment después de observar que la cola no está vacía.

    Escalar a cero la carga de trabajo de LLM

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

    Instalar el complemento KEDA-HTTP

    Si se reduce la escala de un servicio HTTP a cero pods durante los periodos de inactividad, se producirán errores en las solicitudes, ya que no habrá ningún backend que las gestione.

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

    Instala el complemento 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
    

    Desplegar una carga de trabajo de LLM de Ollama

    Para desplegar una carga de trabajo de LLM de Ollama, sigue estos pasos:

    1. Crea un grupo de nodos que contenga g2-standard-4 nodos con GPUs conectadas y configura el autoescalado de 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. Añade el repositorio oficial de charts de Helm de Ollama y actualiza el repositorio de tu cliente de Helm local:

      helm repo add ollama-helm https://otwld.github.io/ollama-helm/
      helm repo update
      
    3. Despliega 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 helm-values-ollama.yaml especifica los modelos de LLM que se van a cargar, los requisitos de GPU y el puerto TCP del servidor de Ollama.

    Configurar el escalado a cero

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

    1. Crea el recurso HTTPScaledObject para describir el comportamiento de autoescalado esperado:

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

      De esta forma, se 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 dirigen al servidor de Ollama.
      • scaledownPeriod: especifica (en segundos) la velocidad a la que se debe reducir la escala cuando no se reciben solicitudes.
      • replicas: especifica el número mínimo y máximo de pods que se deben mantener en la implementación de Ollama.
      • scalingMetric: especifica las métricas que se usan para controlar el autoescalado, como la tasa de solicitudes en este ejemplo. Para ver 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 ha procesado correctamente el HTTPScaledObject creado en el paso anterior:

      kubectl get hpa,scaledobject -n ollama
      

      El resultado muestra el recurso HorizontalPodAutoscaler (creado por KEDA) y el recurso 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 la implementación se reduce a cero pods.

      Espera el periodo de tiempo definido en el campo scaledownPeriod y ejecuta el comando:

      kubectl get deployments -n ollama
      

      El resultado muestra que KEDA ha reducido el escalado del despliegue de Ollama y que no se está ejecutando ningún pod:

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

    Activar el aumento de escala

    Para estimular el escalado vertical de la implementación, llama al servicio Ollama mediante el proxy configurado por el complemento KEDA-HTTP. Esto provoca que aumente el valor de la métrica Tasa de solicitudes y que se cree un primer pod.

    Usa las funciones de reenvío de puertos kubectl para acceder al proxy, ya que este no está expuesto externamente.

    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 petición "¡Hola!" a un modelo de Gemma. Observa los tokens de respuesta que se devuelven en la respuesta. Para ver las especificaciones de la API, consulta la guía de Ollama.

    Limpieza

    Para evitar que los recursos utilizados en este tutorial se cobren en tu cuenta de Google Cloud, elimina el proyecto que contiene los recursos o conserva el proyecto y elimina los recursos.

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

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

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

    Siguientes pasos