# CloudWatch para OTel: Desmontando o Padrão de Bridge de Observabilidade

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.

- URL: https://fernando.moretes.com/blog/cloudwatch-para-opentelemetry-com-lambda-e-guardrails

- Markdown: https://fernando.moretes.com/blog/cloudwatch-para-opentelemetry-com-lambda-e-guardrails/article.md?lang=pt

- Published: 2026-05-28T00:00:00.000Z

- Category: Dados & Plataformas

- Tags: observability, opentelemetry, cloudwatch, lambda, otel-collector, financial-grade, platform-engineering, aws

- Reading time: 7 min

- Source: [CloudWatch metrics to OTel collectors](https://aws.amazon.com/blogs/architecture/)

---

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.

### 🟧 AWS — Metric Sources

- Lambda Invocations/Errors/Duration (compute)
- EKS / EC2 CPU, Memory, Network (compute)
- RDS / Aurora DBConnections, Latency (data)
- API Gateway 4xx/5xx, Latency (network)

### 🟦 AWS — Ingestion Layer

- CloudWatch Namespaces (storage)
- CW Metric Streams ~2-3s latency, Firehose (messaging)
- Kinesis Firehose JSON/OTel0.7 format (messaging)

### 🟨 AWS — Transform & Forward

- Bridge Lambda OTLP transform + retry (compute)
- SQS DLQ failed batches (messaging)
- KMS CMK payload encryption (security)

### 🔵 External — Observability Backend

- OTel Collector OTLP/gRPC :4317 (external)
- Datadog / Grafana / Honeycomb (external)

### Fluxos

- lambda-src -> cw-ns: emite métricas
- eks-src -> cw-ns: emite métricas
- rds-src -> cw-ns: emite métricas
- apigw-src -> cw-ns: emite métricas
- cw-ns -> metric-stream: stream contínuo
- metric-stream -> firehose: OpenTelemetry 0.7
- firehose -> bridge-lambda: batch trigger
- bridge-lambda -> kms-key: decrypt/encrypt
- bridge-lambda -> dlq: falha após 3 retries
- bridge-lambda -> otel-collector: OTLP/gRPC export
- otel-collector -> obs-backend: pipeline processado

## 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

- Você tem um backend de observabilidade externo (Datadog, Grafana Cloud, Honeycomb) que fala OTLP e precisa de métricas de serviços AWS gerenciados que não têm agente instalável.
- O volume de métricas justifica Metric Streams (>10k séries ativas) — abaixo disso, o custo fixo do stream raramente se paga versus polling seletivo.
- Seu SLO de freshness de métricas é ≤60 segundos — Metric Streams entrega em 2–3s; polling com intervalo de 1 minuto tem latência efetiva de até 90s.
- A equipe de plataforma quer desacoplar o backend de observabilidade da AWS sem reescrever instrumentação de aplicação — o bridge é transparente para os times de produto.
- Você precisa de correlação de traces e métricas em um único backend: OTel permite enriquecer métricas com resource attributes (account ID, cluster, service) que o CloudWatch nativo não propaga.

## 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 `GetMetricData` em 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

- **2–3s** — Latência de Metric Streams. Da emissão no serviço AWS até o Firehose. Polling com 1min de intervalo tem latência efetiva de 60–90s.
- **~$0.30** — Custo por 100k séries/mês. $0.003/1k updates × ~100k séries × ~1 update/min × 43.200 min/mês ≈ $13k/mês — filtre agressivamente.
- **50** — Métricas por request na API GetMetricData. Quota hard. Com 500 req/s soft limit, polling de 25k métricas requer 500 requests — atinge o limite em 1 segundo.

> **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

- **security**: 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.
- **reliability**: 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.

> **Minha Nota de Curadoria:** 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

- [CloudWatch Metric Streams — AWS Documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Metric-Streams.html)
- [OpenTelemetry Collector — Official Docs](https://opentelemetry.io/docs/collector/)
- [Kinesis Data Firehose Lambda Transformation](https://docs.aws.amazon.com/firehose/latest/dev/data-transformation.html)
- [GetMetricData API — Quotas and Limits](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_limits.html)
- [AWS Well-Architected Framework — Operational Excellence Pillar](https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/welcome.html)
- [OTLP Specification — OpenTelemetry](https://opentelemetry.io/docs/specs/otlp/)
- [Observability Engineering — Charity Majors, Liz Fong-Jones, George Miranda](https://www.oreilly.com/library/view/observability-engineering/9781492076438/)
