Fazer o escalonamento automático de pools de workers do Cloud Run com base no volume da fila do Pub/Sub usando o CREMA

1. Introdução

Visão geral

Neste tutorial, mostramos como implantar um pool de workers do Cloud Run (consumidor) para processar mensagens do Pub/Sub e escalonar automaticamente as instâncias do consumidor com base na profundidade da fila usando o escalonamento automático de métricas externas do Cloud Run (CREMA).

O que você vai aprender

Neste codelab, você vai:

  • Crie um tópico e uma assinatura do Pub/Sub e envie mensagens para ele.
  • Implante um pool de workers do Cloud Run (consumidor) que consome mensagens do Pub/Sub.
  • Implante o projeto CREMA no GitHub como um serviço do Cloud Run para escalonar automaticamente seu pool de workers com base no número de mensagens na assinatura do Pub/Sub.
  • Teste a configuração de escalonamento automático gerando carga ao executar um script Python localmente.

2. Configurar variáveis de ambiente

Como muitas variáveis de ambiente são usadas ao longo deste codelab, recomendamos executar

set -u

que vai avisar se você tentar usar uma variável de ambiente que ainda não foi definida. Para desfazer essa configuração, execute set +u

Primeiro, mude a variável a seguir para o ID do projeto.

export PROJECT_ID=<YOUR_PROJECT_ID>

e defina como o projeto deste codelab.

gcloud config set project $PROJECT_ID

Em seguida, defina as variáveis de ambiente usadas neste codelab.

export REGION=us-central1
export TOPIC_ID=crema-pubsub-topic
export SUBSCRIPTION_ID=crema-pubsub-sub
export CREMA_SA_NAME=crema-service-account
export CONSUMER_SA_NAME=consumer-service-account
export CONSUMER_WORKER_POOL_NAME=worker-pool-consumer
export CREMA_SERVICE_NAME=my-crema-service

Crie um diretório para este codelab

mkdir crema-pubsub-codelab
cd crema-pubsub-codelab

Ativar APIs

gcloud services enable \
        artifactregistry.googleapis.com \
        cloudbuild.googleapis.com \
        run.googleapis.com \
        parametermanager.googleapis.com

Por fim, verifique se a gcloud está usando a versão mais recente.

gcloud components update

3. Configuração do Pub/Sub

Crie o tópico e a assinatura de pull que seu pool de workers vai processar. Bash

Crie o tópico.

gcloud pubsub topics create $TOPIC_ID

Crie a assinatura.

gcloud pubsub subscriptions create $SUBSCRIPTION_ID --topic=$TOPIC_ID

4. IAM e contas de serviço

Recomendamos criar uma conta de serviço para cada recurso do Cloud Run. Neste codelab, você vai criar o seguinte:

  • SA do consumidor: identidade do pool de workers que processa mensagens do Pub/Sub.
  • CREMA SA: identidade do serviço de escalonamento automático da CREMA.

Criar contas de serviço

Crie a SA do consumidor do pool de workers:

gcloud iam service-accounts create $CONSUMER_SA_NAME \
  --display-name="PubSub Consumer Service Account"

Crie a conta de serviço do CREMA para o pool de workers:

gcloud iam service-accounts create $CREMA_SA_NAME \
  --display-name="CREMA Autoscaler Service Account"

Conceder permissões à Consumer SA

Conceda permissões à SA do consumidor do pool de workers para extrair mensagens da assinatura.

gcloud pubsub subscriptions add-iam-policy-binding $SUBSCRIPTION_ID \
  --member="serviceAccount:$CONSUMER_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/pubsub.subscriber"

Conceder permissões à CREMA SA

O CREMA precisa de permissões para ler parâmetros, escalonar o pool de workers e monitorar métricas do Pub/Sub.

  1. Acessar o Gerenciador de parâmetros (leitor de configuração):
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/parametermanager.parameterViewer"
  1. Escalonar o pool de workers (desenvolvedor do Cloud Run):
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/run.developer"
  1. Monitorar o Pub/Sub:

Conceda o papel de leitor do Monitoring.

gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/monitoring.viewer"

Adicione uma política à assinatura da SA do serviço CREMA para visualizá-la.

gcloud pubsub subscriptions add-iam-policy-binding $SUBSCRIPTION_ID \
  --member="serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/pubsub.viewer"

A SA do CREMA também precisa de um usuário da conta de serviço, que é necessário para mudar as contagens de instâncias:

gcloud iam service-accounts add-iam-policy-binding \
    $CONSUMER_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com \
    --member="serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/iam.serviceAccountUser"

5. Verificar permissões de SA

Antes de continuar o codelab, verifique se a SA do serviço CREMA tem as funções corretas no nível do projeto.

gcloud projects get-iam-policy $PROJECT_ID \
  --flatten="bindings[].members" \
  --format="table(bindings.role)" \
  --filter="bindings.members:serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com"

Isso vai resultar no seguinte:

roles/monitoring.viewer
roles/parametermanager.parameterViewer
roles/run.developer

Verifique se a assinatura do Pub/Sub tem uma política que permite que a SA do serviço CREMA a consulte.

gcloud pubsub subscriptions get-iam-policy $SUBSCRIPTION_ID \
  --flatten="bindings[].members" \
  --filter="bindings.members:serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --format="table(bindings.role)"

Resulta em

roles/pubsub.viewer

e verifique se a SA do CREMA tem o papel de usuário da conta de serviço.

gcloud iam service-accounts get-iam-policy \
  $CONSUMER_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com \
  --flatten="bindings[].members" \
  --filter="bindings.members:serviceAccount:$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com"

Isso vai resultar no seguinte:

bindings:
  members: serviceAccount:crema-service-account@<PROJECT_ID>.iam.gserviceaccount.com
  role: roles/iam.serviceAccountUser

e a SA do consumidor do pool de workers tem o papel de assinante do Pub/Sub.

gcloud pubsub subscriptions get-iam-policy $SUBSCRIPTION_ID \
  --flatten="bindings[].members" \
  --filter="bindings.members:serviceAccount:$CONSUMER_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --format="table(bindings.role)"

Resulta em

ROLE
roles/pubsub.subscriber

6. Criar e implantar o pool de workers do consumidor

Crie um diretório para seu código de consumidor e acesse-o.

mkdir consumer
cd consumer
  1. Crie um arquivo consumer.py
import os
import time
from google.cloud import pubsub_v1
from concurrent.futures import TimeoutError

# Configuration
PROJECT_ID = os.environ.get('PROJECT_ID')
SUBSCRIPTION_ID = os.environ.get('SUBSCRIPTION_ID')

subscription_path = f"projects/{PROJECT_ID}/subscriptions/{SUBSCRIPTION_ID}"

print(f"Worker Pool instance starting. Watching {subscription_path}...")

subscriber = pubsub_v1.SubscriberClient()

def callback(message):
    try:
        data = message.data.decode("utf-8")
        print(f"Processing job: {data}")
        time.sleep(5)  # Simulate work
        print(f"Done {data}")
        message.ack()
    except Exception as e:
        print(f"Error processing message: {e}")
        message.nack()

streaming_pull_future = subscriber.subscribe(subscription_path, callback=callback)
print(f"Listening for messages on {subscription_path}...")

# Wrap subscriber in a 'with' block to automatically call close() when done.
with subscriber:
    try:
        # When `timeout` is not set, result() will block indefinitely,
        # unless an exception is encountered first.
        streaming_pull_future.result()
    except TimeoutError:
        streaming_pull_future.cancel()  # Trigger the shutdown.
        streaming_pull_future.result()  # Block until the shutdown is complete.
    except Exception as e:
        print(f"Streaming pull failed: {e}")
  1. Criar um Dockerfile
FROM python:3.12-slim
RUN pip install google-cloud-pubsub
COPY consumer.py .
CMD ["python", "-u", "consumer.py"]
  1. Implantar pool de workers do consumidor

Este codelab recomenda implantar o pool de workers com zero instâncias para começar. Assim, você pode observar o CREMA escalonar o pool de workers quando ele detectar as mensagens do Pub/Sub na assinatura.

gcloud beta run worker-pools deploy $CONSUMER_WORKER_POOL_NAME \
  --source . \
  --region $REGION \
  --service-account="$CONSUMER_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
  --instances=0 \
  --set-env-vars PROJECT_ID=$PROJECT_ID,SUBSCRIPTION_ID=$SUBSCRIPTION_ID

7. Configurar o CREMA

  1. Volte para o diretório raiz do projeto.
cd ..
  1. Crie o arquivo de configuração crema-config.yaml.
apiVersion: crema/v1
kind: CremaConfig
spec:
  pollingInterval: 30
  triggerAuthentications:
    - metadata:
        name: adc-trigger-auth
      spec:
        podIdentity:
          provider: gcp
  scaledObjects:
    - spec:
        scaleTargetRef:
          name: projects/PROJECT_ID_PLACEHOLDER/locations/REGION_PLACEHOLDER/workerpools/CONSUMER_WORKER_POOL_NAME_PLACEHOLDER
        triggers:
          - type: gcp-pubsub
            metadata:
              subscriptionName: "SUBSCRIPTION_ID_PLACEHOLDER"
              # Target number of undelivered messages per worker instance
              value: "10"
              mode: "SubscriptionSize"
            authenticationRef:
              name: adc-trigger-auth
  1. Substituir variáveis
sed -i "s/PROJECT_ID_PLACEHOLDER/$PROJECT_ID/g" crema-config.yaml
sed -i "s/REGION_PLACEHOLDER/$REGION/g" crema-config.yaml
sed -i "s/CONSUMER_WORKER_POOL_NAME_PLACEHOLDER/$CONSUMER_WORKER_POOL_NAME/g" crema-config.yaml
sed -i "s/SUBSCRIPTION_ID_PLACEHOLDER/$SUBSCRIPTION_ID/g" crema-config.yaml
  1. Verifique se o crema-config.yaml está correto
if grep -q "_PLACEHOLDER" crema-config.yaml; then
  echo "❌ ERROR: Validations failed. '_PLACEHOLDER' was found in crema-config.yaml."
  echo "Please check your environment variables and run the 'sed' commands again."
else
  echo "✅ Config check passed: No placeholders found."
fi
  1. Fazer upload para o Gerenciador de parâmetros

Definir outras variáveis de ambiente para o Gerenciador de parâmetros

export PARAMETER_ID=crema-config
export PARAMETER_REGION=global
export PARAMETER_VERSION=1

Criar o recurso Parameter

gcloud parametermanager parameters create $PARAMETER_ID \
  --location=$PARAMETER_REGION \
  --parameter-format=YAML

Criar parâmetro versão 1

gcloud parametermanager parameters versions create $PARAMETER_VERSION \
  --parameter=crema-config \
  --project=$PROJECT_ID \
  --location=$PARAMETER_REGION \
  --payload-data-from-file=crema-config.yaml

Verifique se o parâmetro foi adicionado

gcloud parametermanager parameters versions list \
  --parameter=$PARAMETER_ID \
  --location=$PARAMETER_REGION

Você vai ver algo como

projects/<YOUR_PROJECT_ID>/locations/global/parameters/crema-config/versions/1

8. Implantar o serviço CREMA

Nesta seção, você vai implantar o serviço de escalonador automático do CREMA. Você vai usar a imagem disponível publicamente.

  1. Definir as variáveis de ambiente necessárias para o CREMA
CREMA_CONFIG_PARAM_VERSION=projects/$PROJECT_ID/locations/$PARAMETER_REGION/parameters/$PARAMETER_ID/versions/$PARAMETER_VERSION
  1. Verificar o caminho do nome da versão
echo $CREMA_CONFIG_PARAM_VERSION

Ele vai ficar assim:

projects/<YOUR_PROJECT>/locations/global/parameters/crema-config/versions/1
  1. Defina a variável de ambiente para a imagem do CREMA
IMAGE=us-central1-docker.pkg.dev/cloud-run-oss-images/crema-v1/autoscaler:1.0
  1. e implante o serviço CREMA

A imagem de base é obrigatória.

gcloud beta run deploy $CREMA_SERVICE_NAME \
  --image=$IMAGE \
  --region=${REGION} \
  --service-account="${CREMA_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
  --no-allow-unauthenticated \
  --no-cpu-throttling \
  --labels=created-by=crema \
  --base-image=us-central1-docker.pkg.dev/serverless-runtimes/google-24/runtimes/java25 \
  --set-env-vars="CREMA_CONFIG=${CREMA_CONFIG_PARAM_VERSION},OUTPUT_SCALER_METRICS=True,ENABLE_CLOUD_LOGGING=True"

9. Teste de carga

  1. Criar um script que publique mensagens no tópico do Pub/Sub
touch load-pubsub.sh
  1. Adicione o seguinte código ao arquivo load-pubsub.sh:
#!/bin/bash
TOPIC_ID=${TOPIC_ID} 
PROJECT_ID=${PROJECT_ID}
NUM_MESSAGES=100

echo "Publishing $NUM_MESSAGES messages to topic $TOPIC_ID..."

for i in $(seq 1 $NUM_MESSAGES); do
  gcloud pubsub topics publish $TOPIC_ID --message="job-$i" --project=$PROJECT_ID &
  if (( $i % 10 == 0 )); then
    wait
    echo "Published $i messages..."
  fi
done
wait
echo "Done. All messages published."
  1. Executar teste de carga
chmod +x load-pubsub.sh
./load-pubsub.sh
  1. Monitore a espera de escalonamento por 3 a 4 minutos. Confira os registros do CREMA para ver as recomendações de instâncias com base na nova configuração "authenticationRef".
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=$CREMA_SERVICE_NAME AND textPayload:SCALER" \
  --limit=20 \
  --format="value(textPayload)" \
  --freshness=5m
  1. Monitore o processamento e veja os registros do consumidor para conferir se ele está sendo iniciado.
gcloud beta run worker-pools logs tail $CONSUMER_WORKER_POOL_NAME --region=$REGION

Você vai ver registros como

Done job-100

10. Solução de problemas

Primeiro, determine se o problema está na configuração do serviço CREMA ou na configuração do consumidor do Pub/Sub.

Defina o escalonador automático do consumidor do Pub/Sub como 1, em vez de 0. Se ele começar imediatamente a processar mensagens pubsub, o problema é com o CREMA. Se ele não processar as mensagens do pubsub, há um problema com o consumidor do pubsub.

11. Parabéns!

Parabéns por concluir o codelab!

Recomendamos que você consulte a documentação do Cloud Run.

O que aprendemos

  • Como criar um tópico e uma assinatura do Pub/Sub e enviar mensagens para esse tópico.
  • Como implantar um pool de workers do Cloud Run (consumidor) que consome mensagens do Pub/Sub.
  • Como implantar o projeto CREMA no GitHub como um serviço do Cloud Run para escalonar automaticamente o pool de workers com base no número de mensagens na assinatura do Pub/Sub.
  • Como testar sua configuração de escalonamento automático gerando carga executando um script Python localmente.

12. Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados neste tutorial, exclua os recursos criados neste codelab ou o projeto inteiro.

Excluir recursos usados neste codelab

  1. Excluir o serviço CREMA do Cloud Run
gcloud run services delete $CREMA_SERVICE_NAME --region=$REGION --quiet
  1. Excluir o consumidor do pool de workers do Cloud Run
gcloud beta run worker-pools delete $CONSUMER_WORKER_POOL_NAME --region=$REGION --quiet
  1. Excluir a assinatura e o tópico do Pub/Sub
gcloud pubsub subscriptions delete $SUBSCRIPTION_ID --quiet
gcloud pubsub topics delete $TOPIC_ID --quiet
  1. Excluir a configuração do Parameter Manager

Excluir a versão dentro do parâmetro

gcloud parametermanager parameters versions delete $PARAMETER_VERSION \
  --parameter=$PARAMETER_ID \
  --location=$PARAMETER_REGION \
  --quiet

Agora, exclua o parâmetro vazio.

gcloud parametermanager parameters delete $PARAMETER_ID \
  --location=$PARAMETER_REGION \
  --quiet
  1. Excluir as contas de serviço
gcloud iam service-accounts delete "$CREMA_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" --quiet
gcloud iam service-accounts delete "$CONSUMER_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" --quiet

Ou excluir todo o projeto

Para excluir todo o projeto, acesse Gerenciar recursos, selecione o projeto criado na etapa 2 e escolha "Excluir". Se você excluir o projeto, será necessário mudar de projeto no SDK do Cloud. Para conferir a lista de todos os projetos disponíveis, execute gcloud projects list.