Aplique a escalabilidade para zero com o KEDA

Este tutorial mostra como reduzir as cargas de trabalho do GKE para zero pods com o KEDA. A redução das implementações para zero pods permite poupar recursos durante períodos de inatividade (como fins de semana e fora do horário de expediente) ou para cargas de trabalho intermitentes, como tarefas periódicas.

Objetivos

Este tutorial descreve os seguintes exemplos de utilização:

Custos

Neste documento, usa os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custos com base na sua utilização projetada, use a calculadora de preços.

Os novos Google Cloud utilizadores podem ser elegíveis para uma avaliação gratuita.

Quando terminar as tarefas descritas neste documento, pode evitar a faturação contínua eliminando os recursos que criou. Para mais informações, consulte o artigo Limpe.

Antes de começar

Neste tutorial, vai usar a Cloud Shell para executar comandos. O Cloud Shell é um ambiente de shell para gerir recursos alojados no Google Cloud. É fornecido pré-instalado com as ferramentas de linha de comandos CLI do Google Cloud, kubectl, Helm e Terraform. Se não usar o Cloud Shell, tem de instalar a Google Cloud CLI e o Helm.

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

    Cloud Shell

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

    Na parte inferior desta página, é iniciada uma sessão do Cloud Shell e é apresentado um pedido de linha de comandos. A sessão pode demorar alguns segundos a ser inicializada.

    Shell local

    Para usar um ambiente de desenvolvimento local, siga estes passos:

    1. Instale a CLI gcloud.
    2. Inicialize a CLI gcloud.
    3. Instale o Helm, uma ferramenta de gestão 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 (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 (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 o seu ambiente

    Para configurar o seu ambiente com o Cloud Shell, siga estes passos:

    1. Defina 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 seu Google Cloud ID do projeto e LOCATION pelas regiões ou zonas onde o seu cluster do GKE deve ser criado.

      Se não seguir todo o tutorial numa única sessão ou se as variáveis de ambiente não estiverem definidas por algum motivo, certifique-se de que executa este comando novamente para definir as variáveis novamente.

    2. Crie um cluster do GKE padrão com a escala automática de clusters e a Federação do Workload Identity para o GKE ativadas:

      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
      

      Instale o KEDA

      O KEDA é um componente que complementa o redimensionador automático de pods horizontal do Kubernetes. Com o KEDA, pode dimensionar uma implementação para zero pods e de zero pods para um pod. Uma implementação é um objeto da API Kubernetes que lhe permite executar várias réplicas de pods distribuídas entre os nós num cluster. O algoritmo de escala automática horizontal de pods padrão é aplicado depois de o GKE criar, pelo menos, um pod.

      Depois de o GKE dimensionar a implementação para zero pods, uma vez que não existem pods em execução, o dimensionamento automático não pode basear-se em métricas de pods, como a utilização da CPU. Como consequência, o KEDA permite obter métricas originárias de fora do cluster através de uma implementação da API de métricas externas do Kubernetes. Pode usar esta API para ajustar automaticamente a escala com base em métricas como o número de mensagens pendentes numa subscrição do Pub/Sub. Consulte a documentação do KEDA para ver uma lista de todas as origens de métricas suportadas.

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

      Leme

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

      Tenha em atenção que este comando também configura regras de autorização que exigem que o cluster seja configurado com a Workload Identity Federation para o GKE.

      kubectl

      Execute os seguintes comandos para instalar o KEDA através do 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
      

      Tenha em atenção que este comando também configura regras de autorização que exigem que o cluster seja configurado com a Workload Identity Federation para o GKE.

      Confirme se todos os recursos do KEDA aparecem no espaço de nomes keda:

      kubectl get all -n keda
      

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

      Dimensione a sua carga de trabalho do Pub/Sub para zero

      Esta secção descreve uma carga de trabalho que processa mensagens de uma subscrição do Pub/Sub, processando cada mensagem e confirmando a respetiva conclusão. A carga de trabalho é dimensionada dinamicamente: à medida que o número de mensagens não reconhecidas aumenta, o dimensionamento automático instancia mais pods para garantir o processamento atempado.

      A redução para zero garante que não são instanciados pods quando não são recebidas mensagens durante algum tempo. Isto poupa recursos, uma vez que nenhum Pod permanece inativo durante longos períodos.

      Implemente uma carga de trabalho do Pub/Sub

      Implemente uma carga de trabalho de exemplo que processe mensagens colocadas em fila num tópico do Pub/Sub. Para simular uma carga de trabalho realista, este programa de exemplo aguarda três segundos antes de acusar a receção de uma mensagem. A carga de trabalho está configurada para ser executada na conta de serviço keda-pubsub-sa.

      Execute os seguintes comandos para criar o tópico e a subscrição do Pub/Sub, configurar a respetiva autorização e criar a implementação que inicia a carga de trabalho no espaço de nomes 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
      

      Configure a escalabilidade para zero

      Para configurar a sua carga de trabalho do Pub/Sub para ser dimensionada para zero, use o KEDA para definir um recurso ScaledObject para especificar como a implementação deve ser dimensionada. Em seguida, o KEDA cria e gere automaticamente o objeto HorizontalPodAutoscaler (HPA) subjacente.

      1. Crie o recurso ScaledObject para descrever o comportamento de dimensionamento automático 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 -
        

        Isto 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
        

        Pode ler mais acerca da escala automática na documentação do Kubernetes.

      3. Aguarde até que o KEDA confirme que a subscrição do Pub/Sub está vazia e ajuste a implementação para zero réplicas.

        Inspecione o redimensionador automático da 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 Horizontal Pod Autoscaler reconhece que o KEDA reduziu a implementação para zero, momento em que deixa de funcionar até que a implementação volte a aumentar 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
        

      Acione o aumento da escala

      Para estimular a implementação de forma a aumentar a escala:

      1. Coloque mensagens em fila 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 implementação está a aumentar a escala:

        kubectl get deployments -n keda-pubsub
        

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

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

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

      Dimensione a sua carga de trabalho de MDG para zero

      Esta secção descreve uma carga de trabalho de um grande modelo de linguagem (GML) que implementa um servidor Ollama com uma GPU anexada. O Ollama permite executar LLMs populares, como o Gemma e o Llama 2, e expõe as respetivas funcionalidades principalmente através de HTTP.

      Instale o suplemento KEDA-HTTP

      Reduzir um serviço HTTP para zero pods durante períodos de inatividade provoca falhas de pedidos, uma vez que não existe um back-end para processar os pedidos.

      Esta secção mostra como resolver este problema através do suplemento KEDA-HTTP. O KEDA-HTTP inicia um proxy HTTP que recebe pedidos do utilizador e encaminha-os para os serviços configurados para a redução a zero. Quando o serviço não tem nenhum agrupamento, o proxy aciona o serviço para aumentar a escala e armazena em buffer o pedido até que o serviço tenha aumentado a escala para, pelo menos, um agrupamento.

      Instale o suplemento KEDA-HTTP com 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
      

      Implemente uma carga de trabalho de GML Ollama

      Para implementar uma carga de trabalho de MDI do Ollama:

      1. Crie um node pool com g2-standard-4 nós com GPUs anexadas e configure o dimensionamento 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 do gráfico 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. Implemente o servidor Ollama através do 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 MDIs a carregar, os requisitos de GPU e a porta TCP para o servidor Ollama.

      Configure a escalabilidade para zero

      Para configurar a sua carga de trabalho do Ollama para ser reduzida a zero, o KEDA-HTTP usa um HTTPScaledObject.

      1. Crie o recurso HTTPScaledObject para descrever o comportamento de dimensionamento automático esperado:

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

        Isto cria o objeto HTTPScaledObject que define os seguintes campos:

        • scaleTargetRef: especifica o serviço para o qual o KEDA-HTTP deve encaminhar os pedidos. Neste exemplo, todos os pedidos com o anfitrião ollama.ollama são encaminhados para o servidor Ollama.
        • scaledownPeriod: especifica (em segundos) a rapidez com que a capacidade é reduzida quando não são recebidos pedidos.
        • replicas: especifica o número mínimo e máximo de pods a manter para a implementação do Ollama.
        • scalingMetric: especifica as métricas usadas para acionar a escala automática, como a taxa de pedidos 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 com êxito o HTTPScaledObject criado no passo 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 implementação é reduzida para zero pods.

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

        kubectl get deployments -n ollama
        

        O resultado mostra que o KEDA reduziu a implementação do Ollama e que não existem pods em execução:

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

      Acione o aumento da escala

      Para estimular a implementação a aumentar a escala, chame o serviço Ollama através do proxy configurado pelo suplemento KEDA-HTTP. Isto faz com que o valor da métrica taxa de pedidos aumente e aciona a criação de um primeiro conjunto de anúncios.

      Use as capacidades de encaminhamento de portas do kubectl para aceder ao proxy porque o proxy não está 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 Gemma. Observe os tokens de resposta devolvidos na resposta. Para ver a especificação da API, consulte o guia do Ollama.

      Limpar

      Para evitar incorrer em custos na sua conta do Google Cloud pelos recursos usados neste tutorial, elimine o projeto que contém os recursos ou mantenha o projeto e elimine os recursos individuais.

      1. Limpe a subscrição e o tópico do Pub/Sub:

        gcloud pubsub subscriptions delete keda-echo-read
        gcloud pubsub topics delete keda-echo
        
      2. Elimine o cluster do GKE:

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

      O que se segue?