# Observabilidade ML no EKS: Logs, Métricas e Rastreamento em Bake-off

Workloads de ML no EKS geram volumes de telemetria que expõem as limitações de qualquer pipeline de observabilidade não projetado para esse perfil. Neste artigo, comparo quatro abordagens de coleta e roteamento de logs e métricas, com foco em custo real, latência de diagnóstico e adequação a ambientes financeiros regulados.

- URL: https://fernando.moretes.com/blog/observabilidade-para-ml-em-eks-com-logs-metricas-e-custo

- Markdown: https://fernando.moretes.com/blog/observabilidade-para-ml-em-eks-com-logs-metricas-e-custo/article.md?lang=pt

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

- Category: Dados & Plataformas

- Tags: eks, mlops, observability, opentelemetry, cloudwatch, fluent-bit, datadog, cost-optimization

- Reading time: 9 min

- Source: [EKS ML core logging](https://aws.amazon.com/blogs/architecture/)

---

Quando um job de treinamento distribuído no EKS começa a divergir silenciosamente — gradientes explodindo, workers GPU ociosos, throughput de dados caindo pela metade — o tempo entre o primeiro sinal anômalo e o diagnóstico confirmado é determinado inteiramente pela qualidade do pipeline de observabilidade que você escolheu construir. Em ambientes financeiros onde cada hora de GPU custa entre US$ 3 e US$ 32 (p3.16xlarge a p4d.24xlarge), essa janela de diagnóstico não é um detalhe operacional: é uma linha de custo direta. Este artigo é um bake-off honesto entre quatro estratégias de observabilidade para ML no EKS — Fluent Bit nativo, OpenTelemetry Collector (OTel), CloudWatch Container Insights com Enhanced Observability, e Datadog Agent com DogStatsD. Cada uma tem um perfil de custo, latência e complexidade operacional radicalmente diferente, e a escolha errada em escala custa mais do que o workload que você está tentando monitorar.

## Por que workloads ML no EKS são um caso especial de observabilidade

Um pod de inferência stateless gera telemetria previsível: algumas centenas de linhas de log por minuto, métricas de CPU/memória, latência de request. Um job de treinamento distribuído com PyTorch DDP ou Ray Train em 32 nós GPU é outra categoria completamente diferente.

Primeiro, o **volume de logs é não-linear com o número de workers**. Cada rank emite logs de progresso de epoch, checkpointing, gradiente norm e NCCL collectives. Com 32 workers e epochs de 10 minutos, é comum ver 50-200 MB/min de stdout não estruturado chegando ao DaemonSet de coleta — antes de qualquer log de aplicação de negócio.

Segundo, as **métricas GPU têm cardinalidade alta**. O DCGM Exporter expõe ~40 métricas por GPU (SM utilization, memory bandwidth, NVLink throughput, tensor core activity, ECC errors). Em um nó `p4d.24xlarge` com 8 A100s, isso são 320 séries temporais por nó, scrapeadas a cada 15s. Com 20 nós, você tem 6.400 séries ativas — dentro do limite do CloudWatch (10 métricas customizadas por namespace sem custo adicional até 10k), mas perigosamente próximo de estourar o budget de métricas customizadas se você não filtrar na origem.

Terceiro, **rastreamento distribuído em treinamento é diferente de rastreamento em microserviços**. Não existe um trace de request; existe um grafo de execução de operações CUDA, comunicação coletiva e I/O de dados. O OpenTelemetry ainda não tem semântica nativa para isso — você precisa instrumentar manualmente os hooks de PyTorch ou usar o MLflow Tracing, que é uma camada separada.

Essas três características — volume de log explosivo, alta cardinalidade de métricas GPU e ausência de rastreamento nativo — definem os critérios de avaliação deste bake-off.

## Os quatro concorrentes: arquitetura e posicionamento

**Fluent Bit (DaemonSet nativo EKS)** é o coletor de logs padrão no add-on `aws-for-fluent-bit`. Ele lê de `/var/log/containers/*.log` via tail, parseia JSON ou regex, e roteia para CloudWatch Logs, S3, Kinesis Data Streams ou Firehose. A configuração de backpressure via `mem_buf_limit` e `storage.type filesystem` é crítica: sem ela, um burst de 200 MB/min de logs de treinamento vai OOM o DaemonSet em nós com pouca memória disponível. O ponto forte é a leveza — ~50 MB de memória em operação normal — e a integração nativa com IAM via IRSA. O ponto fraco é que ele não coleta métricas nem traces; é um coletor de logs puro.

**OpenTelemetry Collector (OTel)** é o concorrente mais versátil. Deployado como DaemonSet ou Deployment via `opentelemetry-operator`, ele unifica logs (via filelog receiver), métricas (via prometheusreceiver scrapeando DCGM) e traces (via otlp receiver) em um único pipeline. O custo operacional é maior: o pipeline precisa de tuning de `batch processor` (send_batch_size, timeout), `memory_limiter` e exporters paralelos. Mas a capacidade de rotear para múltiplos backends simultaneamente — CloudWatch, S3 via OTLP/Parquet, Jaeger — sem duplicar agentes é o diferencial arquitetural real.

**CloudWatch Container Insights com Enhanced Observability para EKS** é a opção gerenciada AWS. Ativado via add-on `amazon-cloudwatch-observability`, ele instala automaticamente o OTel Collector pré-configurado, o DCGM Exporter e o Fluent Bit. O custo de operação é zero, mas o custo financeiro é alto: Enhanced Observability cobra US$ 0,009 por vCPU-hora e US$ 0,009 por GB de memória-hora por nó monitorado — em um cluster de 20 nós `p4d.24xlarge` (96 vCPUs cada), isso é ~US$ 17.280/mês apenas de observabilidade.

**Datadog Agent com DogStatsD** é a opção enterprise mais completa. O Helm chart `datadog/datadog` instala o Agent como DaemonSet com coleta de logs, métricas (incluindo integração DCGM nativa), APM e NPM. O diferencial é o ML Observability (antigo Weights & Biases integration) e a correlação automática entre logs, métricas e traces. O custo é US$ 15-23/host/mês dependendo do tier, mais ingestão de logs a US$ 0,10/GB após o free tier.

## Pipelines de Observabilidade ML no EKS: Quatro Abordagens em Paralelo

Cada coluna representa uma estratégia de coleta. Os nós GPU e o DCGM Exporter são compartilhados. As setas mostram o fluxo de telemetria até o backend de análise.

### 🖥️ EKS — Workload Layer

- Training Pod PyTorch DDP / Ray (compute)
- DCGM Exporter /metrics :9400 (compute)
- stdout/stderr /var/log/containers (data)

### 📦 EKS — Collection DaemonSets

- Fluent Bit mem_buf_limit=256MB (compute)
- OTel Collector filelog+prom+otlp (compute)
- CW Addon amazon-cloudwatch-obs (compute)
- Datadog Agent DogStatsD :8125 (compute)

### 🟧 AWS — Managed Backends

- CloudWatch Logs /aws/eks/ml-cluster (storage)
- CloudWatch Metrics Custom NS: ML/GPU (data)
- S3 + Parquet long-term archive (storage)
- Kinesis Data Streams hot path routing (messaging)

### 🔵 External — SaaS Backends

- Datadog SaaS ML Observability (external)
- Jaeger / Tempo distributed traces (external)

### Fluxos

- gpu_pod -> stdout: emite logs
- gpu_pod -> dcgm: métricas GPU
- stdout -> fluentbit: tail
- stdout -> otel: filelog receiver
- stdout -> cw_addon: managed tail
- stdout -> dd_agent: log collection
- dcgm -> otel: scrape /metrics
- dcgm -> cw_addon: scrape /metrics
- dcgm -> dd_agent: DCGM integration
- fluentbit -> cwlogs: PutLogEvents
- fluentbit -> kinesis: hot path
- otel -> cwlogs: awscloudwatchlogs
- otel -> cwmetrics: awsemf exporter
- otel -> s3_logs: OTLP/Parquet
- otel -> jaeger: traces
- cw_addon -> cwlogs: managed
- cw_addon -> cwmetrics: EMF
- dd_agent -> dd_backend: HTTPS/443

## Comparação Técnica: Quatro Estratégias de Observabilidade ML no EKS
| Critério | Critério | Fluent Bit Nativo | OTel Collector | CW Container Insights Enhanced | Datadog Agent |
| --- | --- | --- | --- | --- | --- |
| Sinais cobertos | Logs apenas | Logs + Métricas + Traces | Logs + Métricas (GPU via DCGM) | Logs + Métricas + Traces + APM | — |
| Overhead de memória por nó | ~50 MB | 150–400 MB (pipeline size) | 200–350 MB (bundle) | 300–600 MB (full agent) | — |
| Custo mensal (20 nós p4d.24xlarge) | ~US$ 180 (CW Logs ingestion) | ~US$ 400–800 (CW EMF + Logs) | ~US$ 17.280 (Enhanced vCPU/mem fee) | ~US$ 460 + log ingestão | — |
| Latência de diagnóstico (P99) | 30–90 s (CW Logs Insights query) | 10–30 s (backend dependente) | 15–45 s (CW dashboards) | 5–15 s (Live Tail + alertas) | — |
| Suporte a métricas GPU (DCGM) | Não nativo | Via prometheusreceiver | Nativo (add-on instala DCGM) | Nativo (integração DCGM) | — |
| Conformidade / soberania de dados | Alta (dados ficam na AWS) | Alta (configurável por backend) | Alta (dados ficam na AWS) | Média (dados saem para SaaS) | — |
| Complexidade operacional | Baixa | Alta (pipeline YAML, tuning) | Baixa (gerenciado) | Média (Helm + API key rotation) | — |
| Vendor lock-in | AWS (moderado) | Mínimo (padrão aberto) | AWS (alto) | Datadog (alto) | — |

## O problema real com Enhanced Observability: custo oculto de vCPU

O add-on `amazon-cloudwatch-observability` com Enhanced Observability é a opção mais simples de ativar — um `eksctl enable addon` e você tem DCGM, Fluent Bit e OTel Collector pré-configurados. Mas o modelo de preços é armadilhoso para clusters ML.

O Enhanced Observability cobra por **vCPU-hora e GB-memória-hora por nó monitorado**, independentemente de quantas métricas você realmente usa. Um `p4d.24xlarge` tem 96 vCPUs e 1.152 GB de RAM. A US$ 0,009/vCPU-hora, só a dimensão de vCPU custa **US$ 0,864/hora por nó**. Com 20 nós rodando 24/7, isso é **US$ 12.441/mês** — e a dimensão de memória adiciona mais ~US$ 4.976/mês. Total: ~US$ 17.417/mês.

Para comparação, o custo de GPU desses 20 nós é ~US$ 460.800/mês (a US$ 32,77/hora por nó). Então a observabilidade representa ~3,8% do custo de compute — o que pode parecer razoável até você perceber que o OTel Collector standalone com EMF exporter para CloudWatch Metrics cobre 90% dos mesmos casos de uso por ~US$ 800/mês.

A recomendação prática: use Enhanced Observability **apenas em clusters de desenvolvimento/staging com nós menores** (m5, c5) onde o custo por vCPU é irrelevante. Em clusters de produção com instâncias GPU grandes, construa o pipeline OTel manualmente com `memory_limiter` configurado para 80% do limite do container, `batch processor` com `send_batch_size: 1000` e `timeout: 10s`, e `prometheusreceiver` scrapeando DCGM a cada 30s (não 15s — você reduz o volume de métricas pela metade sem perda diagnóstica significativa).

## Números que importam em clusters ML de produção

- **96×** — Multiplicador de séries temporais por nó GPU. DCGM expõe ~40 métricas × 8 GPUs × 3 dimensões (nó, GPU, processo) em um p4d.24xlarge
- **21×** — Diferença de custo mensal: Enhanced vs OTel manual (20 nós GPU). US$ 17.280 (Enhanced) vs ~US$ 820 (OTel + CW EMF + Fluent Bit standalone)
- **<15 s** — Latência de alerta com Datadog Live Tail + monitor threshold. Comparado a 60–120 s com CloudWatch Metric Alarms em alta cardinalidade

## OTel Collector: o pipeline que vale o custo operacional

O OpenTelemetry Collector é o único dos quatro concorrentes que resolve o problema de **múltiplos backends sem duplicação de agentes**. Em ambientes financeiros regulados, isso é frequentemente mandatório: você precisa enviar logs para CloudWatch (retenção regulatória de 7 anos via S3 Glacier), métricas para um backend de análise interno (Prometheus + Thanos ou Amazon Managed Prometheus) e traces para Jaeger ou Tempo — tudo simultaneamente, com garantias de entrega diferentes.

A configuração de pipeline que uso em produção para workloads ML tem três estágios críticos:

**1. Receivers paralelos com isolamento de memória**: `filelog` receiver com `start_at: beginning` desativado (evita re-leitura de logs históricos no restart do pod), `prometheusreceiver` com `scrape_interval: 30s` e `target_allocator` habilitado para distribuir o scraping entre múltiplos Collectors quando o cluster tem >50 nós.

**2. Processors em cadeia com circuit breaker**: `memory_limiter` como primeiro processor (limite em 80% do `resources.limits.memory` do container), seguido de `resource` processor para adicionar atributos `k8s.cluster.name`, `ml.job.id` e `gpu.node.type` — essenciais para correlação cross-signal. O `batch processor` vem por último, não primeiro: colocar o batch antes do memory_limiter é o erro mais comum que vejo em revisões de arquitetura.

**3. Exporters com retry e queue persistente**: `awscloudwatchlogs` exporter com `log_stream_name` derivado de `k8s.pod.name` (evita throttling por stream), `awsemf` exporter com `metric_declarations` explícitas para evitar enviar todas as 320 séries DCGM para CloudWatch (selecione as 8-12 que realmente importam: `DCGM_FI_DEV_GPU_UTIL`, `DCGM_FI_DEV_MEM_COPY_UTIL`, `DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL`, `DCGM_FI_DEV_ECC_DBE_VOL_TOTAL`). O `retry_on_failure` com `max_elapsed_time: 300s` e `sending_queue` com `storage: file_storage` garante que um throttle temporário do CloudWatch não perde dados de treinamento.

## Matriz de Decisão: Qual Estratégia para Qual Contexto

### Fluent Bit Nativo

**Pros**
- Overhead mínimo (~50 MB); ideal para nós com memória disputada por containers GPU
- Integração IRSA nativa; sem credenciais estáticas
- Roteamento para Kinesis para hot-path de alertas em tempo real

**Cons**
- Cobre apenas logs; métricas GPU requerem agente separado
- Sem correlação nativa entre logs e métricas

**Verdict:** Use como complemento de outro coletor, não como solução única

### OTel Collector

**Pros**
- Unifica logs, métricas e traces em um único DaemonSet
- Padrão aberto; sem lock-in; exporta para qualquer backend
- Custo 21× menor que Enhanced Observability em clusters GPU grandes
- target_allocator escala o scraping horizontalmente

**Cons**
- Alta complexidade de configuração; erros de pipeline causam perda silenciosa de dados
- Requer expertise em YAML de pipeline OTel e tuning de memória

**Verdict:** Melhor escolha para produção em clusters GPU de médio a grande porte com requisitos multi-backend

### CW Container Insights Enhanced

**Pros**
- Zero operação; add-on gerenciado pela AWS
- DCGM nativo sem configuração adicional
- Ideal para equipes sem expertise em OTel

**Cons**
- Custo proibitivo em instâncias GPU grandes (~US$ 17k/mês para 20 nós p4d)
- Lock-in forte em CloudWatch; difícil migrar dados históricos

**Verdict:** Aceitável apenas em clusters de dev/staging com instâncias menores; nunca em produção GPU de grande escala

### Datadog Agent

**Pros**
- Menor latência de diagnóstico (<15 s com Live Tail)
- ML Observability com correlação automática run/experimento
- APM nativo para pipelines de serving (Triton, TorchServe)

**Cons**
- Dados saem da AWS; problema em ambientes com restrições de soberania de dados financeiros
- Custo cresce linearmente com hosts; pode superar OTel+CW em clusters grandes
- API key rotation requer processo de segurança adicional (Secrets Manager + External Secrets Operator)

**Verdict:** Melhor para equipes de ML que precisam de diagnóstico rápido e não têm restrições de soberania de dados

## Segurança e governança: o que muda quando logs de ML contêm dados sensíveis

Em ambientes financeiros, logs de treinamento frequentemente contêm informações que não deveriam sair do perímetro de segurança: IDs de clientes em datasets de validação, valores de features derivadas de transações, ou simplesmente o nome do modelo e versão (que é informação competitiva sensível). Isso adiciona uma camada de requisitos que a maioria das comparações de observabilidade ignora.

**CloudWatch Logs com KMS**: todos os quatro coletores suportam envio para CloudWatch Logs, mas apenas se você configurar `kms_key_id` no log group. O Fluent Bit faz isso via `auto_create_group Off` e criação prévia do grupo com `aws logs create-log-group --kms-key-id arn:aws:kms:...`. O OTel Collector exige que o log group exista antes do primeiro `PutLogEvents` — uma race condition comum em clusters que escalam rapidamente.

**IAM com condições de contexto**: a política IRSA do coletor deve ter `Condition: StringEquals: aws:RequestedRegion: [região]` para evitar exfiltração cross-region, e `aws:SourceVpc` se você usa VPC Endpoints para CloudWatch. O Datadog Agent armazena a API key em um Secret do Kubernetes — use External Secrets Operator com AWS Secrets Manager e rotação automática de 90 dias, não um `kubectl create secret` manual.

**Data masking no pipeline**: o OTel Collector tem o `transform` processor com OTTL (OpenTelemetry Transformation Language) que permite mascarar campos antes do envio: `replace_pattern(body, "customer_id=\\d+", "customer_id=REDACTED")`. O Fluent Bit tem o `lua` filter para o mesmo fim. O Datadog tem Sensitive Data Scanner no lado do backend, mas isso significa que o dado já saiu da AWS — para ambientes regulados, o mascaramento deve acontecer no coletor, não no backend.

**Retenção e imutabilidade**: para conformidade com regulações financeiras (BACEN, CVM, SOX), configure S3 Object Lock em modo COMPLIANCE nos buckets de arquivo de logs com período de retenção de 7 anos. O Fluent Bit com output S3 suporta `s3_key_format /%Y/%m/%d/%H/` para particionamento temporal que facilita queries de auditoria com Athena.

> **O erro de arquitetura mais caro: scraping DCGM sem filtro de cardinalidade:** Vi equipes enviarem todas as ~40 métricas do DCGM Exporter para CloudWatch Metrics sem `metric_declarations`. Em um cluster de 30 nós p4d.24xlarge, isso gera 9.600 séries temporais customizadas. O CloudWatch cobra US$ 0,30 por métrica customizada/mês após as primeiras 10.000 — mas o custo real não é esse. É o custo de API: `PutMetricData` tem limite de 1.000 valores por chamada e 150 TPS por conta. Com scraping a cada 15s e 9.600 séries, você precisa de ~10 chamadas por ciclo por nó — e começa a ver `ThrottlingException` que o OTel Collector trata com retry exponencial, aumentando a latência de diagnóstico exatamente quando você mais precisa dela. Filtre para 8-12 métricas essenciais na origem, não no destino.

## Anti-padrões que vi em revisões de arquitetura ML/EKS

- Ativar Enhanced Observability em clusters de produção GPU sem calcular o custo por vCPU-hora antecipadamente
- Colocar `batch processor` antes de `memory_limiter` no pipeline OTel — o batch acumula dados na memória antes do limiter poder agir
- Usar `start_at: beginning` no filelog receiver sem `storage` extension — causa re-leitura de todo o histórico de logs a cada restart do Collector
- Armazenar API keys do Datadog em Secrets Kubernetes sem rotação automática via External Secrets Operator
- Não configurar `mem_buf_limit` no Fluent Bit em nós GPU — um burst de logs de treinamento pode OOM o DaemonSet e interromper a coleta de todos os pods no nó
- Enviar dados de PII de datasets de validação para backends SaaS sem mascaramento no coletor

> **Minha nota de curadoria:** Em produção, eu construo o pipeline como OTel Collector com `prometheusreceiver` para DCGM (scrape a 30s, não 15s) + `awsemf` exporter com `metric_declarations` explícitas para as 10 métricas GPU que realmente importam + Fluent Bit apenas para o hot-path de logs críticos via Kinesis. O Datadog fica reservado para ambientes onde a equipe de ML precisa de diagnóstico em <15s e não há restrição de soberania de dados — que em bancos brasileiros é raramente o caso. A lição mais cara que aprendi: o custo de observabilidade em clusters GPU não é marginal — em um cluster de treinamento de grande escala, pode facilmente superar o custo de um engenheiro sênior por mês se você não dimensionar o pipeline conscientemente desde o primeiro deploy.

## Recomendação Final: Composição, Não Escolha Única

Não existe um único vencedor neste bake-off porque os quatro concorrentes resolvem problemas diferentes. A arquitetura que recomendo para clusters ML de produção em ambientes financeiros é uma composição deliberada:

**Camada de coleta**: OTel Collector como DaemonSet principal, com `opentelemetry-operator` para gerenciar o ciclo de vida. Configure `filelog receiver` para logs, `prometheusreceiver` para DCGM (30s, filtrado para 10 métricas), e `otlp receiver` para traces de serving. Use `memory_limiter` como primeiro processor, `resource` para enriquecimento de atributos ML, e `batch` por último.

**Roteamento dual**: `awsemf` exporter para métricas críticas em CloudWatch (alertas operacionais), `awscloudwatchlogs` para logs com KMS, e `otlphttp` exporter para S3 via Parquet para arquivo de longo prazo e análise com Athena. Para clusters com requisitos de trace de serving, adicione `jaeger` exporter apontando para AWS X-Ray via OTel.

**Fluent Bit como hot-path**: mantenha o Fluent Bit DaemonSet apenas para roteamento de logs críticos (erros, OOM, falhas de checkpoint) para Kinesis Data Streams → Lambda → PagerDuty. Isso garante latência de alerta <30s sem o overhead do OTel Collec

**Rating:** OTel Collector + Fluent Bit hot-path

## Referências Técnicas

- [AWS: Amazon CloudWatch Observability EKS Add-on](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-EKS-otel.html)
- [AWS: CloudWatch Container Insights Enhanced Observability Pricing](https://aws.amazon.com/cloudwatch/pricing/)
- [AWS: Using AWS Distro for OpenTelemetry with EKS](https://aws-otel.github.io/docs/getting-started/adot-eks-add-on)
- [OpenTelemetry: Collector Configuration — Memory Limiter Processor](https://github.com/open-telemetry/opentelemetry-collector/tree/main/processor/memorylimiterprocessor)
- [NVIDIA: DCGM Exporter for Kubernetes](https://github.com/NVIDIA/dcgm-exporter)
- [AWS: Fluent Bit for EKS — aws-for-fluent-bit](https://github.com/aws/aws-for-fluent-bit)
- [AWS Architecture Blog: EKS ML Core Logging](https://aws.amazon.com/blogs/architecture/)
- [OpenTelemetry: Target Allocator for Prometheus Receiver](https://github.com/open-telemetry/opentelemetry-operator/tree/main/cmd/otel-allocator)
