Backup di un repository svn

Uno degli strumenti che utilizzo ed apprezzo di più nell’uso quotidiano è sicuramente subversion. Questo strumento eccezionale è insuperabile quando si lavora in gruppo, ma è utilissimo anche quando si è gli unici a lavorare su un singolo progetto.

Per questo ognuno dei progetti su cui lavoro è gestito tramite un repository subversion nel quale memorizzo tutti i files di interesse. In tal modo ho sempre la possibilità di ricostruire le modifiche ad un file nel tempo, di poterne recuperare le versioni precedenti, di poter trasferire le modifiche con estrema semplicità da una macchina all’altra, insomma una versione super di Time Machine (chiaramente senza fronzoli grafici e non di così immediato utilizzo).

Quando si utilizza subversion, indipendentemente da come si accede al repository, tutto il contenuto (dati, revisioni e configurazione) è memorizzato nella directory del repository stesso, che è semplicemente una directory all’interno del filesystem. Per eseguire il backup di un repository si potrebbe pensare di salvare il contenuto di questa directory su supporto esterno ed amen. Sebbene questa soluzione potrebbe anche funzionare, di sicuro non è quella consigliata. Infatti una soluzione di questo tipo potrebbe corrompere lo stato interno del repository nel caso in cui venga effettuata una qualsiasi operazione sul repository stesso durante la copia, rendendo inservibile il backup creato.

Vediamo invece una delle strategie di backup possibili utilizzando gli strumenti messi a disposizione con la distribuzione standard di subversion.

Lo script hot-backup.py

Il primo strumento da utilizzare per creare un backup di un repository è lo script hot-backup.py. Questo script è progettato per eseguire una copia completa di un repository “a caldo”, ovvero anche se il repository è in uso crea una copia consistente di tutte le transazioni evitando di incorrere nei problemi descritti sopra. Per cui utilizzando tale script si otterrà una copia funzionante del repository pronta all’uso. L’utilizzo di questo comando è abbastanza immediato (su Debian lo script è installato di default in /usr/bin/svn-hot-backup):

# svn-hot-backup --help
USAGE: svn-hot-backup [OPTIONS] REPOS_PATH BACKUP_PATH

Create a backup of the repository at REPOS_PATH in a subdirectory of
the BACKUP_PATH location, named after the youngest revision.

Options:
  --archive-type=FMT Create an archive of the backup. FMT can be one of:
                       bz2 : Creates a bzip2 compressed tar file.
                       gz  : Creates a gzip compressed tar file.
                       zip : Creates a compressed zip file.
  --num-backups=N    Number of prior backups to keep around (0 to keep all).
  --help      -h     Print this help message and exit.

È sufficiente passargli il percorso del repository ed il percorso dove si vuole salvare il backup ed il gioco è fatto, copia creata:

# svn-hot-backup /home/svn/repositories/src backup/
Beginning hot backup of '/home2/svn/repositories/src'.
Youngest revision is 1663
Backing up repository to 'backup/src-1663'...
Done.

Il comando ha appena creato una copia perfettamente funzionante del repository in oggetto nella directory di backup.

Vediamo come utilizzare questo comando con le sue opzioni per programmare un backup a lungo termine. Le opzioni a disposizione sono solamente due, ma entrambe molto utili. La prima opzione è --archive-type=FMT, con questa opzione si può dire al comando di creare un archivio compresso in uno dei formati supportati dallo script, ovvero gz, bz2 e zip. La seconda opzione, --num-backups=N, invece consente di eseguire il rotate del log in maniera simile al comando logrotate, mantenendo solamente le N copie più recenti dei backup.

Combinando le due opzioni viste si può scrivere un cronjob per salvare il contenuto dei repository su un supporto di backup ad intervalli regolari. Nel mio caso ho inserito il backup nei cron settimanali della macchina, creando il file /etc/cron.weekly/backup-svn il cui contenuto è

#! /bin/sh

MAILTO='fabio@example.com'

# Backup svn repositories

test -x /usr/bin/svn-hot-backup || exit 0

# repositories list
REPOS_LIST="
/home/svn/repositories/src
/home/svn/repositories/src2"

# rotate every n backups
ROTATE=4

# backup path
BACKUP_PATH=/home/svn/backups

for REPOS in $REPOS_LIST; do
        svn-hot-backup --num-backups=$ROTATE --archive-type=bz2 $REPOS $BACKUP_PATH
done

In questo modo ogni settimana viene creata una copia integrale dei repositories indicati nella variabile REPOS_LIST mantenendo solamente gli ultimi 4 nella directory per i backup. Chiaramente se la directory BACKUP_PATH risiede sulla stesso disco del repository, è quasi inutile questo tipo di backup. Nel mio caso la directory contenente i backups viene copiata giornalmente tramite rsync su un’altra macchina. In alternativa è sufficiente aggiungere un comando dopo il ciclo for che copi il contenuto della directory dove meglio si crede (disco esterno, disco di rete, nastro, etc).

Un consiglio importante, in precedenza avevo provato ad impostare la variabile BACKUP_PATH con il percorso di un disco montato con nfs, e le prestazioni erano veramente ridicole, per cui se volete utilizzare un disco di rete (smb o nfs che sia) conviene creare prima i backup sul disco locale e poi copiarli. Non eseguite direttamente lo script hot-backup sull’unità di rete perché è spaventosamente lento.

Backup incrementale delle singole revisioni

Il metodo precedente crea i backup settimanalmente (anche se basta impostare diversamente cron per eseguirlo con cadenza differente), ma che succede se c’è un imprevisto dopo 6 giorni dal backup. Potenzialmente si possono perdere fino a 6 giorni di lavoro, e sicuramente si perdono tutte le singole revisioni di cui si è fatto commit dall’ultimo backup.

Una soluzione molto elegante a questo problema (che può funzionare anche come soluzione stand-alone di backup) è quella di utilizzare il backup incrementale delle singole revisioni di cui si fa commit utilizzando il programma svnadmin dump e gli hook scripts. Utilizzando questi due strumenti è possibile salvare ogni singola revisione del repository per poi reimportarla in un repository creato nella sezione precedente. In tal modo in casi di disastri si perderà poco o niente del proprio repository.

Per salvare il contenuto di ogni singola revisione in una directory a piacimento è sufficiente editare (o creare) lo script post-commit nella directory hooks del repository ed aggiungere il seguente contenuto

REPOS="$1"
REV="$2"

# backup of current revision to revision dir
svnadmin dump "$REPOS" -q -r $REV --incremental --deltas | gzip > /home/svn/backups/revisions/src.rev$REV.gz

In questo modo ogni volta che si fa commit il contenuto della revisione corrente viene salvato nel file src.rev<num>.gz e può essere utilizzato tramite il comando svnadmin load per ricreare la modifica su una copia del repository.

Una cosa estremamente importante a cui prestare attenzione è il flag --incremental, infatti senza di esso verrebbe salvato un dump completo del repository nel file di backup.

Per quanto riguarda invece il flag --deltas può essere anche omesso, esso serve per creare il dump in formato binario e in modo che contenga solo le differenze con la precedente revisione; questo fa si che il file che contiene la revisione sia sensibilmente più piccolo di quello ottenuto senza questo flag (che contiene una copia intera in formato testuale dei files modificati nella revisione corrente). Quindi usare o meno il flag --deltas è un compromesso tra spazio disco ed uso processore in fase di dump/load e di conseguenza in fase di commit. Questo è il raffronto in termini di costi tra un backup creato con e senza il flag --deltas su un repository di dimensioni medio-piccole. Da notare come, se compressi con gzip, i backup ottenuti abbiano più o meno la stessa dimensione finale.

# time svnadmin dump -q /home/svn/repositories/src --deltas > deltas.dump

real	0m16.554s
user	0m12.181s
sys	0m4.128s
# time svnadmin dump -q /home/svn/repositories/src > nodeltas.dump

real	0m9.240s
user	0m6.956s
sys	0m2.016s
# ll -h
total 86M
-rw-r--r-- 1 root root 28M 2009-11-13 22:12 deltas.dump
-rw-r--r-- 1 root root 58M 2009-11-13 22:13 nodeltas.dump
# ll -h
total 42M
-rw-r--r-- 1 root root 21M 2009-11-13 22:12 deltas.dump.gz
-rw-r--r-- 1 root root 22M 2009-11-13 22:13 nodeltas.dump.gz

I backup delle singole revisioni andrebbero memorizzati insieme ad i backup completi creati in precedenza, in modo tale da poterli utilizzare congiuntamente per ricreare il repository nello stato dell’ultima versione disponibile. Per eseguire un ripristino è sufficiente prendere l’ultima versione completa a disposizione del repository scompattarla e importarci tutte le successive revisioni di cui si ha il backup con il comando svnadmin load.

You can leave a response, or trackback from your own site.

Leave a Reply

Subscribe to RSS Feed