Alerting che non fa rumore: Prometheus e Alertmanager in un homelab
Come progettare alert utili in un homelab: sintomi invece di cause, soglie con for e keep_firing_for, raggruppamento e inibizione in Alertmanager, finestre di manutenzione e metriche di backup con il textfile collector.
Raccogliere metriche è la parte facile. La parte difficile è decidere quando quelle metriche devono interrompere la giornata di qualcuno. In molti homelab il problema non è la mancanza di monitoraggio: è l'eccesso di notifiche. Dopo due settimane di alert notturni su un picco di CPU al 92% per quaranta secondi, la reazione naturale è silenziare il canale. A quel punto il monitoraggio esiste ancora, ma non serve più a nulla.
Questo articolo descrive un impianto di alerting a basso rumore con Prometheus e Alertmanager, con regole, comandi e criteri verificabili. Niente dashboard decorative: solo quello che serve per far arrivare l'avviso giusto, alla persona giusta, nel momento in cui è utile.
Alert sul sintomo, non sulla causa
La prima scelta di progettazione è cosa misurare. Un alert dovrebbe descrivere un sintomo percepibile dall'utente o un requisito operativo violato, non una condizione interna del sistema.
- Rumore:
cpu_usage > 80%. Un host che compila pacchetti o fa un backup satura la CPU per minuti; non è un problema. - Utile:
servizio_X_down,backup_non_riuscito_da_36h,endpoint_pubblico_non_risponde. - Utile:
pieno_utilizzo_disco_al_90%con margine reale di intervento, non all'85% "per sicurezza".
Un criterio pratico: se l'alert scatta, deve esistere un'azione concreta da compiere oppure la regola va cancellata. Se non sai cosa fare quando suona, la regola sta solo addestrando chi la riceve a ignorarla.
for e keep_firing_for: distinguere un picco da un incidente
Prometheus permette di richiedere che la condizione resti vera per un certo tempo prima di generare l'alert. keep_firing_for (disponibile nelle versioni recenti di Prometheus) mantiene invece l'alert attivo per una coda di secondi dopo che la condizione è rientrata, evitando il classico lampeggio intermittente durante un riavvio dei servizi.
groups:
- name: nodi
interval: 30s
rules:
- alert: NodoNonRaggiungibile
expr: up{job="node"} == 0
for: 5m
keep_firing_for: 5m
labels:
severity: critical
annotations:
summary: "Nodo {{ $labels.instance }} non raggiungibile"
description: "up == 0 da 5 minuti: verificare alimentazione, rete e accesso out-of-band."
Il parametro for è la prima difesa contro il rumore. Scegli in base al tempo di reazione ragionevole: cinque minuti per un host spento, dieci per un disco che si riempie, un'ora per un certificato in scadenza.
Per i servizi esposti conviene lavorare su un indicatore di errore aggregato, coerente con un ragionamento da error budget:
- record: job:sli_error_ratio:rate5m
expr: |
sum(rate(http_requests_total{code=~"5.."}[5m])) by (job)
/
sum(rate(http_requests_total[5m])) by (job)
- alert: ErrorBudgetBruciato
expr: job:sli_error_ratio:rate5m > 0.01
for: 10m
labels:
severity: warning
annotations:
summary: "Oltre l'1% di errori 5xx su {{ $labels.job }}"
Alertmanager: raggruppare prima di notificare
Alertmanager non decide se un alert esiste, decide come e quando inviarlo. Le tre manopole che contano sono il raggruppamento, la finestra di attesa e la cadenza di ripetizione.
route:
receiver: default
group_by: [alertname, instance]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- matchers: [severity="critical"]
receiver: oncall
repeat_interval: 1h
- matchers: [severity="warning"]
receiver: default
repeat_interval: 12h
receivers:
- name: default
webhook_configs:
- url: http://127.0.0.1:8060/notify
- name: oncall
webhook_configs:
- url: http://127.0.0.1:8060/notify
Tre dettagli che fanno la differenza:
group_bysenza il nome dell'alert rischia di accorpare avvisi scollegati in un unico messaggio confuso. Tienilo esplicito.group_waitbreve per i critical (30 secondi) evita che un riavvio di nodo generi venti notifiche separate.repeat_intervallungo sulle warning. Un warning che si ripete ogni quindici minuti viene letto come rumore anche quando è reale.
Inibizione: un alert che ne silenzia altri
Quando un nodo va giù, gli alert sui servizi che girano su quel nodo sono conseguenze, non cause. Le regole di inibizione evitano di ricevere dieci notifiche per un singolo guasto.
inhibit_rules:
- source_matchers: [severity="critical"]
target_matchers: [severity="warning"]
equal: [alertname, instance]
Il campo equal va scelto con attenzione: se inibisci tutto ciò che condivide l'instance, un host spento silenzia correttamente i suoi servizi; se inibisci su etichette troppo generiche rischi di sopprimere incidenti indipendenti.
Manutenzione pianificata e silence operative
Le finestre di manutenzione ricorrenti (finestre di backup, aggiornamenti settimanali) hanno un posto dedicato in Alertmanager:
mute_timeouts:
- name: finestra-backup
time_intervals:
- times:
- start_time: '01:30'
end_time: '03:30'
weekdays: ['saturday', 'sunday']
Per interventi una volta sola, il silence via amtool è più pratico e lascia traccia:
amtool silence add alertname=NodoNonRaggiungibile \
--duration=2h --comment="riavvio programmato nodo pve"
Metriche che nessun exporter produce: il textfile collector
I backup sono il classico caso in cui il fallimento è silenzioso: lo script gira alle 03:00, fallisce, e nessuno se ne accorge fino al giorno del ripristino. Il textfile collector di node_exporter copre questo vuoto senza scrivere un exporter dedicato.
Abilita il collector e fai scrivere lo script di backup in formato Prometheus:
node_exporter --collector.textfile.directory=/var/lib/node_exporter/textfile_collector
backup_last_success_timestamp_seconds 1758950400
backup_failed 0
La regola che trasforma il dato in un avviso utile:
- alert: BackupNonRiuscito
expr: time() - backup_last_success_timestamp_seconds > 129600
for: 15m
labels:
severity: warning
annotations:
summary: "Nessun backup riuscito da più di 36 ore"
Verifica prima del deploy
Una regola di alerting errata è peggio di una regola assente, perché fallisce in silenzio. Prima di ricaricare:
promtool check rules /etc/prometheus/rules/*.yml
amtool check-config /etc/alertmanager/alertmanager.yml
curl -s localhost:9090/api/v1/rules | jq '.data.groups[].rules[] | select(.state=="firing") | .name'
Checklist
- Ogni alert ha un'azione associata; se non ce l'ha, va rimosso.
- Le condizioni hanno un
forproporzionato al tempo di reazione. - I critical sono raggruppati e ripetuti più spesso delle warning, non tutti insieme.
- Esistono regole di inibizione per le conseguenze di un guasto del nodo.
- Le finestre pianificate sono silenziate in modo tracciabile, non con alert disattivati a mano.
- Il backup ha una metrica di esito e un alert sulla sua assenza.
- Le regole passano
promtoole la configurazione passaamtoolprima del reload.
Un impianto di alerting si giudica da quanto è credibile: se ogni notifica che arriva è utile, chi la riceve reagisce. Tutto il resto è configurazione.