Avvisi di errore backup: scoprilo prima del ripristino

I backup falliscono in silenzio per settimane. Intercetta errori di restic e borg, snapshot vecchi ed esecuzioni saltate, e fatti chiamare.

Aggiornato

Indice

La maggior parte delle persone scopre che i backup erano rotti il giorno in cui servono. Il job è terminato con un errore tre settimane fa, nessuno ha letto il log, e lo snapshot più recente è più vecchio dell'errore che vuoi annullare.

La soluzione non è uno strumento di backup migliore. È fare in modo che un problema di backup raggiunga una persona nella stessa ora in cui si verifica. Questa guida mostra come farlo per restic e borg (e per qualsiasi cosa si lanci da shell), coprendo i tre modi in cui un backup fallisce davvero, con script da copiare. L'ultimo passo manda l'avviso al telefono tramite Echobell, così un backup rotto squilla invece di restare in una casella di posta.

I tre modi in cui un backup fallisce

Monitorare solo il primo è l'errore più comune.

  1. Parte e fallisce. Repository bloccato, disco pieno, password sbagliata, rete caduta. Lo strumento esce con un codice diverso da zero.
  2. Parte e riesce a metà. restic esce con il codice 3 quando lo snapshot è stato creato ma alcuni file non erano leggibili. borg esce con 1 per gli avvisi. Entrambi si liquidano facilmente, ed entrambi possono nascondere un problema di permessi che salta in silenzio la tua cartella più importante.
  3. Non parte mai. Il server era spento, il timer disabilitato, il crontab sovrascritto. Non c'è nessun errore perché non c'è nessun processo. È il guasto che fa più danni.

Ognuno richiede un rilevatore diverso, quindi li costruiremo tutti e tre.

Passo 1: crea un canale in grado di attirare la tua attenzione

In Echobell crea un canale chiamato backups e imposta il tipo di notifica su Chiamata per i job che contano. Copia il suo URL del webhook:

https://hook.echobell.one/t/<channel-token>

Dagli template che abbiano senso anche mezzo addormentati, usando i campi che invieremo qui sotto:

Titolo: Backup {{status}} su {{host}}
Corpo: {{job}}: {{detail}}

Consulta il riferimento dei template per la sintassi.

Passo 2: intercetta gli errori con uno script wrapper

Un piccolo wrapper trasforma i codici di uscita in avvisi. Salvalo come /usr/local/bin/backup-run:

#!/usr/bin/env bash
set -uo pipefail

HOOK="https://hook.echobell.one/t/<channel-token>"
JOB="nightly-restic"

alert() {
  curl -fsS -m 10 -X POST "$HOOK" \
    -H "Content-Type: application/json" \
    -d "{\"job\":\"$JOB\",\"host\":\"$(hostname)\",\"status\":\"$1\",\"detail\":\"$2\"}" || true
}

restic backup /srv /etc --exclude-caches
code=$?

case $code in
  0)  ;;                                   # successo: resta in silenzio
  3)  alert "incomplete" "Snapshot saved, but some files were unreadable" ;;
  11) alert "locked" "Repository is locked by another process" ;;
  12) alert "auth-failed" "Wrong repository password" ;;
  *)  alert "failed" "restic exited with code $code" ;;
esac

exit $code

Perché i dettagli contano:

  • set -e non è usato di proposito, così lo script raggiunge comunque l'avviso dopo che restic fallisce.
  • || true sul curl fa sì che un webhook irraggiungibile non mascheri mai il vero codice di uscita.
  • -m 10 impedisce a una chiamata di rete bloccata di intralciare la prossima esecuzione programmata.

I codici di uscita di restic usati sopra sono documentati nel manuale di restic: 0 successo, 1 errore fatale, 3 snapshot creato ma alcuni file sorgente illeggibili, 10 il repository non esiste, 11 blocco non riuscito, 12 password errata. Verificali con restic version sulla tua macchina, perché i codici sono stati aggiunti nel tempo.

borg e borgmatic

borg restituisce 0 per il successo, 1 per gli avvisi e 2 o più per gli errori. Un file che cambia durante il backup produce 1, cosa normale per le cartelle di log, quindi un approccio comune è chiamare solo da 2 in su e mandare una notifica silenziosa per 1:

borg create --stats ::'{hostname}-{now}' /srv /etc
code=$?
if [ "$code" -ge 2 ]; then
  alert "failed" "borg exited with code $code"
elif [ "$code" -eq 1 ]; then
  alert "warning" "borg finished with warnings"
fi

Se usi borgmatic, il suo hook on_error esegue un comando quando è borgmatic stesso a incontrare un errore, ed è il posto naturale per lo stesso curl. Attenzione al limite che la documentazione di borgmatic dichiara apertamente: se borgmatic non parte mai, on_error non scatta mai. È il guasto numero tre, e serve il Passo 3.

Passo 3: intercetta il backup che non è mai partito

Un wrapper può riferire solo su un'esecuzione avvenuta. Per catturare il silenzio, inverti la logica: il backup invia un ping a un monitor quando riesce, e il monitor ti avvisa quando il ping è in ritardo. Si chiama dead man's switch.

Healthchecks.io (ospitato o self-hosted) fa esattamente questo:

  1. Crea un check con la pianificazione 0 3 * * * e un tempo di tolleranza di un'ora o due.
  2. Aggiungi il suo ping in fondo al job, così parte solo in caso di successo.
  3. Nelle integrazioni del check, aggiungi un Webhook che punta all'URL del tuo canale Echobell.

Il ping va in fondo al wrapper:

# in backup-run, subito prima dell'`exit $code` finale; fa il ping solo in caso di successo
if [ $code -eq 0 ]; then
  curl -fsS -m 10 --retry 3 -o /dev/null https://hc-ping.com/<your-uuid>
fi

Healthchecks supporta anche le varianti di ping /start e /fail e può accettare il codice di uscita nell'URL, quindi un solo check può intercettare anche i job che si bloccano. Il risultato: un server spento, un timer disabilitato e una riga di crontab cancellata producono lo stesso esito, un telefono che squilla. Per lo schema generale e altri monitor, vedi avvisi di errore dei cron job.

Passo 4: verifica che il backup sia recente e ripristinabile

Un backup può partire in orario, uscire con 0 ed essere comunque inutile, per esempio quando stai salvando una cartella vuota perché un mount è fallito. Aggiungi due controlli di sanità poco costosi in un job giornaliero separato:

Età dello snapshot. Avvisa se lo snapshot più recente è più vecchio del previsto:

latest=$(restic snapshots --latest 1 --json | jq -r '.[0].time')
age_h=$(( ( $(date +%s) - $(date -d "$latest" +%s) ) / 3600 ))
if [ "$age_h" -gt 30 ]; then
  alert "stale" "Newest snapshot is ${age_h}h old"
fi

Integrità del repository. restic check verifica la struttura del repository. Eseguilo ogni settimana; per i repository grandi, restic check --read-data-subset=5% campiona i dati senza leggerli tutti. Avvisa su un'uscita diversa da zero.

Nessuno dei due sostituisce un vero ripristino di prova. Una volta a trimestre, ripristina un file verificabile in una cartella temporanea. È l'unico test che dimostra che il backup funziona.

Passo 5: squilla solo quando conta

Se ogni avviso ti chiama, silenzierai il canale, e poi non squillerà per quello che conta davvero. Usa le condizioni di Echobell per separare i livelli su un solo canale:

status == "failed" || status == "stale" || status == "auth-failed"

Manda incomplete e warning a un secondo canale, Urgente (Time Sensitive) o normale, così compaiono sulla schermata di blocco senza squillo. Aggiungi una fascia oraria se vuoi che i guasti diurni restino silenziosi, usando le variabili UTC descritte nel riferimento delle condizioni.

Domande frequenti

Come vengo avvisato quando un backup fallisce?

Racchiudi il comando di backup in un wrapper, leggi il suo codice di uscita e invia un webhook per qualsiasi codice diverso da zero. Aggiungi un dead man's switch come Healthchecks.io per sapere anche delle esecuzioni mai partite.

Su quali codici di uscita di restic conviene avvisare?

Avvisa con forza su 1, 10, 11 e 12, che indicano un guasto vero. Tratta 3 (snapshot creato ma alcuni file illeggibili) come un avviso da indagare, perché può nascondere un problema di permessi.

Come faccio a sapere se un backup ha smesso di girare in silenzio?

Usa un heartbeat: il job invia un ping a un monitor in caso di successo, e il monitor avvisa quando il ping è in ritardo. Un hook di errore non può intercettarlo, perché non gira nulla che possa generare l'errore.

Un codice di uscita positivo basta per fidarsi di un backup?

No. Significa che lo strumento ha finito, non che i dati siano completi o ripristinabili. Controlla l'età degli snapshot, esegui controlli di integrità periodici e prova regolarmente un ripristino.

Posso ricevere una telefonata invece di una mail?

Sì. Le mail si perdono facilmente. Un canale di chiamata in Echobell si comporta come una chiamata in arrivo e attraversa la modalità Full Immersion di iOS, quindi un backup fallito alle 03:00 non passa inosservato.

In sintesi

Tre rilevatori coprono le tre modalità di guasto: un wrapper sui codici di uscita per le esecuzioni che falliscono, un controllo di freschezza per quelle che riescono inutilmente e un heartbeat per quelle che non avvengono mai. Instradali su un canale di chiamata, tieni gli avvisi minori su uno più silenzioso, e un backup rotto diventa una correzione di cinque minuti invece di un weekend perso.

Scarica Echobell per iPhone oppure prendilo su Google Play, crea il canale backups ed esegui il wrapper una volta con una password volutamente sbagliata per sentirlo squillare.

Articoli correlati