Files
athletic-map-deploy/platform/monitoring/athleticmap-rules.yaml
T

165 lines
8.8 KiB
YAML

# =============================================================================
# Regras de alerta do Athletic Map.
#
# ── LIMITE QUE VALE SABER (2026-09-20) ──────────────────────────────────────
# O cluster NAO tem Alertmanager: estas regras DISPARAM no Prometheus (aparecem
# em Alerts, e no Grafana), mas ninguem e avisado por e-mail ou mensagem. Enquanto
# o Alertmanager nao existir, alerta so e visto por quem abre a tela. Esta
# registrado no diario como pendencia propria.
# =============================================================================
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: athleticmap-rules
namespace: monitoring
labels:
release: monitoring # o Prometheus so carrega regras com este label
spec:
groups:
- name: athleticmap.backup
rules:
# 26h: o backup roda 1x por dia (02:00 nos tenants, 02:30 no compartilhado).
# Duas horas de folga evitam alarme por atraso de agendamento.
#
# O `pg-backup.*` cobre os DOIS: o `pg-backup` de cada tenant (que ainda
# salva o Postgres antigo dele) e o `pg-backup-compartilhado`, que salva
# os bancos `athleticmap_*` da instancia compartilhada. Antes a regra
# citava `cronjob="pg-backup"` e o compartilhado — o backup dos tres
# tenants migrados — ficava SEM vigilancia nenhuma.
- alert: AthleticMapBackupStale
expr: time() - kube_cronjob_status_last_successful_time{cronjob=~"pg-backup.*"} > 93600
for: 15m
labels:
severity: warning
annotations:
summary: "Backup {{ $labels.cronjob }} ({{ $labels.namespace }}) sem sucesso ha mais de 26h"
description: "Ver: kubectl -n {{ $labels.namespace }} get job -l job-name --sort-by=.metadata.creationTimestamp"
# CronJob que existe e NUNCA teve uma execucao bem-sucedida. A regra
# acima nao pega este caso: sem sucesso nenhum, a metrica nem existe.
# Foi o que aconteceu com o compartilhado em 2026-09-19 (a 1a copia
# falhou por senha com quebra de linha, e o script disse "PUBLICADO").
- alert: AthleticMapBackupNuncaRodou
expr: |
kube_cronjob_spec_suspend{cronjob=~"pg-backup.*"} == 0
unless on (namespace, cronjob) kube_cronjob_status_last_successful_time
for: 6h
labels:
severity: critical
annotations:
summary: "Backup {{ $labels.cronjob }} ({{ $labels.namespace }}) NUNCA rodou com sucesso"
description: "Backup que nunca rodou nao e backup. Rode a copia na hora e veja o log do Job."
# Job de backup que terminou em falha (inclusive a `primeira-rodada` que
# os scripts criam). `kube_job_status_failed` conta tentativas falhas.
- alert: AthleticMapBackupJobFalhou
expr: kube_job_status_failed{job_name=~"pg-backup.*|primeira-rodada.*"} > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Job de backup {{ $labels.job_name }} ({{ $labels.namespace }}) falhou"
description: "kubectl -n {{ $labels.namespace }} logs job/{{ $labels.job_name }}"
- name: athleticmap.health
rules:
# O `unless` tira os pods de Job que TERMINARAM: pod concluido nao e
# "Ready", e sem isso todo backup bem-sucedido acendia um alerta
# (conferido em 2026-09-20 contra o Prometheus: a versao antiga casava
# com os pods do `pg-backup` do dia).
- alert: AthleticMapTenantPodNotReady
expr: |
kube_pod_status_ready{namespace=~".*-prod", condition="true"} == 0
unless on (namespace, pod) kube_pod_status_phase{phase=~"Succeeded|Failed"} == 1
for: 10m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} em {{ $labels.namespace }} nao-Ready ha mais de 10m"
description: "Comece pelo motivo, que costuma estar no evento e nao no log: kubectl -n {{ $labels.namespace }} describe pod {{ $labels.pod }} | tail -20. CrashLoop com imagem recem-trocada e quase sempre a subida do Spring — veja kubectl logs --previous."
# O no e UNICO e tem limite de 110 pods. Em 2026-09-19 a consolidacao
# parou porque nao havia vaga ("Too many pods"), e o sintoma so aparecia
# no evento do pod Pending. 95% = 104 pods.
- alert: AthleticMapNoQuaseSemVagaDePod
expr: |
sum(kube_pod_status_phase{phase=~"Running|Pending"})
/ sum(kube_node_status_allocatable{resource="pods"}) > 0.95
for: 15m
labels:
severity: warning
annotations:
summary: "O no esta com mais de 95% das vagas de pod ocupadas"
description: "Subir algo 'ao lado' pode ficar Pending. Consolidar tenants libera 10 vagas cada."
- name: athleticmap.provisionamento
rules:
# O teto de execucoes por hora existe porque o provisionamento IMPLANTA:
# um pedido que volta para a fila viraria um laco que publica tenant atras
# de tenant. Quando ele segura pedidos, o log registrava e mais nada — "um
# laco as tres da manha enche o log e ninguem ve".
- alert: AthleticMapProvisionamentoTetoAtingido
expr: increase(atm_provisionamento_teto_atingido_total[15m]) > 0
for: 0m
labels:
severity: warning
annotations:
summary: "O teto de provisionamentos por hora segurou pedidos"
description: "Ou entraram muitos clientes de uma vez, ou ha um laco. Veja provisionamento/execucao no banco do plataforma."
# P5.8 — o pedido que FALHOU. Esta e a regra que faltava, e a que a tela
# de espera pressupoe: ela diz ao cliente "nossa equipe ja foi avisada", e
# ate 2026-09-21 ninguem era avisado de nada.
#
# `> 0` e nao um patamar: cada falha e um cliente que pagou e esta olhando
# a tela de espera. Nao existe volume aceitavel disto.
#
# 10m (e nao 15m como as de cima) porque aqui alguem espera do outro lado.
- alert: AthleticMapProvisionamentoFalhou
expr: increase(atm_provisionamento_falhou_total[10m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "Provisionamento falhou — ha cliente pago esperando tenant"
description: "Veja a execucao e a etapa: kubectl -n plataforma-prod exec deploy/pg-compartilhado -- psql -U postgres -d athleticmap -c \"select * from captacao.execucao_provisionamento where situacao='FALHOU' order by criado_em desc limit 5\""
# Execucao que EXPIROU e diferente de falhar: ninguem devolveu a posse —
# tipicamente o pod morreu no meio. O pedido volta para a fila sozinho, e
# por isso e warning; mas expiracao repetida quer dizer que algo mata o pod
# sempre no mesmo ponto, e ai a fila anda para tras.
- alert: AthleticMapProvisionamentoExpirou
expr: increase(atm_provisionamento_expirou_total[1h]) > 2
for: 0m
labels:
severity: warning
annotations:
summary: "Tres ou mais execucoes de provisionamento expiraram em 1h"
description: "A posse venceu sem o dono devolver: o pod morreu no meio da etapa. Veja restarts em kubectl -n plataforma-prod get pods -l app=servico-captacao."
# Webhook recusado e evento que o Asaas mandou e nos rejeitamos: token
# errado (configuracao) ou alguem batendo na rota (tentativa). Um ou outro
# acontece; uma rajada, nao.
- alert: AthleticMapWebhookAssinaturaRecusado
expr: increase(atm_webhook_assinatura_recusado_total[15m]) > 5
for: 0m
labels:
severity: warning
annotations:
summary: "Mais de 5 eventos de assinatura recusados em 15 min"
description: "Token do webhook trocado sem atualizar o SealedSecret, ou alguem batendo na rota publica."
- name: athleticmap.banco
rules:
# O Postgres compartilhado e o PgBouncer atendem TRES tenants: parada
# neles nao e incidente de um cliente, e de todos.
- alert: AthleticMapCompartilhadoFora
expr: kube_deployment_status_replicas_available{namespace="plataforma-prod",
deployment=~"pg-compartilhado|pgbouncer"} < 1
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.deployment }} sem replica disponivel — demo, piloto e acme dependem dele"
description: "Os tres tenants migrados param juntos. kubectl -n plataforma-prod get pods -l app={{ $labels.deployment }} e, se for o PgBouncer, lembre que a userlist e montada por subPath e so entra em vigor com restart."