MIRRORING DI UN DISCO IN BASH

GNU/LINUXGUIDEMAKERSSISTEMI E RETI

Avete un disco con dati sensibili da preservare, ma non avete tempo o soldi da investire per un sistema NAS o RAID? Volete realizzare da soli un piccolo sistema di mirroring ma non sapete da dove iniziare? Vi state ponendo il problema del salvataggio dei dati sul disco usato dal vostro cloud casalingo che vi siete realizzati? O semplicemente avete anni di foto e filmati parcheggiati in un disco ormai datato e vi state chiedendo cosa fare nel caso dovesse rompersi?

Se avete risposto si ad almeno una di queste domande, questa è la guida che fa per voi, vi illustrerò come realizzare un semplice sistema di mirroring per mantenere costantemente sincronizzato un disco sorgente con un disco di backup in modo che ogni volta ci sia una scrittura nel disco sorgente questa venga riportata “in un tempo ragionevole” nel disco di backup, il tutto usando esclusivamente i comandi base della shell bash di Linux.

Innanzitutto chiariamo le idee, per chi non sapesse cosa sia il mirroring, noto anche come RAID 1, è una della tecniche che si può usare per eseguire delle copie “a caldo” da un disco verso un altro. Questo significa che non appena un utente scrive, cancella o modifica un dato su un disco, questo è immediatamente riportato su un altro disco. La rottura del disco sorgente o del disco di backup è di solito segnalata dal sistema e non causa la perdita dei dati. Una volta sostituito il disco rotto c’è una procedura per ripristinare la copia. Il sistema RAID è di solito eseguito o tramite apposito hardware o tramite software proprietari oppure open source.

Dal punto di vista sia ingegneristico che sistemistico, la scelta di un sistema RAID software o meglio ancora hardware è di certo da preferire ad un qualunque metodo casereccio con programmi fai da te. Per questo esistono i NAS, che sono sistemi RAID con una interfaccia di rete e parecchie funzionalità in più, tipo interfaccia web per accedere ai dati e amministrare il sistema, supporto di vari protocolli che permettono di connettere il sistema da dispositivi esterni, quali altri PC, smartphone, Smart TV, ecc.

Ovviamente le soluzioni hardware di dispositivi RAID o NAS hanno un costo, esistono anche soluzioni software open source che sono altrettanto valide, ma cosa succede se dopo qualche anno di utilizzo si rompe l’hardware del NAS o il programma open source che usavate non c’è più o non si può più installare sulle macchine che avete a disposizione? Beh, ve lo dico per esperienza, è una bella gatta da pelare… dal momento che i sistemi RAID spesso formattano i dischi in modo proprietario, rendendoli illeggibili dai sistemi operativi, sia Windows, che Linux, che Apple. Avreste uno o più dischi con i vostri preziosissimi dati, ma non avreste la possibilità di accedervi, e questo è un bel problema.

La soluzione? Potete scrivervi un vostro programma fatto con i comandi della shell bash di Linux che imiti quanto più possibile il funzionamento di un controller RAID 1 ed è quello che vedremo in questa guida. Per semplicità il controllo degli errori non sarà effettuato. Faremo uso di una scheda Raspberry con sistema operativo Rasperry PI OS (l’evoluzione del ben più noto Raspbian), ma tutto quello che dirò è perfettamente adattabile a qualunque sistema basato su architettura Linux.

Un sistema di mirroring si basa su 2 concetti fondamentali:

  1. Capire quando viene eseguita una scrittura sul disco sorgente
  2. Sincronizzare il disco di backup riportando tutto ciò che è stato scritto nel disco sorgente

Cominciamo dalla fase 1. Supponiamo di avere due dischi, un disco sorgente e un disco backup. Facciamo in modo che i due dischi siano automaticamente montati in una posizione comoda nel nostro file system. Per far questo si può seguire un’ottima guida per Raspberry su come montare in automatico un disco esterno. Consiglio l’uso di dischi formattati con NTFS o EXT4, per un buon funzionamento dei comandi che andremo ad usare. Un primo approccio al problema sarebbe quello di memorizzarsi la checksum di tutti i files presenti nel disco sorgente e controllare periodicamente se si modifica, nel caso avvenga, si scatena la fase 2. Purtroppo se il disco contiene troppi files, calcolarne la checksum risulta troppo oneroso. Ma a noi basta sapere che qualcosa è cambiato, non cosa sia cambiato, per cui ci può venire in aiuto il comando stat che serve per dare informazioni su files e directories, tra cui la data di ultima modifica.

Supponiamo per semplicità di aver montato il disco sorgente in /media/source e il disco backup in /media/backup. Il comando stat lanciato direttamente sul device del disco (e non sul path in cui è montato) è capace di riportare l’ultimo accesso in modifica al disco. Per cui recuperiamo il device del disco sorgente controllando nel file /etc/mtab. Per chi non lo conoscesse, questo file di sistema mantiene informazioni tra i device esterni, come dischi, chiavette, ecc. e la posizione in cui sono montati all’interno del file system Linux, maggiori informazioni le potete trovare qui. Per estrarre il device, faremo uso di alcuni comandi combinati (attenzione ad usare i giusti apici):

$ SRCDEV=`grep "/media/source " /etc/mtab | cut -d' ' -f1`

Il comando grep accetta due parametri obbligatori, una stringa di testo esprimente cosa cercare e il o i files dove cercarla; restituisce solo le linee dei files che contengono la stringa impostata. Il programma cut invece taglia il contenuto che gli viene dato in ingresso in diverse colonne identificate dal carattere di separazione espresso dal campo ‘-d’ e ne considera solo la o le colonne indicate dal parametro ‘-f’. Di fatto questo comando si può tradurre in “estrai dal file /etc/mtab la riga che contiene /media/source seguita da uno spazio, separa il risultato in colonne usando lo spazio come carattere separatore e considera solo la prima colonna”. Tutto il risultato è memorizzato nella variabile SRCDEV, che a questo punto conterrà il nome del device del disco sorgente.

Tornando al comando stat, lanciato su un device restituisce diverse informazioni, tra cui anche l’ultima data di modifica del disco, che è il dato che ci serve. Per estrarre solo questo, possiamo usare l’opzione ‘-c %Z’:

$ stat -c %Z ${SRCDEV}

Memorizzando il risultato in una variabile e rilanciandolo periodicamente posso capire se è intercorsa una modifica nel disco sorgente.

Passiamo alla fase 2. Come mantengo sincronizzati due dischi? Linux fornisce un comando presente nella maggior parte delle distribuzioni (ma comunque installabile in tutte come tool aggiuntivo) chiamato rsync che in realtà è un potente comando che permette di trasferire da un’unità ad un’altra tutti i files modificati. Per far questo analizza ogni singolo file per determinare se è il caso di trasferirlo o meno. Il comando rsync fornisce moltissimi parametri di configurazione e permette di scegliere i files da sincronizzare, da escludere, il meccanismo di comparazione, ecc.

Il comando che useremo è questo:

$ rsync -az --delete-after --delete-excluded --force /media/source/ /media/backup/

Vediamo nel dettaglio le opzioni, ‘-az’ imposta rsync per fare delle copie di archivio mantenendo tutti gli attributi che hanno i files nel disco sorgente, inoltre il trasferimento avviene comprimendo i files per agevolare la velocità di trasferimento, l’unione delle tre opzioni ‘–delete-after’ e ‘–delete-excluded’ e ‘–force’ imposta la modalità di copia integrale non facendo caso ai vari errori che possono capitare, cancellando nel disco di backup eventuali files cancellati anche nel disco sorgente. Una nota particolare per come ho indicato il disco sorgente e il disco di backup, notate che alla fine del nome ho messo un carattere di ‘/’, che non è per niente casuale, infatti se quel path indica un disco che è stato montato, mettendo lo ‘/’ alla fine si indica che si vuole indicare il contenuto del disco montato e non il nome logico della directory (che di per sé è vuota)

A questo punto manca solo di unire le due fasi in modo tale che eseguano un ciclo infinito in cui si controlla se il disco sorgente è stato modificato, nel qual caso occorre sincronizzare disco sorgente con disco di backup. Quanto spesso venga fatta questa operazione è un parametro che deve scegliere l’utente valutando bene le sue esigenze: basso periodo significa controllare spesso, quindi potenzialmente avere un’ottima probabilità di non perdere dati, ma si intensificano anche gli accessi al disco, facendone diminuire il tempo di vita. A scopo didattico nell’esempio che segue il periodo è impostato a 30 secondi.

 

#!/usr/bin/bash

 

# Time to wait in seconds between two checks

TTW=30

# Log file name and position

LOGFILE="/var/log/backup.log"

# Path where source drive is mounted

SRCPATH="/media/source"

# Path where backup drive is mounted

BKPPATH="/media/backup"

# Retrieve source device

SRCDEV=`grep "${SRCPATH} " /etc/mtab | cut -d' ' -f1`

 

# Synchronize source and backup disks as first step

rsync -az --delete-after --delete-excluded --force ${SRCPATH}/ ${BKPPATH}/

ORIG=`stat -c %Z ${SRCDEV}`

> ${LOGFILE}

while true

do

     sleep ${TTW}

     NEW=`stat -c %Z ${SRCDEV}`

     echo `date +'%F %T.%N %Z'` - "Check diff" >> ${LOGFILE}

     if [ "${ORIG}" != "${NEW}" ]; then

         echo `date +'%F %T.%N %Z'` - "Execute backup" >> ${LOGFILE}

         rsync -az --delete-after --delete-excluded --force ${SRCPATH}/ ${BKPPATH}/

         ORIG=${NEW}

     fi

done

 

Nello script sono state introdotte diverse variabili, tra cui la variabile TTW (Time To Wait) che misura in secondi quanto tempo attendere tra una esecuzione del ciclo e quella successiva. Inoltre c’è anche la possibilità di salvare un file di log che traccia le operazioni principali eseguite dal programma. Il nome e la posizione di tale file sono contenuti nella variabile LOGFILE. Abbiamo poi altre due variabili che contengono il path nel file system in cui sono stati rispettivamente montati il disco sorgente e il disco backup, le variabili si chiamano SRCPATH e SRCPATH. Queste 4 variabili sono le uniche cose che dovrete modificare secondo i vostri bisogni, il resto del programma può essere lasciato inalterato.

Ultima nota, salvate tutto in un file di testo a cui potete dare l’estensione .sh, che per convenzione indica gli script di shell e non dimenticate di fornire i permessi di esecuzione al file. Se avete chiamato il file backup.sh, il comando per renderlo eseguibile è questo:

$ chmod +x backup.sh

Resta la ciliegina sulla torta, far partire il programma all’accensione della scheda Raspberry. Per far questo, esistono varie tecniche, una delle più semplici è quella di agire sulla comando crontab che normalmente serve per schedulare operazioni ricorrenti, ma che ha una interessante opzione che prevede di lanciare uno o più comandi all’accensione del sistema. Lanciate quindi questo comando:

$ sudo crontab -e

L’uso di sudo è necessario perché occorre che il programma sia lanciato con i permessi di esecuzione di root, infatti anche nel caso lanciate backup.sh da linea di comando ricordatevi di farlo precedere dal comando sudo. Ma torniamo al nostro comando crontab, una volta lanciato vi chiederà di scegliere un editor di testo per fare la modifica, scegliete quello che preferite, vi si aprirà un file pieno di commenti, andate all’ultima riga e aggiungete (modificate il path se avete messo backup.sh in un direttorio diverso):

@reboot /home/pi/script/backup.sh

Salvate e provare a fare reboot, dovreste vedere che il file di log viene creato e periodicamente aggiornato con le varie operazioni.

Buon backup a tutti! E fatemi sapere nei commenti se vi è piaciuto il programma, se avete dubbi o avete trovato problemi nell’eseguirlo. A presto!