Optimizando un job de backup

Este fin de semana me aburría y me he puesto a optimizar un script para realizar backup que llevo usando unos cuantos años en diversos PCs de escritorio. Es un poco "pedestre" puesto que básicamente se dedica a comprimir con tar una serie de directorios, y guardar el contenido en un disco externo USB.

Originalmente el script era algo así :

#! /bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_MNT="/media/disco_usb"
BACKUP_TARGET="$BACKUP_MNT/backup/$HOSTNAME"
TAROPS="--absolute-names --atime-preserve -z "

mkdir -p $BACKUP_TARGET/$DATE
BACKUP_TARGET="$BACKUP_TARGET/$DATE"

FILEPREFIX="$BACKUP_TARGET/$DATE-$HOSTNAME"

if [ ! -d "$BACKUP_TARGET" ]; then
        echo "Backup target '$BACKUP_TARGET' does not exit, cannot backup"
        exit 0
fi

tar -c $TAROPS -f $FILEPREFIX-home.tar.gz /home
tar -c $TAROPS -f $FILEPREFIX-etc.tar.gz  /etc
# Y aquí más comandos similares para otros directorios.

md5sum $FILEPREFIX-home.tar.gz > $FILEPREFIX-home.tar.gz.md5
md5sum $FILEPREFIX-etc.tar.gz  > $FILEPREFIX-etc.tar.gz.md5
# Despues de comprimir los ficheros, guardamos la suma MD5 para poder
# saber en el futuro si han sido alterados (fichero a medias, problemas
# de lectura si es un CDROM, etc).

Esta forma de hacer el backup no es muy eficiente, porque gzip (opción -z) solo aprovecha un único core a la vez, con lo que no sacamos provecho de tener múltiples cores/procesadores.

Además, el proceso de hacer un md5sum era bastante costoso al tener que releer desde disco el fichero comprimido (en mi caso, unos 20Gb).

Para el primer problema, me ha alegrado descubrir pigz, un clon de gzip multihilo que aprovecha todos los cores de forma simultánea. El uso es tan sencillo como pigz fichero.tar , o en el caso de quererlo usar con tar :

tar -I pigz -cf fichero.tar.gz  dir1 dir2 dir3

Para el segundo problema, he recordado la existencia de tee, un programa de GNU que nos permite enviar la entrada estándar hacia múltiples procesos y salidas. ¿Cómo nos influye esto en el rendimiento? Simplemente es mucho menos costoso hacer la suma MD5SUM antes de escribir el fichero en disco (mientras está en memoria), que escribirlo y luego tenerlo que leer.

La idea con esto, por tanto, es que tar genere un fichero comprimido y que mediante tee, sea examinado por MD5SUM y, por otra parte, redireccionada la salida estándar al fichero .tar.gz correspondiente. Tal que así :

tar -I pigz -c dir1 dir2 dir3 | tee >( md5sum > fichero.tar.gz.md5 ) > fichero.tar.gz

Ojo que al usar tee debemos cambiar la forma en la que invocamos tar. Ya no es posible usar la opción -f para indicar el fichero de salida, si no que se especifica redireccionado la salida estándar.

Al final quedaría el script de backup de esta forma:

#! /bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_MNT="/media/disco_usb"
BACKUP_TARGET="$BACKUP_MNT/backup/$HOSTNAME"
TAROPS="--absolute-names --atime-preserve -I pigz "

mkdir -p $BACKUP_TARGET/$DATE
BACKUP_TARGET="$BACKUP_TARGET/$DATE"

FILEPREFIX="$BACKUP_TARGET/$DATE-$HOSTNAME"

if [ ! -d "$BACKUP_TARGET" ]; then
        echo "Backup target '$BACKUP_TARGET' does not exit, cannot backup"
        exit 0
fi

tar -c $TAROPS /home | tee >(md5sum > $FILEPREFIX-home.tar.gz.md5) > $FILEPREFIX-home.tar.gz
tar -c $TAROPS /etc  | tee >(md5sum > $FILEPREFIX-etc.tar.gz.md5)  > $FILEPREFIX-etc.tar.gz

¿Cuánto ha mejorado con esto? Mi trabajo tardaba originalmente unos 26 minutos en completarse. Activando la compresión paralela, ha pasado a tardar unos 17. Y activando la suma md5sum con tee, ha pasado a tardar 13.5min .

No está mal bajar a la mitad de tiempo con un par de comandos! ;-)