Una mattina di qualche settimana fa, mentre prendevo il caffè, mi accorgo che la dashboard Grafana non risponde. Stessa cosa per la web UI di Observium e per il logger centrale. Tre servizi giù insieme: non è coincidenza, è un host giù. Quell’host, nel mio homelab, è un Raspberry Pi 5 8GB in case dedicato che chiamo “monitoring” (qui lo chiamo rpi-monitoring) e che fa girare diversi servizi:

Snipe-IT per asset management

Observium Community come NMS che polla SNMP gli altri host

InfluxDB v2 come time series database delle metriche

rsyslog centrale che riceve log dalla LAN

MariaDB condiviso fra Snipe-IT e Observium

Tutto girava su una microSD 64 GB classe A1 generica. Dopo circa un anno di servizio la scheda si è bloccata: kernel pieno di mmc0: Card stuck being busy e poi EXT4-fs error a cascata. Ecco come ho recuperato i dati e ricostruito il servizio.

Perché la microSD è morta

Una microSD generica entry-level è progettata per fotografia e video: scritture sequenziali grosse, poche cancellazioni. Quello che le ho fatto fare era l’esatto contrario: SQL relazionale di Snipe-IT, polling SNMP Observium ogni cinque minuti su dieci host con scrittura MariaDB + RRD, ingestion InfluxDB v2 di metriche ogni dieci secondi, rsyslog centrale, journald del Pi, log rotation di Apache e nginx.

Il TBW sopportabile da una microSD entry-level è qualche TB. Con quel carico ci si arriva in 8-12 mesi. Errore mio: non aver migrato a SSD via USB 3.0 prima.

Step 1: bitwise dump con ddrescue

La prima cosa che si fa con un dispositivo morente, prima di provare a recuperare file, è un dump bitwise dell’intera scheda. ddrescue è lo strumento giusto: legge sequenzialmente, prova a recuperare i settori illeggibili con strategie successive e mantiene un logfile delle aree da riprovare. Su un Raspberry Pi di servizio con uno slot USB ho infilato la microSD malata tramite un adattatore e ho lanciato:


sudo apt install gddrescue
sudo ddrescue -d -r 3 /dev/sda /mnt/ssd/monitoring.img /mnt/ssd/monitoring.log

-d salta la cache del kernel e legge in direct I/O, -r 3 riprova ogni settore tre volte. Il dump è andato avanti per quasi cinque ore. Risultato: 62,8 GB su 64 GB recuperati, 1,2 GB irrecuperabili distribuiti soprattutto nell’area dove stavano i datafile MariaDB di un mese. Già un buon punto di partenza.

Step 2: montare l’immagine read-only

Con l’immagine in mano, l’ho associata a un loop device read-only per non rischiare di scriverci sopra:


sudo losetup -r -fP /mnt/ssd/monitoring.img
sudo losetup -a

Il losetup -a mostra il device /dev/loop0 con partizioni loop0p1 (boot) e loop0p2 (rootfs). Ho montato la rootfs:


sudo mkdir -p /mnt/recover
sudo mount -o ro /dev/loop0p2 /mnt/recover

Filesystem montato pulito, con qualche file segnato come corrotto in dmesg ma navigabile.

Step 3: estrazione dati per servizio

Ho copiato selettivamente i datadir dei servizi importanti su un SSD USB di lavoro:


sudo rsync -aH --info=progress2 /mnt/recover/var/lib/mysql /mnt/ssd/recover/
sudo rsync -aH --info=progress2 /mnt/recover/var/lib/influxdb /mnt/ssd/recover/
sudo rsync -aH --info=progress2 /mnt/recover/var/lib/observium /mnt/ssd/recover/
sudo rsync -aH --info=progress2 /mnt/recover/etc /mnt/ssd/recover/

Per MariaDB il datafile più recente era nell’area corrotta. Ho recuperato il mysqldump notturno dal NAS, datato 7 ore prima del crash: perdita accettabile.

Step 4: ricostruzione su nuovo hardware

Ho montato un Raspberry Pi 5 8GB nuovo, microSD A2 ad alta affidabilità solo per OS, SSD USB 3.0 da 480 GB per /var/lib/observium, /var/lib/influxdb, /var/lib/mysql e /var/lib/observium/rrd tramite bind mount in fstab:


/mnt/ssd/observium  /var/lib/observium  none  bind  0  0
/mnt/ssd/influxdb   /var/lib/influxdb   none  bind  0  0
/mnt/ssd/mysql      /var/lib/mysql      none  bind  0  0
/mnt/ssd/rrd        /var/lib/observium/rrd  none  bind  0  0

Ripristino del dump SQL, copia dei datadir InfluxDB e Observium dal SSD di recupero, riavvio servizi. Snipe-IT è tornato online con sette ore di asset persi (un paio di check-out ricostruibili a memoria). Observium ha ripreso polling con storia RRD intatta. InfluxDB ha ripreso con un buco di un paio d’ore.

Un caso reale dentro il caso

Mentre chiudevo il restore, intorno alle 22:30, ho notato che Observium non discoverava due switch aggiunti la settimana prima. La config era persa nel buco MariaDB? No: avevano cambiato community string dopo una rotazione che mi ero dimenticato di propagare. Credenziali corrette, switch tornati visibili. La documentazione delle credenziali deve vivere fuori dall’host che stai ripristinando, sempre.

Cosa funziona bene

ddrescue fa una cosa e la fa benissimo. Il logfile permette di riprovare incrementalmente sui settori illeggibili senza rifare l’intera immagine. Avere backup esterni (mysqldump notturno su NAS, snapshot config di /etc su altra macchina) ha trasformato un disastro potenziale in un fastidio di mezza giornata.

Limiti

L’unico vero limite era la microSD stessa: classe A1 generica per workload database-heavy è stato un errore di partenza. Le microSD A2 industrial sono molto più resistenti, ma per workload veri serve SSD via USB 3.0 o M.2 HAT.

In pratica

è la storia di scelte hardware sbagliate compensate da una disciplina di backup ragionevole. Oggi configuro SSD via USB 3.0 da subito su qualsiasi Pi che ospiti database o ingestion log, e tengo backup esterni con cadenza coerente con il valore dei dati. Una microSD corrotta non è più un disastro, è una procedura.


Immagine generata con Cloudflare Workers AI / SDXL Base 1.

Articolo originale su rpi.temporiti.net