Temporal no Cloud Run: arquitetura e critérios para workers sob demanda
Uma plataforma de integração pode passar horas aguardando arquivos de parceiros e, depois, receber milhares de operações para validar e persistir. Manter capacidade máxima continuamente parece desperdício; desligar todos os consumidores, porém, exige responder a uma pergunta: quem os iniciará quando houver trabalho?
No Temporal, workers tradicionais buscam tarefas por long polling. Eles não recebem automaticamente uma chamada HTTP para cada tarefa. Portanto, executar o container no Cloud Run não basta para obter escalabilidade sob demanda: é necessário escolher o modelo de execução e o mecanismo de ativação. (docs.temporal.io)
A recomendação central deste artigo é separar durabilidade, processamento e controle de capacidade. Avalie scale-to-zero somente depois de demonstrar como a capacidade volta, inclusive após timers, retries e falhas.
1. O que permanece quando um worker desaparece
Um Workflow representa a orquestração. Seu histórico de eventos permite reconstruir o estado por replay, executando código determinístico. Chamadas externas pertencem normalmente às Activities, não ao código determinístico do Workflow. A vida de uma execução pode ultrapassar a de qualquer container. (docs.temporal.io)
Os componentes relevantes são:
- Temporal Service: mantém o estado durável e coordena a execução; pode ser Temporal Cloud ou uma instalação administrada pela equipe.
- Worker: processo da aplicação, construído com um Software Development Kit, ou SDK, que executa código registrado.
- Task Queue: mecanismo de distribuição de tarefas, consumidas pelos workers.
- Namespace: escopo lógico de organização das execuções.
Hospedar workers não significa hospedar o Temporal Service. Este artigo pressupõe que o serviço durável já existe fora dos containers de processamento. (github.com)
Activities podem ter novas tentativas. Por isso, efeitos externos devem ser idempotentes: repetir uma operação com a mesma chave de negócio não deve duplicar uma cobrança ou importação. Heartbeats podem transportar checkpoints, mas o código precisa ler esses dados e implementar a retomada; não há restauração automática da memória da Activity. (docs.temporal.io)
2. Services, Jobs e Worker Pools não são equivalentes
Cloud Run Services
Services são uma opção para aplicações HTTP. Para manter polling fora de requisições, utilize cobrança baseada em instância, que mantém CPU alocada durante a vida da instância. Apenas configurar instâncias mínimas não substitui essa escolha. (docs.cloud.google.com)
O backlog do Temporal não é um gatilho nativo do autoscaler HTTP. Um Service em zero precisa de uma requisição para voltar a executar; utilização de CPU não existe enquanto não há instância. Para um worker continuamente disponível nesse modelo, configure um mínimo positivo e trate separadamente a expansão de capacidade. (docs.cloud.google.com)
Também será necessário cumprir o contrato HTTP do Service, escutando na porta configurada. Um endpoint artificial não transforma a fila Temporal em tráfego HTTP nem resolve o ciclo de ativação. (docs.cloud.google.com)
Cloud Run Jobs
Jobs são adequados para execução finita. Cada task possui timeout; a documentação permite até 168 horas para tasks sem GPU, mas isso não equivale a um processo permanente. (docs.cloud.google.com)
Minha recomendação é usar Jobs apenas quando houver um lote ou orçamento de execução explícito. Um worker com polling infinito não sabe que terminou o lote. Defina quando parar de buscar tarefas, como concluir ou interromper trabalho e quem executará o próximo Job. Não confunda uma task do Cloud Run com uma Task do Temporal.
Cloud Run Worker Pools
Worker Pools são a opção mais direta para processos de consumo contínuo. O número de instâncias pode ser configurado manualmente, inclusive zero, ou ajustado por um controlador externo. Criar um pool não instala, por si só, um autoscaler para filas Temporal. Instâncias solicitadas continuam sendo cobradas mesmo sem trabalho. (docs.cloud.google.com)
Integração Temporal Serverless Workers
Na documentação consultada para este artigo, o suporte Temporal a Cloud Run está em Public Preview. Um Worker Controller Instance, ou WCI, ajusta Worker Pools pela API administrativa do Cloud Run; não invoca um container por tarefa. A integração exige Worker Versioning. Use um pool por versão e não compartilhe suas filas com workers permanentes externos ao modelo de controle. (docs.temporal.io)
Para adotá-la, valide disponibilidade, versões compatíveis, permissões e suporte contratual. O procedimento oficial inclui configurar identidades, criar o pool, associar uma Worker Deployment Version e promover a versão apropriada. Não basta acrescentar uma variável de ambiente ao worker. (docs.temporal.io)
3. Arquitetura proposta
Para uma plataforma corporativa de importação, proponho separar uma camada de orquestração responsiva de uma camada elástica de Activities:
Cliente → API → Temporal Service
↓
Fila de Workflows
↑ polling
Workers de orquestração
↓ agenda Activities
Fila de importação
↑ polling
Worker Pool de Activities
↓
Object Storage + PostgreSQL
Controlador → API administrativa do Cloud Run → capacidade do poolEssa separação utiliza a capacidade de direcionar Activities para uma Task Queue diferente da utilizada pelo Workflow. Workers conectam-se ao Temporal; não precisam publicar um endpoint de entrada para receber tarefas. (docs.temporal.io)
Decisão proposta: mantenha capacidade de orquestração positiva quando consultas e continuidade rápida forem importantes. Permita zero na fila de importação somente se a latência adicional for aceitável. Configure cada fila conforme sua criticidade, em vez de aplicar um único perfil a toda a plataforma.
Existem dois caminhos de controle:
- Integração Temporal: o WCI participa do controle da capacidade e pode reativar workers quando há tarefas. Esse é um mecanismo do Temporal, não do autoscaler HTTP do Cloud Run. (docs.temporal.io)
- Controlador próprio: um componente externo lê sinais de demanda e altera a quantidade de instâncias. O Cloud Run documenta esse padrão de escalabilidade externa; adaptar os sinais e a reconciliação ao Temporal é responsabilidade da equipe. (docs.cloud.google.com)
No segundo caminho, proponho os seguintes requisitos de projeto:
- executar o controlador fora da capacidade que ele pode reduzir a zero;
- reconciliar periodicamente, não depender apenas de um evento de chegada;
- verificar demanda depois de timers, retries e conclusões de Activities;
- aplicar limites, cooldown e tratamento de indisponibilidade das métricas;
- manter apenas um responsável pelo número desejado de instâncias;
- testar a corrida entre a observação de fila vazia e a chegada de trabalho.
Esses requisitos são critérios de aceitação arquitetural, não recursos automaticamente fornecidos pelo exemplo abaixo.
4. Configuração inicial de um Worker Pool
O exemplo seguinte demonstra capacidade fixa, sem WCI e sem scale-to-zero automático. Pressupõe uma imagem já publicada cujo entrypoint inicia o worker e lê as variáveis indicadas. A imagem deve configurar TLS, autenticação e registro das Activities no SDK; nomes de variáveis, isoladamente, não implementam isso.
Prepare projeto, região, Service Account, repositório e secret. Substitua os valores ilustrativos:
export PROJECT_ID='seu-projeto'
export REGION='us-central1'
export POOL='temporal-import-v1'
export IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/workers/import-worker:1.0.0"
export RUNNER_SA="temporal-worker@${PROJECT_ID}.iam.gserviceaccount.com"
export TEMPORAL_ADDRESS='seu-endpoint-temporal:7233'
export TEMPORAL_NAMESPACE='seu-namespace'
gcloud run worker-pools deploy "$POOL" \
--project="$PROJECT_ID" \
--region="$REGION" \
--image="$IMAGE" \
--instances=2 \
--cpu=1 \
--memory=1Gi \
--service-account="$RUNNER_SA" \
--set-env-vars="TEMPORAL_ADDRESS=${TEMPORAL_ADDRESS},TEMPORAL_NAMESPACE=${TEMPORAL_NAMESPACE},TEMPORAL_TASK_QUEUE=imports" \
--set-secrets='TEMPORAL_API_KEY=temporal-api-key:1'O comando usa opções existentes do Google Cloud CLI. Duas instâncias, uma CPU e 1 GiB são parâmetros didáticos, não dimensionamento universal. Em produção, prefira imagem identificada por digest e ajuste recursos mediante testes. (docs.cloud.google.com)
O secret precisa existir, e a identidade de execução deve poder acessá-lo. A versão numérica torna o rollout de credenciais explícito; faça rotação com implantação controlada, sem incluir o valor da chave no repositório. (docs.cloud.google.com)
Não reduza esse pool a zero antes de implementar e testar o mecanismo de retorno. Para migrar ao WCI, siga o procedimento de integração e retire controladores concorrentes; não trate esse comando isolado como instalação de Serverless Workers. (docs.temporal.io)
5. Critérios para executar sob demanda
Use a tabela como checklist de decisão, não como garantia de desempenho:
| Critério | Aceitar zero quando | Manter capacidade quando |
|---|---|---|
| Latência | Há orçamento para ativação e polling | O início precisa ser previsível |
| Perfil de demanda | Existem intervalos ociosos relevantes | A utilização é contínua |
| Efeitos externos | Operações são idempotentes | Duplicações ainda não são controladas |
| Recuperação | Retry e checkpoint foram testados | Interrupções causam retrabalho excessivo |
| Controle | Há reativação independente e monitorada | Ninguém é responsável por reativar |
| Governança | Preview ou controlador próprio são aceitos | O suporte exigido não está disponível |
Para estimativa inicial, proponho calcular:
instâncias ≈ teto((taxa de chegada × duração média) /
(concorrência por instância × utilização-alvo))É uma aproximação de planejamento, não a fórmula contratual do produto. Exemplo hipotético: 10 Activities/s, duração média de 2 s, 5 execuções simultâneas por instância e utilização-alvo de 70% sugerem 6 instâncias. Valide com distribuição de durações, retries e limites do banco.
Slots de execução, pollers e instâncias são dimensões diferentes. Aumentar pollers não cria capacidade de CPU nem resolve saturação de dependências. Meça a capacidade útil por worker e configure concorrência no SDK. (docs.temporal.io)
6. Falhas, timeouts e encerramento
Separe os limites das Activities:
- Start-to-Close: duração máxima de uma tentativa.
- Schedule-to-Close: orçamento total, incluindo espera e novas tentativas.
- Heartbeat Timeout: detecção de ausência de progresso informado.
- Schedule-to-Start: tempo aguardando um worker; não é um substituto para corrigir capacidade insuficiente.
Inclua o tempo de ativação no orçamento de espera. Timeouts agressivos podem transformar cold starts normais em falhas. (docs.temporal.io)
O contrato documentado de Worker Pools prevê SIGTERM e uma janela de 10 segundos antes de SIGKILL. Uma configuração de graceful shutdown maior no SDK não amplia essa janela. (docs.cloud.google.com)
Ao receber encerramento, inicie o shutdown do worker para interromper polling e tratar tarefas em andamento conforme o SDK. Valide esse comportamento com sinais reais, não apenas encerrando a aplicação em ambiente local. (docs.temporal.io)
No desenho proposto, Activities longas devem persistir resultados parciais e tolerar repetição. Heartbeat é um mecanismo de progresso, não uma transação distribuída com PostgreSQL. Minha recomendação é combinar chave idempotente, restrições de unicidade e checkpoints com a semântica do destino. (docs.temporal.io)
7. Exemplo do dia a dia
Problema: uma equipe importa catálogos de parceiros. Há longos intervalos sem arquivos e picos concentrados. O objetivo é reduzir capacidade ociosa sem perder rastreabilidade.
Solução proposta: a API inicia um Workflow por importação. O Workflow agenda Activities em uma fila exclusiva. Workers elásticos processam partes do arquivo e persistem resultados com uma chave composta por importação e item.
Implementação proposta: mantenha arquivos fora do histórico, transportando referências e identificadores; limite conexões ao PostgreSQL; registre checkpoints; reserve uma janela de tolerância para ativação. Em homologação, interrompa containers após uma escrita e antes da confirmação da Activity.
Resultado esperado, não medido: retomada sem duplicação de efeitos, rastreabilidade da execução e menor tempo de capacidade ociosa. Só apresente economia ou melhoria de latência após medições comparáveis.
8. Kubernetes como alternativa de operação
Se a equipe precisa controlar drenagem, distribuição por zonas e políticas de execução com maior precisão, um Deployment Kubernetes é uma alternativa. O manifesto abaixo é uma base ilustrativa completa para os objetos mostrados; não instala Temporal nem cria credenciais. (kubernetes.io)
Pré-requisitos da imagem: worker configurado por ambiente, TLS habilitado e servidor de saúde em 8080. Os caminhos de saúde abaixo são um contrato da aplicação, não endpoints nativos do Temporal. O Secret temporal-credentials, com a chave api-key, deve ser provisionado previamente.
apiVersion: v1
kind: Namespace
metadata:
name: temporal-workers
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: import-worker
namespace: temporal-workers
automountServiceAccountToken: false
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: import-worker
namespace: temporal-workers
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: import-worker
template:
metadata:
labels:
app: import-worker
spec:
serviceAccountName: import-worker
terminationGracePeriodSeconds: 60
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: import-worker
containers:
- name: worker
image: us-central1-docker.pkg.dev/PROJECT_ID/workers/import-worker:1.0.0
ports:
- name: health
containerPort: 8080
env:
- name: TEMPORAL_ADDRESS
value: "REPLACE_WITH_TEMPORAL_ENDPOINT:7233"
- name: TEMPORAL_NAMESPACE
value: "REPLACE_WITH_NAMESPACE"
- name: TEMPORAL_TASK_QUEUE
value: "imports"
- name: TEMPORAL_API_KEY
valueFrom:
secretKeyRef:
name: temporal-credentials
key: api-key
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
startupProbe:
httpGet:
path: /health/startup
port: health
periodSeconds: 5
failureThreshold: 24
readinessProbe:
httpGet:
path: /health/ready
port: health
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: health
periodSeconds: 10
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 256Mi
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: import-worker
namespace: temporal-workers
spec:
minAvailable: 2
selector:
matchLabels:
app: import-workerAdapte imagem, endpoints, secret e recursos. requests influenciam agendamento; limites restringem consumo. CPU insuficiente pode aumentar latência, e memória subdimensionada pode provocar encerramentos. (kubernetes.io)
A distribuição por zonas depende dos nós elegíveis. DoNotSchedule pode deixar Pods pendentes se não houver capacidade compatível. O PodDisruptionBudget reduz interrupções voluntárias simultâneas, mas não impede falhas de zona. (kubernetes.io)
A probe de liveness deve avaliar saúde local, evitando reinícios em cascata quando o Temporal estiver temporariamente indisponível. Readiness não interrompe polling automaticamente: esse controle precisa existir na aplicação. Não é necessário criar um Service Kubernetes para consumir a fila. (kubernetes.io)
9. Segurança, observabilidade e manutenção
Segurança
Separe a identidade que executa Activities daquela que modifica capacidade. Na integração, a identidade de controle precisa de permissões como run.workerPools.get e run.workerPools.update; a identidade de execução precisa acessar seus secrets. Não entregue administração do projeto ao worker. (docs.temporal.io)
Configure Transport Layer Security, ou TLS, e autenticação suportada pelo endpoint Temporal. Reutilize conexões do client em vez de conectar a cada tarefa. (docs.temporal.io)
Como baseline de segurança, recomendo acesso mínimo a bancos e armazenamento, rotação ensaiada, isolamento de ambientes e ausência de credenciais nos logs. No Kubernetes, revise controle de acesso baseado em funções, ou RBAC, e políticas de rede segundo as dependências reais.
Observabilidade
Monitore latência schedule-to-start, backlog, capacidade de slots, falhas e duração das Activities. CPU baixa não demonstra saúde quando tarefas estão esperando. Correlacione métricas do worker com capacidade disponível e comportamento da fila. (docs.temporal.io)
Como painel operacional proposto, inclua:
- tempo entre demanda e primeira tarefa processada;
- instâncias desejadas versus observadas;
- erros do controlador e idade de sua última reconciliação;
- retries e duplicações evitadas por idempotência;
- conexões de banco e saturação de dependências;
- custo por importação concluída.
Defina um Service Level Objective, ou SLO, de início e conclusão do trabalho. Teste alertas também com zero workers: métricas produzidas apenas por processos vivos não bastam para verificar o mecanismo de ativação.
Upgrades e rollback
Faça replay de históricos representativos antes de alterar Workflows. Worker Versioning permite direcionar execuções por versão; rollback precisa respeitar compatibilidade, não apenas restaurar uma imagem. (docs.temporal.io)
Na integração Cloud Run, preserve pools de versões antigas enquanto houver execuções vinculadas. Não substitua silenciosamente a imagem de um pool associado a uma versão ativa. (docs.temporal.io)
No pipeline de integração e entrega contínuas, ou CI/CD, proponho testes de replay, interrupção, rotação, cold start e recuperação. Para Temporal autogerenciado, exija plano de backup e restauração da persistência; reiniciar workers não substitui recuperar o serviço durável.
10. Troubleshooting
| Sintoma | Possível causa | Investigação | Correção |
|---|---|---|---|
| Pool não cresce | IAM ou associação incorreta | Inspecionar pool e versão | Corrigir permissões e configuração |
| Instâncias sem progresso | Endpoint, secret ou namespace incorreto | Logs de inicialização | Corrigir conexão e autenticação |
| WCI sem fila associada | Worker nunca fez polling na versão | Inspecionar deployment | Alinhar build e registro |
| Activities interrompidas no scale-in | Instância removida durante trabalho | Correlacionar falhas e escala | Checkpoints, retries e estabilização |
Essas verificações seguem o fluxo de diagnóstico da integração. (docs.temporal.io)
gcloud run worker-pools describe "$POOL" \
--project="$PROJECT_ID" --region="$REGION" --format=yaml
kubectl get pods -n temporal-workers
kubectl describe deployment import-worker -n temporal-workers
kubectl logs deployment/import-worker -n temporal-workers
kubectl get events -n temporal-workers --sort-by=.metadata.creationTimestampPara um Pod que reiniciou, use kubectl logs NOME_DO_POD -n temporal-workers --previous. Não confunda rollout bem-sucedido com capacidade efetiva de processar tarefas.
11. Custos, desenvolvimento e alternativas
Inclua no modelo financeiro tempo de instâncias, Temporal, rede, armazenamento, logs e controle de escala. Cobrança baseada em instância considera sua vida útil; scale-to-zero não elimina custos dos demais componentes. Não use preços hipotéticos como orçamento. (cloud.google.com)
Minha recomendação de FinOps é comparar três experimentos: capacidade fixa pequena, capacidade mínima com expansão e zero com reativação. Meça custo por execução concluída, incluindo retries e trabalho repetido.
Em desenvolvimento, uma instância e observabilidade simplificada podem ser suficientes para aprender. Para produção, proponho gates adicionais: limites de capacidade, health checks, alertas, credenciais segregadas, restauração testada e rollback documentado. Produção não exige zero; exige comportamento previsível.
| Alternativa | Avaliação arquitetural proposta |
|---|---|
| Worker Pool fixo | Menos lógica própria; aceita custo ocioso |
| Worker Pool com WCI | Controle integrado; avaliar Preview e versionamento |
| Controlador próprio | Flexibilidade; maior responsabilidade operacional |
| Kubernetes | Maior controle; exige maturidade de plataforma |
| Job finito | Bom para lotes com término explícito |
| Serviço HTTP simples | Considere se a aplicação não precisa de orquestração durável |
Evite scale-to-zero quando o custo de ativação comprometer o SLO, a carga for contínua ou a recuperação ainda não estiver validada. Evite também polling em CPU condicionada a requisições, secrets hardcoded, limites arbitrários e autoscalers disputando o mesmo recurso.
Resumo prático
- Separe Temporal Service, workers e controlador de capacidade.
- Escolha Worker Pools para consumo contínuo; não presuma que um Service HTTP observa filas Temporal.
- Exija uma estratégia verificável de retorno do zero.
- Trate interrupções, idempotência e checkpoints antes de otimizar custos.
- Faça a decisão por fila, SLO e maturidade operacional.
Conclusão
Temporal no Cloud Run deve ser tratado como uma decisão de arquitetura de execução, não apenas de hospedagem de containers. A proposta de Platform Engineering é oferecer perfis padronizados — permanente, elástico e finito — com critérios explícitos de adoção. DevOps automatiza a entrega; SRE valida recuperação e objetivos de serviço; FinOps confirma se a elasticidade realmente melhora o custo por resultado. O melhor desenho é aquele cuja ativação, interrupção e recuperação a equipe consegue demonstrar.
Artigos relacionados
Quer aplicar isso no seu ambiente?
Agende uma conversa de 30 minutos sem compromisso. Eu olho o seu cenário e digo por onde começar.
Agendar uma conversaReceba os novos artigos por e-mail
Um e-mail quando sair artigo novo sobre infraestrutura, backend e IA. Sem spam, cancele quando quiser.