Escalonar para zero usando o KEDA

Neste tutorial, mostramos como reduzir o escalonamento das cargas de trabalho do GKE para zero Pods usando o KEDA. Ao reduzir as implantações para zero pods, você economiza recursos durante períodos de inatividade (como fins de semana e horários fora do expediente) ou para cargas de trabalho intermitentes, como jobs periódicos.

Objetivos

Este tutorial descreve os seguintes casos de uso:

Custos

Neste documento, você vai usar os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na sua projeção de uso, utilize a calculadora de preços.

Novos usuários do Google Cloud podem estar qualificados para um teste sem custo financeiro.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Para mais informações, consulte Limpeza.

Antes de começar

Neste tutorial, use o Cloud Shell para executar os comandos. O Cloud Shell é um ambiente shell para gerenciar recursos hospedados no Google Cloud. Ele vem pré-instalado com a Google Cloud CLI, o kubectl, o Helm e ferramentas de linha de comando do Terraform. Se você não usa o Cloud Shell, instale a Google Cloud CLI e o Helm.

  1. Para executar os comandos nesta página, configure a CLI gcloud em um dos seguintes ambientes de desenvolvimento:

    Cloud Shell

    Para usar um terminal on-line com a CLI gcloud já configurada, ative o Cloud Shell:

    Na parte de baixo desta página, uma sessão do Cloud Shell é iniciada e exibe um prompt de linha de comando. A inicialização da sessão pode levar alguns segundos.

    Shell local

    Para usar um ambiente de desenvolvimento local, siga estas etapas:

    1. Instale a CLI gcloud.
    2. Inicialize a CLI gcloud.
    3. Instale o Helm, uma ferramenta de gerenciamento de pacotes do 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. Como configurar o ambiente

    Para configurar o ambiente com o Cloud Shell, siga estas etapas:

    1. Defina as variáveis de ambiente:

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

      Substitua PROJECT_ID pelo Google Cloud ID do projeto e LOCATION pelas regiões ou zonas em que o cluster do GKE será criado.

      Se você não seguir todo o tutorial em uma única sessão ou se as variáveis de ambiente forem desdefinidas por algum motivo, execute esse comando novamente para definir as variáveis.

    2. Crie um cluster padrão do GKE com o escalonamento automático de cluster e a federação de identidade da carga de trabalho do GKE ativados:

      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 o KEDA

    O KEDA é um componente que complementa o escalonador automático horizontal de pods do Kubernetes. Com o KEDA, é possível escalonar uma implantação para zero pods e de zero para um pod. Uma implantação é um objeto da API Kubernetes que permite executar várias réplicas de pods distribuídos entre os nós de um cluster. O algoritmo padrão do autoescalador de pods horizontal é aplicado depois que o GKE cria pelo menos um pod.

    Depois que o GKE escalona a implantação para zero pods, como nenhum pod está em execução, o escalonamento automático não pode depender de métricas de pod, como a utilização da CPU. Como consequência, o KEDA permite buscar métricas originadas de fora do cluster usando uma implementação da API External Metrics do Kubernetes. É possível usar essa API para escalonar automaticamente com base em métricas como o número de mensagens pendentes em uma assinatura do Pub/Sub. Consulte a documentação do KEDA para ver uma lista de todas as origens de métricas compatíveis.

    Instale o KEDA no cluster com o Helm ou com kubectl.

    Helm

    Execute os comandos a seguir para adicionar o repositório do Helm do KEDA, instalar o gráfico do Helm do KEDA e conceder à conta de serviço do KEDA acesso de leitura ao 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
    

    Esse comando também configura regras de autorização que exigem que o cluster seja configurado com a federação de identidade da carga de trabalho do GKE.

    kubectl

    Execute os comandos a seguir para instalar o KEDA usando kubectl apply e conceder à conta de serviço do KEDA acesso de leitura ao 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
    

    Esse comando também configura regras de autorização que exigem que o cluster seja configurado com a federação de identidade da carga de trabalho do GKE.

    Confirme se todos os recursos do KEDA aparecem no namespace keda:

    kubectl get all -n keda
    

    Para mais informações sobre o design e os recursos do KEDA, consulte a documentação do KEDA.

    Escalonar sua carga de trabalho do Pub/Sub para zero

    Esta seção descreve uma carga de trabalho que processa mensagens de uma assinatura do Pub/Sub, processando cada mensagem e confirmando a conclusão dela. A carga de trabalho é escalonada dinamicamente: à medida que o número de mensagens não confirmadas aumenta, o escalonamento automático cria mais pods para garantir o processamento em tempo hábil.

    O escalonamento para zero garante que nenhum pod seja instanciado quando nenhuma mensagem for recebida por um tempo. Isso economiza recursos, já que nenhum pod fica ocioso por longos períodos.

    Implantar uma carga de trabalho do Pub/Sub

    Implante uma carga de trabalho de amostra que processe mensagens enfileiradas em um tópico do Pub/Sub. Para simular uma carga de trabalho realista, este programa de amostra aguarda três segundos antes de confirmar uma mensagem. A carga de trabalho está configurada para ser executada na conta de serviço keda-pubsub-sa.

    Execute os comandos a seguir para criar o tópico e a assinatura do Pub/Sub, configurar a permissão deles e criar a implantação que inicia a carga de trabalho no namespace 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 o escalonamento para zero

    Para configurar sua carga de trabalho do Pub/Sub para escalonar até zero, use o KEDA para definir um recurso ScaledObject e especificar como o escalonamento do deployment deve ser feito. Em seguida, o KEDA cria e gerencia automaticamente o objeto HorizontalPodAutoscaler (HPA) subjacente.

    1. Crie o recurso ScaledObject para descrever o comportamento esperado do escalonamento 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 -
      

      Isso cria o seguinte 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. Inspecione o objeto HorizontalPodAutoscaler (HPA) que o KEDA cria com base no objeto ScaledObject:

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

      Leia mais sobre o escalonamento automático na documentação do Kubernetes.

    3. Aguarde até que o KEDA reconheça que a assinatura do Pub/Sub está vazia e dimensione a implantação para zero réplicas.

      Inspecione o escalonador automático de carga de trabalho:

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

      Observe que, na resposta do comando, a condição ScalingActive é falsa. A mensagem associada mostra que o Escalonador automático horizontal de pods reconhece que o KEDA escalonou a implantação para zero, momento em que ele para de operar até que a implantação seja escalonada de volta para um 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
      

    Acionar o escalonamento vertical

    Para estimular o escalonar verticalmente da implantação:

    1. Enfileirar mensagens no tópico do Pub/Sub:

      for num in {1..20}
      do
        gcloud pubsub topics publish keda-echo --project=${PROJECT_ID} --message="Test"
      done
      
    2. Verifique se a implantação está sendo escalonada:

      kubectl get deployments -n keda-pubsub
      

      Na saída, observe que a coluna "Pronto" mostra uma réplica:

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

    O KEDA aumenta a implantação depois de observar que a fila não está vazia.

    Escalonar sua carga de trabalho de LLM para zero

    Esta seção descreve uma carga de trabalho de modelo de linguagem grande (LLM) que implanta um servidor Ollama com GPU anexada. O Ollama permite executar LLMs conhecidos, como Gemma e Llama 2, e expõe seus recursos principalmente por HTTP.

    Instalar o complemento KEDA-HTTP

    Reduzir um serviço HTTP para zero pods durante períodos de inatividade causa falhas de solicitação, já que não há um back-end para processar as solicitações.

    Esta seção mostra como resolver esse problema usando o complemento KEDA-HTTP. O KEDA-HTTP inicia um proxy HTTP que recebe solicitações do usuário e as encaminha para os serviços configurados para escalonamento até zero. Quando o serviço não tem um pod, o proxy aciona o escalonar verticalmente do serviço e armazena em buffer a solicitação até que o serviço seja escalonado verticalmente para pelo menos um pod.

    Instale o complemento KEDA-HTTP usando o Helm. Para mais informações, consulte a documentação do 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
    

    Implantar uma carga de trabalho de LLM do Ollama

    Para implantar uma carga de trabalho de LLM do Ollama:

    1. Crie um pool de nós com g2-standard-4 nós e GPUs anexadas e configure o escalonamento automático do cluster para fornecer entre zero e dois nós:

      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. Adicione o repositório oficial de gráficos do Helm do Ollama e atualize o repositório do cliente Helm local:

      helm repo add ollama-helm https://otwld.github.io/ollama-helm/
      helm repo update
      
    3. Implante o servidor Ollama usando o gráfico 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
      

      A configuração helm-values-ollama.yaml especifica os modelos de LLM a serem carregados, os requisitos de GPU e a porta TCP do servidor Ollama.

    Configurar o escalonamento para zero

    Para configurar sua carga de trabalho do Ollama para escalonar até zero, o KEDA-HTTP usa um HTTPScaledObject.

    1. Crie o recurso HTTPScaledObject para descrever o comportamento esperado do escalonamento 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
      

      Isso cria o objeto HTTPScaledObject que define os seguintes campos:

      • scaleTargetRef: especifica o serviço para o qual o KEDA-HTTP precisa encaminhar as solicitações. Neste exemplo, todas as solicitações com o host ollama.ollama são encaminhadas para o servidor Ollama.
      • scaledownPeriod: especifica (em segundos) a velocidade de reduzir escala vertical quando nenhuma solicitação é recebida.
      • replicas: especifica o número mínimo e máximo de pods a serem mantidos para a implantação do Ollama.
      • scalingMetric: especifica as métricas usadas para acionar o escalonamento automático, como a taxa de solicitações neste exemplo. Para mais opções de métricas, consulte a documentação do 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. Execute o seguinte comando para verificar se o KEDA-HTTP processou o HTTPScaledObject criado na etapa anterior:

      kubectl get hpa,scaledobject -n ollama
      

      A saída mostra os recursos HorizontalPodAutoscaler (criados pelo KEDA) e ScaledObject (criados pelo 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. Verifique se a implantação é reduzida a zero pods.

      Aguarde o período definido no campo scaledownPeriod e execute o comando:

      kubectl get deployments -n ollama
      

      A saída mostra que o KEDA reduziu a implantação do Ollama e que nenhum pod está em execução:

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

    Acionar o escalonamento vertical

    Para estimular o escalonar verticalmente da implantação, chame o serviço Ollama usando o proxy configurado pelo complemento KEDA-HTTP. Isso faz com que o valor da métrica taxa de solicitação aumente e aciona a criação de um primeiro pod.

    Use os recursos de encaminhamento de porta kubectl para acessar o proxy, porque ele não é exposto 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!" }'
    

    O comando curl envia o comando "Olá!" para um modelo da Gemma. Observe os tokens de resposta retornando na resposta. Para a especificação da API, consulte o guia do Ollama.

    Limpar

    Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

    1. Remova a inscrição e o tópico do Pub/Sub:

      gcloud pubsub subscriptions delete keda-echo-read
      gcloud pubsub topics delete keda-echo
      
    2. Exclua seu cluster do GKE:

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

    A seguir