Voltar ao blog
AIOpsNOCMonitoringSREAlertmanager

AIOps na prática: como reduzir a fadiga de alertas, do Alertmanager ao machine learning

Mede que alertas disparam e são ignorados, usa o agrupamento e a inibição do Alertmanager, corrige as etiquetas e só depois acrescenta machine learning.

P
Davi Nunes
February 25, 2026Atualizado October 30, 202611 min de leitura

Um pager que toca dezenas de vezes por noite não torna uma equipa mais cuidadosa. Ensina as pessoas a reconhecer primeiro e ler depois, até que o alerta que importava recebe o mesmo tratamento que todos os outros.

O AIOps é vendido como a cura: pões machine learning sobre o fluxo de alertas e deixas que separe o sinal do ruído. Pode ajudar, mas um modelo treinado com alertas sem etiquetas, sem dono e sem registo do que foi tratado aprende o teu ruído com grande precisão. A ordem que funciona: mede a avalanche, aproveita o que o Alertmanager já faz, corrige etiquetas e dependências e só depois põe machine learning onde uma regra estática não consegue fazer o trabalho.

Este artigo trata da avalanche que já tens. Para decidir que alertas criar à partida, lê o nosso guia sobre alertas de Kubernetes que continuam acionáveis.

Mede a avalanche antes de comprar seja o que for

Não consegues reduzir um ruído que não contaste, e muitas vezes o ranking é muito desequilibrado: um punhado de regras produz a maioria dos avisos.

Que alertas disparam mais

O Prometheus escreve uma série ALERTS para cada alerta que as suas regras produzem, com as etiquetas alertname, alertstate (pending ou firing) e as etiquetas do próprio alerta. Duas consultas sobre ela ordenam as tuas regras:

promql
# Time spent firing: rule evaluations in which each alert was firing,
# summed over all its label sets (pods, instances, ...)
topk(20, sum by (alertname) (
  count_over_time(ALERTS{alertstate="firing"}[7d])
))

# Breadth: how many distinct label sets each alert fired for this week
topk(20, count by (alertname) (
  count_over_time(ALERTS{alertstate="firing"}[7d])
))

A primeira consulta conta amostras, não disparos: multiplica pelo intervalo de avaliação das tuas regras para obteres o tempo em disparo. Um alerta que fica ativo durante dias sem que ninguém o corrija ou não é necessário ou está a ser ignorado; ambos os casos pedem uma decisão. A segunda revela os alertas que disparam separadamente para cada pod ou instância de uma frota, candidatos a agregação ou agrupamento.

Para encontrares regras instáveis (flapping), conta quantas vezes cada alerta começa a disparar:

promql
# Roughly how many times each alert went from not firing to firing in the last day
topk(20, sum by (alertname) (
  count_over_time(
    (ALERTS{alertstate="firing"} unless ALERTS{alertstate="firing"} offset 1m)[1d:1m]
  )
))

Uma regra que começa muitas vezes por dia e fica ativa durante uns minutos de cada vez precisa de uma duração for: mais longa, ou de keep_firing_for nas versões recentes do Prometheus, não de uma pessoa.

Dois limites: o ALERTS cobre apenas as regras que o Prometheus avalia e ignora silêncios e inibição, por isso mostra o que disparou, não o que chegou a uma pessoa. E um intervalo de 7 dias precisa de pelo menos uma semana de retenção.

Que alertas são reconhecidos e ignorados

O Alertmanager não tem o conceito de reconhecimento (acknowledgment). Esse dado vive na tua ferramenta de on-call, por isso exporta os avisos dos últimos 30 dias e, para cada alertname, regista quantas vezes avisou, quantas vezes alguém agiu (uma correção, um rollback, um ticket, um passo do runbook) e quantas vezes foi reconhecido e fechado sem qualquer ação ou se resolveu sozinho antes de alguém olhar.

Se a exportação não mostrar se houve ação, pede a quem está de on-call que classifique cada aviso numa revisão semanal como tratado, apenas reconhecido ou duplicado. Lista também os silêncios de longa duração com amtool silence query: um alerta silenciado há meses é um alerta em que ninguém confia.

Cada regra recebe então um veredicto: eliminar, despromover para ticket ou dashboard, corrigir (limiar, duração, etiquetas) ou manter como aviso.

O que o Alertmanager já faz antes de qualquer machine learning

Grande parte da correlação que as plataformas de AIOps anunciam começa com quatro mecanismos que o Alertmanager tem há anos:

  • Agrupamento. O group_by decide que alertas partilham uma notificação. O group_wait atrasa a primeira notificação de um grupo novo para que os alertas relacionados, e qualquer alerta que os deva inibir, cheguem juntos: é a correlação temporal na sua forma mais simples. O group_interval dita o ritmo das notificações sobre alertas acrescentados a um grupo existente, e o repeat_interval define quando são reenviados os alertas que continuam ativos.
  • Encaminhamento. Uma árvore de rotas baseadas em etiquetas envia os alertas críticos para o pager e o resto para as filas de cada equipa. Ganha a primeira rota que corresponde, a não ser que tenha continue: true.
  • Inibição. Enquanto um alerta de origem está ativo, os alertas de destino correspondentes ficam silenciados se as etiquetas listadas em equal coincidirem em ambos.
  • Silêncios. Silenciamentos com prazo para trabalho planeado.

Um ponto de partida realista que passa no amtool check-config:

yaml
route:
  receiver: team-tickets
  group_by: ['env', 'system']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - receiver: oncall-pager
      matchers:
        - severity="critical"
      group_wait: 10s
      repeat_interval: 1h
    - receiver: payments-queue
      matchers:
        - team="payments"
    - receiver: dashboard-only
      matchers:
        - severity="info"

inhibit_rules:
  # A node that is down explains every other alert on that node
  - source_matchers:
      - alertname="KubeNodeNotReady"
    target_matchers:
      - alertname!="KubeNodeNotReady"
    equal: ['cluster', 'node']
  # A database that is down explains the errors of the apps that use it
  - source_matchers:
      - alertname="PostgresDown"
      - system!=""
    target_matchers:
      - tier="app"
    equal: ['env', 'system']

receivers:
  - name: oncall-pager
    webhook_configs:
      - url: 'http://pager-bridge.monitoring.svc:8080/alerts'
  - name: payments-queue
    webhook_configs:
      - url: 'http://ticket-bridge.monitoring.svc:8080/payments'
  - name: team-tickets
    webhook_configs:
      - url: 'http://ticket-bridge.monitoring.svc:8080/default'
  - name: dashboard-only

system e tier são etiquetas tuas (falamos delas mais abaixo), e PostgresDown representa o teu alerta de base de dados. Um receiver sem configuração descarta o que lhe chega, o que serve para alertas informativos.

A regra do nó esconde uma armadilha. Os alertas de pods baseados no kube-state-metrics têm namespace e pod mas não a etiqueta node, e a sua etiqueta instance aponta para o próprio kube-state-metrics. Enquanto não acrescentares node, essa regra de inibição não faz nada e não dá sinal disso. Acrescenta-a na expressão da regra do pod com um join:

promql
(
  max by (cluster, namespace, pod) (
    increase(kube_pod_container_status_restarts_total[15m])
  ) > 3
)
* on (cluster, namespace, pod) group_left(node)
  max by (cluster, namespace, pod, node) (kube_pod_info)

Para trabalho planeado, faz com que o pipeline crie um silêncio restrito e com prazo, em vez de alguém silenciar um canal inteiro:

bash
amtool silence add env=prod system=orders \
  --duration=2h \
  --comment="Planned PostgreSQL failover, change 4812" \
  --alertmanager.url=http://alertmanager.monitoring.svc:9093

Queres o inventário sem o construíres tu? A nossa avaliação gratuita do ruído de alertas corre estas consultas no teu Prometheus, revê as tuas regras de encaminhamento e inibição e entrega-te uma lista ordenada de regras a eliminar, despromover, agrupar ou corrigir, antes de gastares seja o que for em ferramentas.

A correlação começa nas etiquetas e num mapa de dependências

O agrupamento e a inibição só funcionam com etiquetas, tal como qualquer motor de correlação, com ou sem machine learning. Acorda com a tua equipa um conjunto que todos os alertas têm de ter:

  • service: o que está avariado, com o nome que a equipa responsável lhe dá
  • team: quem recebe a notificação; um alerta sem equipa não tem dono
  • env e cluster (ou região): onde está a acontecer
  • severity: critical, warning ou info, cada uma com um único significado acordado

Põe cluster e env em global.external_labels do Prometheus, que o Prometheus acrescenta a todos os alertas que envia ao Alertmanager. Põe team e severity no bloco labels: de cada regra, e faz a CI falhar em qualquer regra a que falte uma delas: o promtool check rules cobre a sintaxe e um script curto cobre as etiquetas. Tornar isto coerente entre clusters é a maior parte do trabalho de uma limpeza de observabilidade.

Depois, escreve as dependências, que é aquilo de que a correlação topológica precisa. Para começar, basta um ficheiro no Git que diga o que cada serviço chama e onde corre, e a partir dele derivas uma etiqueta system, tão obrigatória como as outras, que liga uma base de dados às aplicações que a usam.

Um exemplo prático. Imagina que a orders-db, um primário PostgreSQL, faz failover. Em poucos minutos podes receber o alerta da base de dados, dois alertas de atraso de réplica, três alertas (taxa de erros, latência, readiness probe a falhar) de cada um dos seis serviços que a usam, quatro alertas de fila acumulada, dois alertas de burn rate do SLO e uma verificação sintética do checkout falhada. São 28 notificações para uma única causa.

Com o mapa convertido em etiquetas, o mesmo evento produz um único aviso. Tudo o que pertence ao sistema de encomendas tem system="orders", por isso a rota agrupa-o numa só notificação, e a regra de inibição da base de dados silencia os alertas das aplicações enquanto o PostgresDown estiver ativo. Dá aos teus alertas de SLO o seu próprio tier, por exemplo tier="slo", para que essa regra nunca os silencie: dizem-te que os clientes estão afetados, algo que o alerta da base de dados não consegue dizer.

Onde o machine learning ajuda e onde não ajuda

Quando os alertas estão etiquetados, têm dono e estão agrupados, resta um conjunto mais pequeno de problemas em que um modelo treinado faz melhor do que uma regra fixa.

Onde ajuda

Métricas sem um bom limiar estático. O volume de encomendas ou a profundidade de uma fila podem ser normais às 03:00 e alarmantes ao mesmo nível às 11:00. Experimenta uma comparação sazonal antes de recorreres a um modelo (orders_total representa qualquer contador de negócio que exportes):

promql
sum(rate(orders_total[30m]))
  < 0.5 * sum(rate(orders_total[30m] offset 1w))

Dispara quando a última meia hora corre a menos de metade do ritmo da mesma meia hora uma semana antes. Falha nos feriados e depois de grandes lançamentos, e é aí que um modelo que aprende vários padrões sazonais justifica o seu lugar. O mesmo vale para o próprio fluxo de alertas: um serviço que normalmente gera um par de alertas por dia e de repente gera cinquenta está a dizer-te alguma coisa, mesmo que cada um seja apenas um warning.

Correlacionar alertas com alterações. A primeira pergunta em qualquer chamada de incidente é o que mudou. Faz com que o teu pipeline registe deploys, alterações de configuração, mudanças de feature flags e rotações de certificados como eventos com as mesmas etiquetas service e env dos teus alertas, e mostra em cada incidente as alterações recentes ao serviço afetado e às suas dependências. Um modelo pode depois aprender que alterações costumam preceder que alertas e ordenar os suspeitos.

Padrões recorrentes. Os mesmos alertas todos os domingos às 02:00 enquanto corre o backup. Agrupar incidentes passados pela impressão digital dos seus alertas permite ao sistema dizer "isto parece o incidente do mês passado, e foi isto que o resolveu". Isso exige histórico com etiquetas coerentes e resultados registados, que é precisamente o que o passo de medição começa a construir.

Onde não ajuda

  • Não consegue correlacionar alertas que não partilham etiquetas. Se service se escreve de três formas, o modelo vê três serviços.
  • Não consegue encaminhar um alerta sem dono, nem aprender com feedback que ninguém dá.
  • Não consegue corrigir um mau alerta. Uma regra de CPU acima de 80 que nunca importou acaba aprendida como ruído e suprimida, como os teus engenheiros já fazem à mão. Elimina-a.
  • Não deve ser uma caixa negra. Se quem está de on-call não vê porque é que os alertas foram agrupados ou suprimidos, deixa de confiar no agrupamento e volta a ler todos os alertas.

Os alertas de SLO baseados em sintomas são a solução a longo prazo

Tudo o que vem antes reduz o ruído dos alertas baseados em causas. A solução duradoura é avisar por menos deles: avisa quando os utilizadores são afetados, medido face a um objetivo de nível de serviço (SLO), e envia causas como CPU, memória e reinícios de pods para tickets e dashboards.

Os alertas de burn rate com várias janelas tornam isto prático. Com um objetivo de disponibilidade de 99,9% a 30 dias, o teu orçamento de erros é 0,1% dos pedidos. Um burn rate de 1 gasta esse orçamento em exatamente 30 dias, ou seja, 720 horas. Queimar a 14,4 vezes esse ritmo durante uma hora gasta 14,4 / 720 = 2% do orçamento do mês, o que justifica acordar alguém:

promql
(
  sum(rate(http_requests_total{service="checkout", code=~"5.."}[1h]))
  /
  sum(rate(http_requests_total{service="checkout"}[1h]))
) > (14.4 * 0.001)
and
(
  sum(rate(http_requests_total{service="checkout", code=~"5.."}[5m]))
  /
  sum(rate(http_requests_total{service="checkout"}[5m]))
) > (14.4 * 0.001)

A janela de uma hora prova que o consumo é sustentado; a de cinco minutos faz com que o alerta desapareça pouco depois da recuperação. Um par mais lento, como um burn rate de 6 em seis horas (6 × 6 / 720 = 5% do orçamento), apanha fugas lentas e pode abrir um ticket em vez de avisar. Ajusta os nomes das métricas ao que os teus serviços exportam, e define os objetivos com as pessoas responsáveis pelo serviço, não por elas; essa conversa é o centro do nosso trabalho de consultoria SRE.

Automatiza os runbooks, com salvaguardas

Alguns alertas têm uma resposta conhecida e segura. Automatizá-la elimina um aviso; automatizar a coisa errada provoca uma falha à velocidade da máquina.

Podes automatizar ações idempotentes e reversíveis sobre um único alvo com uma causa bem compreendida, como reiniciar um worker com uma fuga de memória conhecida e com ticket aberto, escalar dentro de um máximo predefinido, rodar logs num volume que está a encher, renovar um certificado ou fazer cordon a um nó que reporta erros de hardware.

Deixa nas mãos de pessoas tudo o que apague dados, a promoção ou o failover de uma base de dados, rollbacks de esquema, alterações a regras de firewall ou políticas de acesso, ações que abranjam mais do que um domínio de falha e qualquer caso em que o diagnóstico seja ambíguo.

As salvaguardas importam mais do que os scripts:

  • Primeiro em dry run. Nas primeiras semanas, a automação só publica o que teria feito, para comparares com o que o engenheiro fez.
  • Limites de frequência. Limita as execuções por alvo, por exemplo três por hora, e a partir daí para e avisa. Uma correção automática que corre todos os dias está a esconder um bug, por isso cada execução abre também um ticket.
  • Auditoria e botão de paragem. Cada ação fica registada junto ao seu alerta, e uma única flag desativa toda a automação.
  • Etiquetas como filtro. A automação só atua sobre alertas que tenham service, team e uma anotação runbook_url.

Liga-a como um receiver webhook do Alertmanager: o Alertmanager envia JSON com as etiquetas e anotações de cada alerta, e um pequeno serviço associa os valores conhecidos de alertname a ações aprovadas.

Um plano de 90 dias

Começa pelo teu serviço ou equipa mais ruidosos, prova o resultado aí e depois alarga.

Mês 1 (dias 1-30): medir e limpar

  • Um inventário de alertas: cada regra com o seu dono, severidade, ligação ao runbook, tempo em disparo e taxa de ação
  • O padrão de etiquetas escrito e imposto na CI
  • As regras sem dono ou que nunca levaram a uma ação, eliminadas ou despromovidas com o acordo da equipa responsável
  • As regras instáveis corrigidas com durações for: e keep_firing_for

Mês 2 (dias 31-60): agrupar, inibir, encaminhar

  • Uma árvore de encaminhamento por equipa e severidade, agrupada por sistema
  • Regras de inibição para as tuas três maiores cascatas, como do nó para os pods e da base de dados para as aplicações
  • Silêncios criados pelo pipeline, não à mão
  • O mapa de dependências no Git para os serviços abrangidos
  • Alertas de burn rate para dois ou três percursos de cliente, com os alertas de causa correspondentes despromovidos
  • As consultas do primeiro mês repetidas e comparadas com a linha de base, a par dos avisos por turno de on-call

Mês 3 (dias 61-90): acrescentar inteligência onde compensa

  • Linhas de base sazonais nas métricas que não têm um limiar estático utilizável
  • Eventos de alteração do pipeline associados a cada incidente
  • Um a três runbooks automatizados, em modo dry run antes de atuarem
  • Uma decisão escrita de comprar, construir ou abandonar uma camada de correlação com machine learning, com base no que continua a fazer ruído e no histórico etiquetado que agora tens

Por onde começar

O agrupamento, a inibição, as etiquetas e os alertas de SLO não são glamorosos, mas funcionam com as ferramentas que já usas e decidem se qualquer modelo que acrescentes depois terá alguma coisa útil com que aprender. A nossa equipa de operações NOC faz a triagem de alertas 24 horas por dia e monta esta configuração com os engenheiros responsáveis pelos serviços.

A avaliação gratuita do ruído de alertas revê os teus alertas e avisos dos últimos 30 dias, as tuas regras de encaminhamento e inibição do Alertmanager e a coerência das tuas etiquetas entre clusters, e devolve-te o inventário do primeiro mês com as três cascatas que mais vale a pena corrigir primeiro.