BASH Scripting 1 – Backup con Rsync

Con este post comenzaremos a ver algunos scripts básicos de BASH que podemos aplicar en la vida diaria conociendo como funcionan y evitando el copiar-pegar que nunca termina de rendir como lo queremos.
Intro
ANTES DE PROCEDER ASEGURATE DE QUE EL ORIGEN DE LOS DATOS ESTE EN READ ONLY! Si algo sale mal, al menos no rompes nada!
EL Rsync es una utilidad muy potente diseñada para poder lograr backups incrementales de un origen, esto es: tamaño del origen + los cambios con respecto a la copia que tenemos (si es que la tenemos) = backup nuevo. Si alguna vez tuvieron que hacer una copia de seguridad de algún archivo sabrán que se manejan dos o mas copias del mismo (archivo original mas al menos una copia del mismo). Si por ejemplo hablamos de una carpeta que tiene 10 archivos de texto con un tamaño total de 10mb y todos los días nos acordamos de copiarla a un pendrive el tamaño que ocupa seria de 10mb en disco (origen) + 10mb en el pendrive (destino), si necesitamos mas copias creamos una carpeta nueva copiamos todo (y asi n veces*10mb). Rsync trata de evitar justamente esto revisando que cambio desde la ultima copia que tenemos, si solo cambiaron 2 archivos no copia nuevamente los 10 sino solo los nuevos y que es mas interesante aun hace un “acceso directo” entre los archivos que no fueron modificados así visualmente podemos tener en todos los backup el contenido correspondiente pero ocupando mucho menos espacio.
Manos a la obra
Seguramente saben porque usar un disco distinto al original para las copias de seguridad, en el caso eventual de quedarse sin uno de los dos por algún fallo de hard nos quedamos sin backups o sin el original, no sin ambos por lo que el siguiente script fue diseñado para funcionar sobre NFS.

timestamp=`date +%Y%m%d` #seteamos el formato de la fecha (asi se llamaran las carpetas con los backup)
strgorig="/origen" #la carpeta original con los datos
chkmount="/origen/nomount" #un archivo que en el caso de que exista significa que el origen no se monto vía NFS y tenemos todas las carpetas vaciás por lo que no seguimos con el backup
strgdest="/destino" #carpeta destino de los backup (pendrive, disco externo u otro disco de la PC)
if [ -e "$chkmount" ]; then
echo "El archivo /origen/nomount presente por lo que no hacemos nada"
exit 1
else

Como verán esta parte es de lo mas sencillo, solo nos fijamos que estén listos los datos del origen. Si un día no necesitamos hacer backup creamos el archivo /origen/nomount vacío y evitamos que se haga directamente. Claro que si trabajamos con discos locales también puede servir en el caso de necesitar revisión del disco original lo desmontamos, creamos el archivo sobre el punto de montaje en el que estuvo (/origen en nuestro caso) y nos aseguramos de no tener un backup vacío con la fecha actual.

if [ ! -n "$1" -o ! -d "$strgorig/$1/" ]; then
echo "No existe la carpeta origen o no esta definida."
exit 1
else

Acá usamos un poco los operadores lógicos:
if (si) [ ! -n (no esta definido) “$1” (el primer parámetro que le pasamos al script) -o (o justamente) ! -d (no es un directorio) “$strgorig/$1/” (/origen/carpeta) ; then (entonces)
mostramos el aviso que agrupa dos posibles errores (o no le pasamos el nombre de la carpeta a copiar al script o esta no existe).

spaceorig=`du -s "$strgorig/$1/" | cut -f 1`
spacedest=`df --block-size K $strgdest | tail -n 1 | tr -s ' ' ':' | cut -d ":" -f 4 | tr -s 'K' ' '`
if [ $spaceorig -gt $spacedest ]; then
echo "Tamaño del disco/particion destino muy chico"
exit 1
else

Medimos el tamaño que ocupa la carpeta origen, luego la partición o el disco sobre el que necesitamos hacer el backup, si nos quedamos con poco espacio el script se corta. En realidad es útil mas que nada si son varias las carpetas a copiar, en mi caso tengo algo como: /origen/usuario1, /origen/usuario2, /origen/usuario…n por lo que es mejor tener a todos con el backup diario sin a lo mejor uno solo que descargo algo grande y así evitar que todos se queden sin backup hasta ver lo que paso.

if [ ! -d "$strgdest/$1/" ]; then
echo "Carpeta destino no existe"
exit 1
else
let cantdir=0
for fldrdir in `ls $strgdest/$1 | tr -s '\n' '\n'`; do
let cantdir=cantdir+1
bkpdirs[$cantdir]="--link-dest=../"$fldrdir
done
logfile="$strgdest/bkplogs/$1.$timestamp.log"
rsync --log-file=$logfile --delete -a ${bkpdirs[@]} $strgorig/$1/ $strgdest/$1/$timestamp
fi

Con esta parte nos fijamos que el directorio destino exista, luego averiguamos sobre que backup nos vamos a estar basando para no generar toda la copia nuevamente. Recuerdan que dijimos que la idea es copiar solo los cambios? Pero como sabemos lo que cambio en este caso?! EL Rsync se fija en las carpetas que enumeramos con “–link-dest” (las que ya existen por copias que hicimos antes) y solo copia los archivos modificados desde el origen. Claro que si no tenemos aun ninguna copia hecha el array “bkpdirs[]” estará vació y el programa copia todos los datos a una carpeta nueva.
También guardamos un log de lo que se hizo para información, debug o para utilizarlo mas adelante para algo aun mas útil. Espero que les haya servido el post, a continuación dejo el script completo:


#!/bin/bash
timestamp=`date +%Y%m%d`
strgorig="/origen"
chkmount="/origen/nomount"
strgdest="/destino"
if [ -e "$chkmount" ]; then
echo "existe el archivo $chkmount, entonces no sigo"
exit 1
else
if [ ! -n "$1" -o ! -d "$strgorig/$1/" ]; then
echo "Sin carpeta origen o sin definir."
exit 1
else
spaceorig=`du -s "$strgorig/$1/" | cut -f 1`
spacedest=`df --block-size K $strgdest | tail -n 1 | tr -s ' ' ':' | cut -d ":" -f 4 | tr -s 'K' ' '`
if [ $spaceorig -gt $spacedest ]; then
echo "Partición destino sin espacio"
exit 1
else
if [ ! -d "$strgdest/$1/" ]; then
echo "Sin carpeta destino."
exit 1
else
let cantdir=0
for fldrdir in `ls $strgdest/$1 | tr -s '\n' '\n'`; do
let cantdir=cantdir+1
bkpdirs[$cantdir]="--link-dest=../"$fldrdir
done
logfile="$strgdest/bkplogs/$1.$timestamp.log"
rsync --log-file=$logfile --delete -a ${bkpdirs[@]} $strgorig/$1/ $strgdest/$1/$timestamp
fi
fi
fi
exit 1
fi

El ultimo paso puede ser cargando el script al cron con “crontab -e” para que corra por ejemplo a las 6:01am todos los días:
1 6 * * * /scripts/backupconrsync.sh música
Siendo “musica” la carpeta dentro de /origen que necesitamos resguardar.

Problemas y soluciones:
El primer problema que se nos presentara dentro de unos días de correr el backup es que aparecerá un error como: “rsync: ERROR: at most 20 –link-dest args may be specified” ya que el rsync soporta no mas de 20 argumentos –link-dest. Las formas de solucionarlo como siempre pueden ser varias:
Crear no mas de 20 backups
Linkear solo el ultimo backup realizado en lugar de todos los disponibles.
Linkear una cantidad fija no mayor a 20 backups

La primer solución no me convence ya que necesito al menos un mes completo de backups de algunas cosas. La ultima puede ser útil si usamos la opción del rsync para que la copia se haga haciendo el checksum del archivo ya que se hace posible eliminar un contenido y luego de unos días volver a subirlo, volverá a ser linkeado y no perdemos capacidad por hacer una copia nueva del mismo archivo. Como esto ultimo en vida diaria es poco frecuente prefiero ganar en velocidad y hago la copia basándome en el tamaño del archivo junto con su fecha y hora de la ultima modificación. Teniendo esto en cuenta vemos que no es posible subir el mismo contenido nuevamente evitando la doble copia pero ganamos mucho tiempo por no hacer el checksum. Bien ahora sabemos que con basarnos solo en la ultima copia es suficiente, agregamos entonces un chequeo justo antes de llamar al rsync:


if [[ $fldrdir == ?(+|-)+([0-9]) ]]; then
bkpdirs[$cantdir]=$fldrdir
if [ $cantdir -eq 0 ]; then
let cantdir=cantdir+1
maxbkpval=$fldrdir
else
let cantdir=cantdir+1
if [ $fldrdir -gt $maxbkpval ]; then
maxbkpval=$fldrdir
fi
fi
#Entonces no es un numero, no hacemos backup basandonos en algo que no sabemos que es
fi
done
echo "haciendo backup con referencia al $maxbkpval"
#codigo que sigue con el rsync...

Ahora lo que nos queda por hacer seria de alguna manera establecer un valor máximo de backups que necesitamos hacer. Si lo hacemos bien, luego de reducirlo los backup mas viejos correspondientes deben ser eliminados automáticamente al correr el script. Les dejo que lo piensen y luego subo lo que se me ocurrió hacer, hasta la próxima!

Deja un comentario

Para proteger el blog de SPAM, le pedimos que complete la tarea:WordPress CAPTCHA