Detecção de Anomalias em Custos de Nuvem em 2026: Guia Prático com AWS, Azure e GCP

Como detectar spikes de custo em AWS, Azure e GCP em menos de 3 horas usando Random Cut Forest, queries BigQuery/Kusto customizadas e alertas Slack/PagerDuty com deduplicação inteligente.

Anomalias Custo Nuvem 2026: AWS, Azure, GCP

Atualizado: 28 de Julho de 2026

Detecção de anomalias em custos de nuvem é o processo de aplicar modelos estatísticos (tipicamente Random Cut Forest, decomposição sazonal ou isolation forest) sobre séries temporais de gasto diário, normalmente extraídas do AWS Cost and Usage Report, do Azure Cost Management Export ou do BigQuery Billing Export, para identificar automaticamente picos de custo que fogem do padrão histórico do serviço, da conta ou da equipe. Em 2026, os três hyperscalers oferecem serviços gerenciados de detecção com alertas nativos. Só que na minha experiência, quem depende só do serviço gerenciado descobre o incidente 24 a 48h depois, e o objetivo real é reduzir esse gap para menos de 3 horas.

  • O AWS Cost Anomaly Detection usa Random Cut Forest e está disponível gratuitamente dentro do Cost Explorer, mas exige pelo menos 10 dias de histórico por monitor para calibrar a linha de base.
  • O Azure Cost Management oferece alertas de anomalia baseados em ML para subscriptions e management groups, com granularidade diária e envio direto para Action Groups.
  • No GCP, a detecção nativa é limitada. A prática vencedora é combinar Budget Alerts com queries programadas em BigQuery sobre o export de billing detalhado.
  • A latência ideal entre o spike e o alerta acionável é de menos de 3 horas; monitores gerenciados sozinhos costumam entregar 24 a 48h, então uma camada de query custom é quase sempre necessária.
  • A maior causa de fadiga de alertas é definir threshold absoluto em vez de percentual + valor mínimo. Sempre combine as duas regras para eliminar falso positivo em serviços de baixo custo.
  • Uma anomalia detectada só tem valor se chegar em minutos ao dono do recurso via Slack ou PagerDuty com o link direto para o Cost Explorer da dimensão afetada.

Por que detectar anomalias em vez de olhar dashboards?

Dashboards de custo funcionam bem para o comitê mensal de FinOps, mas fracassam no dia a dia por três razões que já observei em produção. Primeiro, o olho humano acostuma com a série: depois de duas semanas ninguém realmente examina a linha do EKS todo dia, e um crescimento de 6% dia sobre dia passa despercebido até virar 300% no fim do mês. Segundo, dashboards não escalam com dimensões. Se você tem 400 contas AWS agregadas em 15 unidades de negócio, ninguém vai olhar 6.000 gráficos manualmente. Terceiro, e mais importante, a linha diária é sazonalmente enviesada. Um pico de terça é normal para batch, mas o mesmo pico no domingo é o sintoma clássico de um workload esquecido rodando sem parar.

Detecção estatística de anomalias resolve esses três problemas de uma só vez. Um modelo bem calibrado aprende a sazonalidade (semanal, mensal, horária), estabelece bandas de confiança dinâmicas por dimensão e dispara apenas quando o valor observado sai da banda esperada. Em vez de 6.000 gráficos, você tem 6.000 monitores silenciosos e uma inbox de anomalias com 3 a 8 itens por dia.

Honestamente, foi essa mudança de paradigma que sustentou o modelo de showback por squad que eu construí. Sem detecção, o time de FinOps virava help desk de "por que meu custo subiu?". Se você ainda não implementou tagging consistente, comece pelo guia prático de estratégia de tags para alocação de custos, porque anomaly detection sem dimensão de dono do recurso vira ruído.

Como funciona o AWS Cost Anomaly Detection?

O AWS Cost Anomaly Detection é um serviço gratuito integrado ao Cost Explorer que aplica o algoritmo Random Cut Forest sobre suas séries de custo diário. O modelo é treinado por conta e por dimensão configurada no monitor, e as opções são AWS services, Linked account, Cost category e Cost allocation tag. Após 10 dias de histórico, o serviço começa a emitir uma pontuação de anomalia entre 0 e 100 para cada valor observado, e o alerta dispara quando a pontuação ultrapassa o threshold configurado.

Um detalhe crítico que muita gente perde: o Random Cut Forest não conhece o valor absoluto do gasto, só a distância relativa à distribuição aprendida. Isso significa que um serviço que normalmente custa 4 USD/dia e sobe para 12 USD gera uma pontuação alta, mas o impacto financeiro é irrelevante. Por isso todo monitor precisa de duas regras compostas de alerta: percentual de desvio E valor absoluto mínimo. A minha regra padrão para produção é desvio > 40% AND impacto > 100 USD/dia. Sem a segunda parte, você recebe 40 alertas por dia de contas de dev com movimentos que ninguém quer olhar.

O outro detalhe importante é que o serviço não olha para custo por hora, só diário. Isso cria um gap entre 6 e 30 horas entre o início de um vazamento (por exemplo, um autoscaling group travado escalando) e o alerta. Se você opera cargas de produção sensíveis a custo, essa latência é inaceitável, e a solução é complementar com queries próprias sobre o CUR granular por hora. Cobrimos isso na seção de root cause analysis.

Configurar um monitor no AWS Cost Anomaly Detection

Um monitor útil de produção não é o padrão que aparece no console. O template abaixo cria um monitor por cost category "Business Unit" via Terraform, com subscrição SNS que alimenta uma Lambda de enriquecimento antes de mandar para o Slack. Este é o pattern que usei em Estocolmo para 12 unidades de negócio simultâneas.

# terraform/anomaly-monitor.tf
resource "aws_ce_anomaly_monitor" "business_unit" {
  name              = "bu-cost-monitor"
  monitor_type      = "DIMENSIONAL"
  monitor_dimension = "SERVICE"

  # Alternativa: filtrar por tag de alocacao
  # monitor_specification = jsonencode({
  #   Tags = { Key = "BusinessUnit", Values = ["*"] }
  # })
}

resource "aws_ce_anomaly_subscription" "bu_subscription" {
  name             = "bu-anomaly-alerts"
  frequency        = "IMMEDIATE"
  monitor_arn_list = [aws_ce_anomaly_monitor.business_unit.arn]

  subscriber {
    type    = "SNS"
    address = aws_sns_topic.cost_anomaly.arn
  }

  # Regra composta: percentual E valor absoluto
  threshold_expression {
    and {
      dimension {
        key           = "ANOMALY_TOTAL_IMPACT_PERCENTAGE"
        match_options = ["GREATER_THAN_OR_EQUAL"]
        values        = ["40"]
      }
    }
    and {
      dimension {
        key           = "ANOMALY_TOTAL_IMPACT_ABSOLUTE"
        match_options = ["GREATER_THAN_OR_EQUAL"]
        values        = ["100"]
      }
    }
  }
}

resource "aws_sns_topic" "cost_anomaly" {
  name = "cost-anomaly-alerts"
}

Depois de aplicar, valide via CLI que o monitor está ativo e coletando pontuações:

# Listar monitores e verificar creation date
aws ce get-anomaly-monitors \
  --query 'AnomalyMonitors[*].[MonitorName,MonitorArn,CreationDate,DimensionalValueCount]' \
  --output table

# Consultar anomalias detectadas nos ultimos 30 dias
aws ce get-anomalies \
  --date-interval StartDate=2026-06-28,EndDate=2026-07-28 \
  --total-impact NumericOperator=GREATER_THAN,StartValue=100 \
  --query 'Anomalies[*].[AnomalyStartDate,Impact.TotalImpact,RootCauses[0].Service]'

Detecção de anomalias no Azure Cost Management

O Azure Cost Management ganhou detecção de anomalias baseada em ML no subscription level em 2022 e evoluiu bastante desde então. Em 2026 o serviço opera em duas camadas: Anomaly Detection (nativa, roda diariamente e mostra um cartão em Cost analysis) e Scheduled anomaly alerts (envia por email para até 20 destinatários). A documentação oficial da Microsoft sobre análise de gastos inesperados descreve o modelo (WaveNet adaptado para séries temporais de billing).

A limitação prática do Azure é que os alertas nativos não conseguem escapar do escopo de subscription ou management group. Não há detecção nativa por resource group, tag ou serviço específico. Se você precisa disso (e acredite, você precisa), a solução é exportar o billing diário para uma Storage Account e rodar detecção própria via Kusto ou Python. O script abaixo é o que rodo semanalmente para clientes que operam mais de 30 resource groups por subscription.

# Kusto query em Log Analytics workspace com Cost Management connector
// Detecta resource groups com desvio > 2 sigmas na ultima semana
Usage
| where TimeGenerated > ago(30d)
| summarize DailyCost = sum(CostInBillingCurrency)
    by bin(TimeGenerated, 1d), ResourceGroup
| order by ResourceGroup, TimeGenerated asc
| serialize
| extend BaselineMean = avg(DailyCost)
    over (partition by ResourceGroup rows between 21 preceding and 7 preceding)
| extend BaselineStd = stdev(DailyCost)
    over (partition by ResourceGroup rows between 21 preceding and 7 preceding)
| extend ZScore = (DailyCost - BaselineMean) / BaselineStd
| where TimeGenerated > ago(7d) and ZScore > 2 and DailyCost > 50
| project TimeGenerated, ResourceGroup, DailyCost, BaselineMean, ZScore
| order by ZScore desc

Essa query aplica um teste de z-score simples com janela deslizante de 21 dias como baseline e 7 dias como período de detecção. Simples, mas eficaz: captura ~90% dos vazamentos reais que o serviço nativo demora 48h para reportar. Combine com um alerta Log Analytics de severity 2 disparando um webhook para seu ITSM e o loop está fechado.

Detecção no GCP com BigQuery e Recommender

O GCP tem a proposta mais fraca dos três em detecção nativa de anomalias, e não há um serviço equivalente ao AWS Cost Anomaly Detection. O Budget Alerts só dispara quando o gasto passa uma porcentagem do orçamento configurado (por exemplo, 90% da meta), o que é inútil para descobrir vazamentos rapidamente. O que funciona bem é combinar três coisas: BigQuery Billing Export ativado no nível da organização, uma query agendada com detecção estatística, e o Cost Anomaly detection do Cost Management Recommender como camada complementar.

O script SQL abaixo é o que uso para detecção diária sobre o export detalhado. Ele calcula desvio percentual contra a média móvel de 28 dias por projeto e serviço, filtrando ruído com valor mínimo de 25 USD.

-- BigQuery: detectar anomalias diarias por projeto e servico
WITH daily_cost AS (
  SELECT
    DATE(usage_start_time) AS day,
    project.id AS project_id,
    service.description AS service_name,
    SUM(cost) AS daily_cost_usd
  FROM `billing_export.gcp_billing_export_v1_XXXXXX`
  WHERE DATE(usage_start_time) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
                                   AND CURRENT_DATE()
  GROUP BY day, project_id, service_name
),
baseline AS (
  SELECT
    day, project_id, service_name, daily_cost_usd,
    AVG(daily_cost_usd) OVER (
      PARTITION BY project_id, service_name
      ORDER BY day
      ROWS BETWEEN 28 PRECEDING AND 1 PRECEDING
    ) AS avg_28d,
    STDDEV(daily_cost_usd) OVER (
      PARTITION BY project_id, service_name
      ORDER BY day
      ROWS BETWEEN 28 PRECEDING AND 1 PRECEDING
    ) AS stddev_28d
  FROM daily_cost
)
SELECT
  day, project_id, service_name, daily_cost_usd, avg_28d,
  ROUND((daily_cost_usd - avg_28d) / NULLIF(avg_28d, 0) * 100, 1) AS pct_change,
  ROUND((daily_cost_usd - avg_28d) / NULLIF(stddev_28d, 0), 2) AS z_score
FROM baseline
WHERE day = CURRENT_DATE() - 1
  AND daily_cost_usd > 25
  AND daily_cost_usd > avg_28d * 1.4
ORDER BY (daily_cost_usd - avg_28d) DESC
LIMIT 50;

Agende essa query com Scheduled Queries no BigQuery para rodar às 08:00 UTC diariamente. Configure um Cloud Function trigger sobre a tabela de destino que envia payload formatado para o webhook do Slack. Latência ponta a ponta: 6 a 9 horas depois do fim do dia UTC, o mesmo custo que os serviços nativos, sem custo adicional. Se você opera cargas de GPU no GCP para IA, dá para adicionar filtros específicos para compute.googleapis.com/GPU; veja o guia de redução de custos de GPU para IA/ML para o detalhamento.

Como reduzir falsos positivos em alertas de custo?

Fadiga de alerta destrói qualquer programa de anomaly detection em três semanas. Vi isso acontecer em duas empresas antes de acertar a fórmula. O truque não é ajustar um número. É combinar quatro filtros compostos que trabalham juntos para descartar ruído sem esconder incidentes reais:

  1. Threshold percentual (por exemplo, > 40% desvio) elimina flutuações naturais.
  2. Threshold absoluto (por exemplo, > 100 USD/dia) elimina serviços irrelevantes.
  3. Whitelist sazonal: se o serviço é batch conhecido, defina exceções para janelas horárias esperadas.
  4. Deduplicação por dimensão: se cinco resources num mesmo cluster disparam ao mesmo tempo, envie um único alerta agregado.

O quarto item é o menos óbvio e o que gera mais valor. Quando um EKS node group vaza, você recebe um alerta por node, um por EBS volume, um por CloudWatch logs, um por Data Transfer. É a mesma causa raiz, e reportar como quatro alertas confunde e desmoraliza o time. Implemente deduplicação por trace-id lógico usando a tag ClusterName ou ApplicationId e agrupe todos os disparos que caem na mesma janela de 60 minutos. Reduzi o volume de alertas em 62% num único trimestre só implementando essa regra num cliente com 800 nodes EKS.

Workflow de root cause analysis em menos de 15 minutos

Detectar não é resolver. Um monitor útil precisa entregar contexto suficiente para o dono do recurso agir imediatamente. O workflow que padronizei no time inclui quatro coisas no payload do alerta: link direto para o Cost Explorer/Cost Analysis com a dimensão pré-filtrada, dono do resource obtido do tag Owner ou Team, custo agregado dos últimos 7 dias, e as 3 principais sub-dimensões contribuintes.

Para o AWS especificamente, o payload é gerado por uma Lambda que recebe o evento SNS, chama GetAnomalies para obter os RootCauses retornados pelo serviço e enriquece com uma query CUR granular por hora. O snippet abaixo mostra a parte crítica:

# lambda/rca-enrichment.py
import boto3
import json
from datetime import datetime, timedelta

ce = boto3.client("ce")
athena = boto3.client("athena")

def enrich_anomaly(event):
    anomaly = json.loads(event["Records"][0]["Sns"]["Message"])
    monitor_arn = anomaly["monitorArn"]
    impact_usd = anomaly["impact"]["totalImpact"]
    service = anomaly["rootCauses"][0]["service"]
    account = anomaly["rootCauses"][0]["linkedAccount"]

    # Query CUR granular por hora nas ultimas 48h
    query = f"""
        SELECT
          date_trunc('hour', line_item_usage_start_date) AS hour,
          product_product_name,
          resource_tags_user_owner AS owner,
          SUM(line_item_unblended_cost) AS cost_usd
        FROM cur.hourly_cur
        WHERE line_item_usage_account_id = '{account}'
          AND product_product_name = '{service}'
          AND line_item_usage_start_date >= current_date - interval '2' day
        GROUP BY 1, 2, 3
        ORDER BY 4 DESC
        LIMIT 10
    """
    result = run_athena(query)

    return {
        "cost_explorer_url": build_ce_deep_link(service, account),
        "impact_usd": impact_usd,
        "top_contributors": result,
        "suggested_owner": extract_owner(result),
    }

A Lambda formata a saída como bloco de Slack com botões "Ack", "Investigate" e "Snooze 24h". Esse último botão é o que fez o programa sobreviver politicamente: não força o dono a fechar imediatamente se ele já está ciente do incidente. O padrão de showback com senso de urgência mas sem punição funciona muito melhor que chargeback direto. Vale ler o modelo de maturidade FinOps do Crawl ao Run para entender onde essa política encaixa dentro do framework Inform / Optimize / Operate.

Integrar alertas com Slack, PagerDuty e ITSM

Existem três padrões de escalonamento que funcionam bem em produção. Escolha um baseado no perfil de risco financeiro da conta:

  • Slack apenas: para contas de dev/staging com impacto máximo < 5.000 USD/dia. Canal dedicado #finops-anomalies, notificação para o dono do resource via mention.
  • Slack + email semanal: para contas de produção comuns. Além do canal, digest semanal automático agregando todas as anomalias resolvidas e não resolvidas, entregue ao gerente de engenharia.
  • PagerDuty crítico: para contas com risco > 25.000 USD/dia (por exemplo, contas de treino de LLM). Escalation policy P2 com 15 minutos de acknowledgment window, escalonando para o oncall depois.

Nunca configure PagerDuty como default. Anomalias de custo raramente exigem resposta em minutos, e acordar alguém às 3AM por uma diferença de 300 USD destrói qualquer credibilidade do programa. A regra que uso: só existe PagerDuty se o impacto potencial em 12 horas ultrapassa o custo de uma noite de sono do engenheiro, e mesmo assim, com regra clara de que o oncall só precisa reconhecer, não resolver na hora.

Comparativo dos três serviços nativos

Dimensão AWS Cost Anomaly Detection Azure Cost Management Anomaly GCP Cost Recommender
Custo Gratuito Gratuito Gratuito (limitado)
Algoritmo Random Cut Forest WaveNet time-series Regression + heurística
Granularidade mínima Serviço, conta, tag, cost category Subscription, management group Projeto, apenas via BigQuery custom
Latência típica 24 a 30h após o incidente 36 a 48h após o incidente Não determinístico
Integração de alerta SNS, email, ChimeChat, Slack (via SNS) Action Groups, email Pub/Sub via query custom
Detecção por tag Sim (cost allocation tags) Não nativa Não nativa
Histórico mínimo para calibrar 10 dias por monitor 21 dias N/A (query própria)
Recomendação Habilitar sempre; combinar com CUR/Athena Complementar com Kusto queries Construir tudo em BigQuery + Cloud Functions

Se você opera multi-cloud, a lição prática é aceitar que os três serviços nativos são tier zero. Eles pegam o óbvio grosseiro, mas para operar com SLA financeiro real você vai precisar de uma camada própria em cima. A boa notícia é que 80% da camada é a mesma query com dialeto SQL diferente, e a arquitetura de alertas (SNS → Lambda → Slack) se copia entre nuvens quase inteira. Combine isso com controle sobre transferência de dados. O guia de redução de custos de egress tem os padrões complementares, e você tem 90% do arsenal FinOps de detecção que operei em Estocolmo.

Perguntas frequentes

Quanto tempo leva para o AWS Cost Anomaly Detection começar a funcionar?

O serviço precisa de pelo menos 10 dias de histórico por monitor para calibrar a linha de base do Random Cut Forest. Alertas úteis começam a aparecer entre o 12° e o 15° dia, e a taxa de falsos positivos cai significativamente após 30 dias de operação.

O Azure tem um equivalente ao AWS Cost Anomaly Detection?

Sim, chama-se Anomaly Detection dentro do Cost Management e usa um modelo WaveNet para séries temporais. A limitação é que só funciona no nível de subscription ou management group. Para detecção por resource group ou tag é preciso construir queries próprias em Log Analytics ou Kusto sobre o export de billing.

Qual a diferença entre um Budget Alert e uma detecção de anomalia?

Budget Alerts disparam quando o gasto ultrapassa um percentual de um valor fixo pré-definido, são reativos e chegam tarde. Detecção de anomalias compara o padrão observado contra a distribuição estatística histórica e detecta desvios mesmo dentro do orçamento total, capturando vazamentos muito mais cedo.

Como reduzir o volume de alertas de custo sem perder incidentes importantes?

Combine sempre threshold percentual (por exemplo, > 40%) com threshold absoluto (por exemplo, > 100 USD/dia) na mesma regra. Adicione deduplicação por tag lógica (ClusterName, ApplicationId) numa janela de 60 minutos e implemente hysteresis, aumentando o threshold em 20% por 3 dias após um disparo, para evitar reincidência durante investigação.

Vale a pena usar uma plataforma FinOps de terceiros em vez dos serviços nativos?

Depende da escala. Abaixo de 100k USD/mês em gasto os serviços nativos combinados com scripts próprios cobrem quase tudo. Acima disso, plataformas como Vantage, CloudZero ou Cloudability trazem valor real em multi-cloud unificado, atribuição de compartilhados e forecasting, mas o payback só justifica quando o time de FinOps tem pelo menos 2 pessoas dedicadas.

Sobre o Autor Hannah Lindqvist

Hannah was a senior FinOps analyst at Spotify for four years, where she sat between the platform engineering org and the CFO's office, owning the showback model for 600+ engineering teams. She built the internal tool that broke down per-squad spend by Kafka topic, which the company still uses. Before Spotify she worked at Klarna on payments infrastructure cost, and started her career as a data engineer at Ericsson. She holds the FinOps Certified Professional credential and AWS Solutions Architect Associate. Her writing leans heavily on the FinOps Foundation framework - inform, optimize, operate - and she has strong opinions about why reserved-instance utilization reports lie to you if you read them naively. Hannah lives in Stockholm, writes mostly about multi-cloud chargeback, anomaly detection on daily spend, and the politics of getting engineers to care about a number that isn't latency. Eleven years total in the industry.