Português (BR)
OpsCanopy 11 min de leitura
Como funciona o roteamento do Alertmanager: matchers, continue e a árvore de rotas
Um modelo mental claro para o roteamento do Alertmanager — a árvore de rotas, os matchers, a flag continue, o agrupamento e a herança de receivers — para você saber exatamente para onde cada alerta vai.
- alertmanager
- observability
- alerting
Nesta página
- Roteamento é uma árvore, não uma lista
- Como uma rota corresponde: a sintaxe matchers (e os antigos match/match_re)
- Correspondência em profundidade e continue: o primeiro irmão correspondente vence, a menos que continue seja true
- Agrupamento: group_by, group_wait, group_interval, repeat_interval
- Herança: rotas filhas herdam receiver e group_by do pai
- Lendo uma árvore de rotas real: onde um determinado alerta cai
- Teste sua árvore
Um alerta severity=critical disparou ontem à noite e o time de plantão nunca foi acionado. O alerta era real, o receiver existia, o webhook do Slack funcionava. O problema estava três linhas acima na configuração: uma rota catch-all abrangente ficava acima da rota do time e engolia silenciosamente tudo que chegava até ela. Ninguém mexeu no receiver — mexeram na ordem.
É isso que torna o roteamento do Alertmanager tão fácil de errar. Os receivers geralmente estão corretos. É na árvore de rotas que moram as surpresas. Quando você tem um modelo preciso de como a árvore de rotas é percorrida — como os matchers são avaliados, quando o continue mantém um alerta em movimento e o que cada filho herda do seu pai — “por que esse alerta foi parar ali?” deixa de ser um jogo de adivinhação. Este post constrói esse modelo, e cada regra aqui corresponde ao que o Alertmanager Route Tester realmente faz quando percorre uma árvore com um alerta de exemplo.
Roteamento é uma árvore, não uma lista
A leitura equivocada mais comum de uma configuração do Alertmanager é tratar routes: como uma lista plana de regras contra as quais cada alerta é verificado. Não é uma lista. É uma árvore, e todo alerta entra no mesmo lugar: a rota raiz.
route:
receiver: 'default-receiver' # the root — the catch-all
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes: # child routes
- receiver: 'team-X-mails'
match:
team: frontend
- receiver: 'team-DB-pages'
match:
service: database
- receiver: 'team-Y-mails'
match:
team: backend
A rota raiz é especial: ela é acessada por todos os alertas, independentemente dos seus próprios matchers. É a catch-all. Seu receiver é o destino padrão onde um alerta cai quando nada mais específico corresponde, e seus campos de agrupamento são a base que tudo abaixo herda. Dentro da raiz fica uma lista routes: — seus filhos. Cada filho pode ter seu próprio routes:, e assim por diante árvore abaixo. Um alerta desce a partir da raiz por meio de qualquer filho que corresponda, e o receiver no qual ele acaba é o do nó onde a descida para.
Então, quando você ler um alertmanager.yml, não percorra a lista de rotas procurando a regra que corresponde. Comece na raiz e desça. A árvore de rotas do Alertmanager é uma árvore de decisão que você percorre de cima para baixo, em profundidade.
Como uma rota corresponde: a sintaxe matchers (e os antigos match/match_re)
Um nó de rota corresponde a um alerta quando todos os seus próprios matchers se aplicam aos labels do alerta. AND lógico, sem exceções. Um nó sem matchers sempre corresponde. Há três formas de declarar esses matchers do Alertmanager, e você verá as três em configurações reais.
routes:
# Modern matchers: syntax — preferred. One operator per line.
- receiver: 'staging-slack'
matchers:
- env=~"staging-.*" # =~ regex
- severity!="info" # != inequality
# Older match: exact string equality on each key.
- receiver: 'team-X-mails'
match:
team: frontend
# Older match_re: each value is a regex.
- receiver: 'prod-pager'
match_re:
env: 'prod-.*'
A forma moderna matchers: carrega o operador inline. São quatro: = (igual a), != (diferente de), =~ (regex corresponde) e !~ (regex não corresponde). Os valores podem estar entre aspas ou sem aspas. As duas formas mais antigas são açúcar sintático sobre o mesmo mecanismo — match: é um conjunto de matchers =, e match_re: é um conjunto de matchers =~.
Dois detalhes confundem as pessoas o tempo todo:
- As regexes são totalmente ancoradas. O Alertmanager envolve todo padrão
=~,!~ematch_recomo^(?:…)$. Entãoenv=~"staging"corresponde ao valorstaginge a mais nada —env=staging-eunão corresponde. Você precisa escreverenv=~"staging-.*"para cobrir o restante do valor. Essa é a causa mais frequente de “minha rota não corresponde a nada”. - Um label ausente é a string vazia. O Alertmanager compara um label ausente como
"". Entãofoo=""corresponde a um alerta que não tem nenhum labelfoo, efoo!=""exige quefooesteja presente e não vazio. Útil e, de vez em quando, surpreendente.
Colocar esses labels no alerta em primeiro lugar é um trabalho à parte, que acontece no momento do scrape — se o label que seu matcher verifica nunca foi definido, rastreie de volta até a sua configuração de scrape com o Prometheus Relabel Tester antes de culpar a árvore de rotas.

Correspondência em profundidade e continue: o primeiro irmão correspondente vence, a menos que continue seja true
Aqui está a regra que o exemplo da madrugada quebrou. Dentro de uma rota correspondente, as rotas filhas são avaliadas em ordem, de cima para baixo. O alerta desce para o primeiro filho cujos matchers se aplicam por completo — e então, por padrão, a varredura dos irmãos para. Os irmãos seguintes nem chegam a ser verificados.
# TRAP: the broad rule above shadows the specific one
routes:
- receiver: catch-all
matchers: ['severity=~".*"'] # matches everything
- receiver: db-pager # NEVER reached
match: { service: database }
Um alerta service=database, severity=critical atinge catch-all primeiro, essa correspondência para a varredura, e db-pager vira código morto. A correção é ou ordenar do específico para o abrangente, ou definir continue: true.
continue: true em uma rota correspondente diz ao Alertmanager para não parar a varredura dos irmãos depois que aquela rota corresponde. A avaliação prossegue para os irmãos seguintes, cada um dos quais também pode corresponder. Essa é a única maneira de um único alerta cair em mais de um receiver.
# Mirror every critical alert to an audit receiver,
# THEN keep routing so the owning team is still paged.
routes:
- receiver: all-critical-audit
matchers: ['severity="critical"']
continue: true # <- do not stop here
- receiver: team-backend
match: { team: backend }
Para um alerta team=backend, severity=critical, a primeira rota corresponde e normalmente pararia a varredura — mas continue: true a mantém viva, a segunda rota também corresponde, e ambos os receivers disparam. Remova o continue e apenas all-critical-audit dispara; o time nunca fica sabendo.
A varredura é em profundidade: quando um filho corresponde, o alerta desce para a subárvore daquele filho e se resolve ali antes que qualquer continue o leve ao próximo irmão. O Alertmanager Route Tester marca cada receiver que foi alcançado apenas porque um irmão anterior definiu continue: true, para que você veja num relance quais correspondências são o caminho principal e quais são fan-out.
Agrupamento: group_by, group_wait, group_interval, repeat_interval
O roteamento decide para onde um alerta vai. O agrupamento decide como suas notificações são agrupadas e ritmadas depois que ele chega lá. Quatro campos controlam isso, e eles ficam nos nós de rota, lado a lado com os matchers.
route:
receiver: 'default-receiver'
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s # wait this long to collect more alerts for a new group
group_interval: 5m # then wait this long before sending updates to that group
repeat_interval: 4h # re-send an unresolved group no more often than this
group_byé a lista de labels que define um grupo. Alertas que compartilham os mesmos valores para esses labels são reunidos em uma única notificação. Um caso especial comum égroup_by: ['...'], que agrupa por todos os labels (cada alerta distinto é seu próprio grupo), e a ausência de agrupamento junta tudo em um único grupo.group_waité por quanto tempo o Alertmanager segura um grupo recém-criado antes de enviar a primeira notificação, para que uma rajada de alertas relacionados chegue como um único acionamento em vez de vinte.group_intervalé o intervalo mínimo antes de ele enviar uma notificação atualizada para um grupo que já disparou (por exemplo, quando um novo alerta entra no grupo).repeat_intervalé com que frequência ele renotifica sobre um grupo que ainda está disparando e não resolvido.
Esses campos fazem a diferença entre um único acionamento útil e uma tempestade de alertas. E, crucialmente — eles são herdados.
Herança: rotas filhas herdam receiver e group_by do pai
Uma rota filha não precisa repetir o receiver e o agrupamento que deseja. Tudo que ela não define é herdado do ancestral mais próximo que definiu. Isso é por campo: um filho pode sobrescrever group_by enquanto ainda herda group_wait, group_interval, repeat_interval e até receiver.
route:
receiver: 'default-receiver'
group_by: ['alertname', 'cluster']
group_wait: 30s
repeat_interval: 4h
routes:
- receiver: 'team-DB-pages'
match:
service: database
group_by: ['alertname', 'cluster', 'database']
# group_wait and repeat_interval are INHERITED from the root:
# group_wait: 30s, repeat_interval: 4h
routes:
- match:
severity: critical
# No receiver set here, so it INHERITS 'team-DB-pages'.
# No group_by set, so it INHERITS [alertname, cluster, database].
O nó mais profundo dessa árvore não define nem um receiver nem group_by, mas um alerta service=database, severity=critical que chega até ele aciona team-DB-pages e agrupa por [alertname, cluster, database] — ambos puxados pela cadeia. É por isso que a folha que você está olhando pode não contar a história toda: o receiver e o agrupamento efetivos são montados percorrendo para cima a partir do nó correspondente até o primeiro ancestral que definiu cada campo. Quando você for depurar um alerta mal roteado ou mal agrupado, resolva a herança, não apenas a folha.
Lendo uma árvore de rotas real: onde um determinado alerta cai
Juntando tudo. Aqui está uma árvore completa com três filhos no nível superior e uma subárvore aninhada sob um deles.
route:
receiver: 'default-receiver'
group_by: ['alertname']
group_wait: 30s
repeat_interval: 4h
routes:
- receiver: 'all-critical-audit'
matchers:
- severity="critical"
continue: true # mirror, then keep going
- receiver: 'web-team'
match:
service: web
group_by: ['alertname', 'instance']
routes:
- receiver: 'web-team-pager'
matchers:
- severity="critical"
- receiver: 'web-team-slack'
matchers:
- severity=~"warning|info"
- receiver: 'team-Y-mails'
match:
team: backend
Agora rastreie um alerta com estes labels:
alertname=Latency
service=web
severity=critical
instance=web-3
Percorrendo a árvore, em profundidade, em ordem:
- A raiz é acessada (sempre). Ela não para aqui; tem filhos para avaliar.
- Primeiro filho,
all-critical-audit:severity="critical"se aplica. Corresponde →all-critical-auditdispara. Temcontinue: true, então a varredura não para. - Segundo filho,
web-team:service: webse aplica. O alerta desce para sua subárvore.- Primeiro neto,
web-team-pager:severity="critical"se aplica →web-team-pagerdispara. Semcontinue, então este ramo para aqui. Ogroup_byefetivo é[alertname, instance], herdado deweb-team.
- Primeiro neto,
- A correspondência de
web-team(uma correspondência semcontinue) para a varredura do nível superior, entãoteam-Y-mailsnunca é avaliado.
Resultado final: o alerta alcança dois receivers — all-critical-audit (via continue) e web-team-pager (o caminho principal). Mude severity para warning e o quadro muda: all-critical-audit sai de cena, e dentro de web-team o alerta cai em web-team-slack. Remova service=web e ele nunca entra naquela subárvore, caindo em team-Y-mails se team=backend, ou no default-receiver da raiz se nada corresponder.
Se as suas próprias regras de alerta não estiverem disparando como você espera — labels errados, severidade errada, timing errado — isso é a montante do roteamento por completo; comprove a regra primeiro com o AlertLint e depois rastreie onde a saída dela cai aqui.
Teste sua árvore
Você pode fazer esse percurso à mão e, para uma árvore de três nós, vale a pena fazer uma vez para internalizar o modelo. Mas árvores reais aninham cinco níveis de profundidade, misturam match, match_re e matchers, e espalham continue entre os irmãos — e o custo de errar é um SEV-1 que não aciona ninguém, ou um warning rotineiro que acorda o time inteiro.
Então torne barato verificar. Cole sua árvore de rotas e os labels de um alerta de exemplo no Alertmanager Route Tester e ele faz exatamente o percurso acima — inteiramente no seu navegador, sem nada enviado. Ele relata cada receiver que o alerta alcança na ordem de avaliação, o breadcrumb do caminho da rota desde a raiz até cada nó correspondente, uma marca em qualquer receiver alcançado apenas via continue: true, e o group_by efetivo após a herança. Ele reproduz a semântica que este post descreve: regexes ancoradas, label-ausente-como-string-vazia, primeira-correspondência-depois-continue, e herança por campo.
Na próxima vez que um alerta cair em um lugar inesperado, você não precisa disparar um de verdade e ficar observando. Cole a árvore, cole os labels e leia o caminho que ele de fato percorreu.
Posts relacionados
-
13 min de leitura
Por Que Meu Alerta Não Chega ao Receiver Certo? Depurando o Roteamento do Alertmanager
Alertas indo para o receiver errado, ou para nenhum receiver? Depure o roteamento do Alertmanager — primeiro match vence, continue ausente, regex de matcher e defaults catch-all.
-
10 min de leitura
Por que o Prometheus descartou meu target? Depurando relabel_configs
Um target sumiu ou um label desapareceu após o relabeling. Depure relabel_configs vs metric_relabel_configs, ancoragem de regex e a lógica de keep/drop.
-
11 min de leitura
relabel_configs do Prometheus explicado: um guia prático
Entenda os relabel_configs do Prometheus de ponta a ponta — source_labels, regex, replacement e cada action (replace, keep, drop, labelmap, hashmod) — com receitas prontas para copiar e colar.