CloudWatch para OTel: Desmontando o Padrão de Bridge de Observabilidade
Ouvir artigo
Voz do FernandoFernando · 16:46
Com tecnologia Amazon Polly + OmniVoice
O padrão de bridge entre CloudWatch e OpenTelemetry resolve um problema real de fragmentação de observabilidade em ambientes multi-plataforma, mas carrega custos operacionais e armadilhas de design que raramente aparecem nos tutoriais. Neste artigo desmonto a anatomia desse padrão, quando ele faz sentido e quando ele cria mais problema do que resolve.
Em ambientes financeiros onde times de plataforma precisam consolidar sinais de dezenas de workloads AWS em um backend de observabilidade unificado — seja Datadog, Grafana Cloud, Honeycomb ou um stack interno — o padrão de bridge CloudWatch → OpenTelemetry aparece como solução óbvia. Mas 'óbvio' e 'correto' raramente coincidem em arquitetura. Esse padrão tem uma anatomia específica, um envelope de validade estreito e modos de falha que só aparecem em produção sob carga real. Vou dissecar cada camada.
O Problema Real: Fragmentação de Observabilidade em Ambientes AWS-Native
Toda organização que cresce além de dois ou três times de engenharia enfrenta a mesma tensão: serviços AWS emitem métricas nativamente para o CloudWatch — Lambda, RDS, EKS, API Gateway, MSK — mas o backend de observabilidade corporativo fala OTLP. O resultado é um mundo partido: SREs precisam abrir dois consoles para correlacionar um incidente, alertas vivem em namespaces diferentes, e dashboards de negócio ficam impossíveis de construir sem ETL manual.
O padrão de bridge existe para resolver exatamente isso. A ideia central é simples: uma função Lambda (ou um coletor OTel rodando em ECS/EKS) subscreve streams de métricas do CloudWatch via CloudWatch Metric Streams ou polling da API GetMetricData, transforma o payload para o formato OTLP (OpenTelemetry Protocol), e o encaminha para um endpoint de coletor. Na teoria, você ganha um único plano de controle de observabilidade. Na prática, a complexidade se esconde nos detalhes.
O que torna esse problema especialmente traiçoeiro em ambientes financeiros é a combinação de três fatores: (1) volume de métricas — uma conta AWS de médio porte com EKS, RDS Multi-AZ e Lambda pode gerar facilmente 50.000+ séries de métricas por minuto; (2) latência de negócio — SLOs de detecção de anomalia exigem freshness de 60 segundos ou menos; (3) custo de API — cada chamada GetMetricData tem custo direto e quota de 50 métricas por request, o que significa que polling ingênuo em escala quebra tanto o orçamento quanto os rate limits da AWS.
Anatomia do Padrão CloudWatch → OTel Bridge
Fluxo completo desde a emissão de métricas pelos serviços AWS até o backend de observabilidade externo, passando pelos dois caminhos de ingestão (Metric Streams e polling) e os guardrails de segurança e custo.
- Lambda · Invocations/Errors/Duration
- EKS / EC2 · CPU, Memory, Network
- RDS / Aurora · DBConnections, Latency
- API Gateway · 4xx/5xx, Latency
- CloudWatch · Namespaces
- CW Metric Streams · ~2-3s latency, Firehose
- Kinesis Firehose · JSON/OTel0.7 format
- Bridge Lambda · OTLP transform + retry
- SQS DLQ · failed batches
- KMS CMK · payload encryption
- OTel Collector · OTLP/gRPC :4317
- Datadog / Grafana · / Honeycomb
Anatomia do Padrão: Dois Caminhos, Um Trade-off Fundamental
O padrão tem dois sabores de ingestão, e a escolha entre eles define tudo que vem depois.
CloudWatch Metric Streams + Kinesis Firehose é o caminho de baixa latência. Streams entregam dados em formato OpenTelemetry 0.7 (protobuf) ou JSON com latência de 2–3 segundos. O custo é previsível: $0.003 por 1.000 atualizações de métrica. Para uma conta com 100k séries ativas, isso é aproximadamente $300/mês antes do custo do Firehose. A vantagem arquitetural crítica é que você não está fazendo polling — o dado flui, e a Lambda no destino do Firehose recebe batches, não chamadas individuais.
Polling via GetMetricData é o caminho de controle granular. Você escolhe exatamente quais métricas, com qual resolução e qual período de lookback. Mas a API tem quota de 50 métricas por request e 500 requests por segundo por conta (soft limit). Em uma conta grande, atingir esse limite é questão de minutos se o código de polling não implementar backoff exponencial com jitter e não batchear corretamente. Vi sistemas financeiros em produção quebrarem alertas críticos porque o poller entrou em throttling às 09:00 de uma segunda-feira — exatamente quando o mercado abre e o volume de transações explode.
A Lambda de transformação precisa de três capacidades não-negociáveis: (1) idempotência — Firehose pode re-entregar batches em caso de falha; a Lambda deve detectar duplicatas via hash do payload; (2) circuit breaker para o endpoint OTel externo — se o coletor estiver down, a Lambda não pode ficar em loop consumindo concorrência; (3) DLQ com alarme — batches que falham após 3 tentativas vão para SQS DLQ e um alarme CloudWatch deve disparar em menos de 5 minutos.
Quando Este Padrão Faz Sentido
Segurança e Guardrails: O que os Tutoriais Não Contam
Em ambientes financeiros, o bridge de observabilidade é um vetor de exfiltração de dados subestimado. Métricas de negócio — volume de transações, latência de pagamentos, taxa de erros de autenticação — são informação sensível. Um endpoint OTel mal configurado pode vazar esses dados para fora da conta AWS sem nenhum alarme.
As três camadas de controle que implemento em todo deployment desse padrão:
IAM com condições de recurso: A role da Lambda deve ter permissão cloudwatch:GetMetricData e cloudwatch:ListMetrics com condição aws:ResourceTag/Environment: production — nunca um wildcard. Para Metric Streams, a role do Firehose precisa de cloudwatch:PutMetricStream e firehose:PutRecord, mas a role da Lambda de destino deve ser separada e ter apenas s3:GetObject no bucket de buffer (se usar S3 como fallback) e permissão de invocação.
KMS CMK para payload em trânsito: O Firehose deve ser configurado com ServerSideEncryption usando uma CMK gerenciada pelo time de segurança. A Lambda de transformação deve descriptografar com a mesma chave. Isso garante que mesmo um acesso não autorizado ao stream do Firehose não expõe dados legíveis.
VPC Endpoint para o coletor OTel: Se o coletor roda em ECS dentro da VPC, a Lambda deve estar na mesma VPC e usar DNS privado. Se o coletor é externo (SaaS), o tráfego deve sair por um NAT Gateway com IP fixo whitelistado no firewall do fornecedor — nunca por Internet Gateway sem controle de saída. Adicione um WAF rule no API Gateway se o coletor externo expõe um endpoint HTTP.
Um detalhe que queimou um cliente meu: a Lambda de bridge rodando com timeout de 15 minutos (máximo) e sem reserva de concorrência pode consumir todo o pool de concorrência da conta em um pico de ingestão, derrubando funções de negócio críticas. Reserve concorrência explícita — tipicamente 10–20 instâncias são suficientes para um Firehose com batch de 5MB.
Anti-Padrões: Quando Este Bridge Vai Explodir em Produção
- Polling ingênuo sem backoff: Chamar
GetMetricDataem loop com intervalo fixo de 60s para centenas de namespaces. Em contas com muitos serviços, você atinge o rate limit em minutos e perde dados de observabilidade exatamente quando mais precisa — durante incidentes. - Lambda sem DLQ e sem alarme de erro: Falhas silenciosas de transformação ou export significam que métricas somem sem nenhum sinal. Em ambientes financeiros, isso pode mascarar degradação de serviço por horas.
- Encaminhar todas as métricas de todos os namespaces: O CloudWatch tem mais de 200 namespaces. Encaminhar tudo para o backend externo multiplica o custo de ingestão do SaaS por 5–10x sem valor proporcional. Filtre por namespace e dimensão no próprio Metric Stream.
- Sem idempotência na Lambda de transformação: Firehose garante entrega at-least-once. Sem deduplicação por hash do payload, você injeta séries duplicadas no backend OTel, corrompendo agregações e alertas baseados em soma/contagem.
- Usar este padrão para traces e logs: O bridge CloudWatch → OTel é projetado para métricas. Tentar encaminhar CloudWatch Logs Insights ou X-Ray traces pelo mesmo canal cria um sistema frágil de propósito múltiplo. Use o OTel Collector diretamente nas aplicações para traces e logs.
- Sem reserva de concorrência na Lambda: Em picos de ingestão (abertura de mercado, batch noturno), a Lambda de bridge pode esgotar o pool de concorrência da conta e throttlear funções de negócio críticas.
Design de Referência: O que Realmente Funciona em Produção Financeira
Depois de implementar e depurar esse padrão em três ambientes financeiros diferentes, o design que funciona tem estas características concretas:
Ingestão via Metric Streams com filtro de namespace: Configure o stream para incluir apenas os namespaces relevantes — AWS/Lambda, AWS/EKS, AWS/RDS, AWS/ApiGateway, AWS/MSK — e exclua explicitamente namespaces de custo alto e baixo valor como AWS/Billing e AWS/CloudFront (a menos que você monitore CDN). O formato deve ser opentelemetry0.7 (protobuf), não JSON, para reduzir o tamanho do payload em ~40%.
Firehose com buffer de 60s/5MB e S3 como fallback: Configure BufferingHints com IntervalInSeconds: 60 e SizeInMBs: 5. O S3 de fallback deve ter lifecycle policy para expirar objetos em 7 dias — ele existe apenas para replay manual em caso de falha do coletor externo, não como storage permanente.
Lambda de transformação em Python/Rust com SDK OTel: Use o opentelemetry-sdk para construir o payload OTLP. Adicione resource attributes fixos no momento da transformação: cloud.account.id, cloud.region, deployment.environment. Esses atributos permitem correlação cross-account no backend. O timeout deve ser 3 minutos (não 15) — se a transformação de um batch leva mais de 3 minutos, há um problema de volume que precisa ser resolvido no filtro do stream, não no timeout.
Observabilidade do próprio bridge: Instrumente a Lambda com métricas customizadas: bridge.metrics.transformed.count, bridge.export.latency.p99, bridge.export.errors.count. Crie um dashboard CloudWatch para o bridge em si — é irônico mas necessário: você precisa observar o sistema de observabilidade. Defina um SLO de bridge.export.errors.count < 0.1% com alarme de 5 minutos de burn rate.
Números Reais de Produção
Filtre no Stream, Não na Lambda
O CloudWatch Metric Stream suporta filtros por namespace e por dimensão. Use-os. Cada métrica que você não encaminha economiza custo de Firehose, custo de Lambda e custo de ingestão no backend externo. A Lambda de transformação deve ser burra e rápida — transformar formato, adicionar resource attributes e exportar. Lógica de filtragem na Lambda é um anti-padrão: você já pagou pelo dado ao chegar no Firehose.
Lentes Well-Architected para Este Padrão
Segurança
IAM com condições de tag de recurso, KMS CMK no Firehose, Lambda em VPC privada, egress controlado via NAT com IP fixo. A role da Lambda nunca deve ter cloudwatch:* — escopo mínimo por namespace.
Confiabilidade
DLQ no destino do Firehose, circuit breaker para o endpoint OTel externo, alarme de burn rate no SLO do bridge. Teste de falha do coletor externo deve ser parte do runbook de DR.
Implementei esse padrão pela primeira vez em 2022 em um ambiente de pagamentos e aprendi da forma difícil que o bridge de observabilidade precisa de observabilidade própria — parece óbvio, mas na prática é sempre o último item da lista. O que mais me preocupa nesse padrão não é a complexidade técnica, mas o falso senso de segurança que ele cria: times assumem que porque as métricas estão 'fluindo', a observabilidade está funcionando. Não está — até você ter SLOs de freshness, alarmes de erro no bridge e um runbook de falha do coletor externo. Em ambientes financeiros, eu sempre exijo que o time demonstre o que acontece quando o endpoint OTel externo fica indisponível por 10 minutos antes de ir para produção. A resposta a essa pergunta revela se o design tem guardrails reais ou apenas boa intenção.
Veredicto: Use com Cirurgia, Não com Entusiasmo
O padrão CloudWatch → OTel Bridge via Lambda e Metric Streams é tecnicamente sólido e resolve um problema real de fragmentação de observabilidade. Mas ele tem um custo operacional não trivial e um envelope de validade estreito. Use-o quando: (a) você tem >10k séries de métricas ativas de serviços AWS gerenciados que precisam estar em um backend OTLP externo; (b) seu SLO de freshness é ≤60 segundos; (c) a equipe de plataforma tem capacidade para operar e observar o próprio bridge. Não use quando: você está tentando consolidar traces e logs pelo mesmo canal; quando o volume não justifica o custo fixo do Metric Stream; ou quando não há maturidade operacional para manter um sistema que observa outros sistemas. O risco mais alto não é técnico — é organizacional: times que deployam esse padrão e depois não o monitoram criam um ponto cego de observabilidade disfarçado de solução de observabilidade.
Referências
Deep dives de arquitetura, AWS, IA e mercado — direto no seu email. Grátis.
Sem spam · cancele quando quiser
Pergunte ao Fernando sobre isto
Receba uma resposta focada sobre este artigo do meu assistente de IA, baseada no meu trabalho.
Participe da conversa
Entre para comentar
Confirme seu e-mail para participar — você também recebe a newsletter. Sem senha.
Continue lendo
Inteligência de arquitetura, na sua caixa de entrada
Sinais curados e análises originais sobre AWS, IA, sistemas distribuídos e mercado — do jeito que um arquiteto de soluções lê.
- Curadoria de AWS · IA · arquitetura · mercado
- Novos estudos de arquitetura e deep-dives quando saem
- Sínteses diretas — profundidade sem ruído
- Sem spam · double opt-in · cancele quando quiser