Usando rsync y cron para automatizar respaldos incrementales
In English  

Hay dos clases de gente en el mundo: aquellos que respaldan, y aquellos que nunca han experimentado una pérdida de datos severa.

La pérdida de datos en una seria preocupación para individuos y compañías que se confían en el uso de computadoras para su vida u operaciones diarias. Aquellos que tienen un sistema basado en Unix cuentan con poderosas herramientas para prevenir esto, como lo es rsync para respaldar la información y cron para hacer el proceso de respaldo automático. En publicaciones previas escribí sobre lo básico de rsync y su uso como un servicio, así como lo básico de cron. En esta publicación el enfoque está en la funcionalidad de rsync para respaldos y su uso conjunto con cron para automatizar el proceso de respaldo.

Temas
Respaldo completo y respaldo incremental
Lo básico del parámetro --backup
Usando un directorio de respaldo
Usando los parámetros --backup y --delete juntos
rsync y cron para respaldos incrementales automáticos
Respaldo incremental rotatorio con rsync y cron
Caso de ejemplo:
Notas al pie

Respaldo completo y respaldo incremental

Un respaldo completo, como su nombre lo implica, es una copia de todos y cada uno de los archivos y directorios (y subdirectorios) en una localidad diferente. Esta es una buena manera de respaldar tus archivos (y el paso inicial de cualquier sistema de respaldos incrementales y diferenciales), sin embargo, conforme el número y el tamaño de los archivos crece, se vuelve pesado y consume más tiempo hacer este respaldo completo cada día, o cualquier periodo de tiempo que requieras basado en la importancia de la información y la frecuencia a la que cambia. Y sin embargo, debes respaldar tu información tan frecuente como sea requerido, especialmente si estás trabajando con información crítica.

Aquí es donde entran los respaldos incrementales. Como escribí antes en Actualizar los contenidos de una carpeta, rsync nos permite transferir solo los cambios más recientes a nuestros archivos así como los nuevos archivos que hayan sido creados desde la última copia, utilizando su algoritmo delta-transfer. Cuando activamos la funcionalidad de respaldo de rsync, rsync copia los archivos que están por ser modificados a ya sea un archivo llamado similarmente pero con un sufijo, o a un directorio dedicado a mantener la información de respaldo, y si utilizamos el parámetro --delete, también copia los archivos que están por ser borrados. Entonces, una vez que todos estos archivos han sido copiados a la carpeta de respaldo o se les ha dado un nuevo nombre, rsync actualiza los archivos con la nueva información, y si el parámetro --delete fue utilizado, los archivos que fueron previamente removidos de el directorio de origen son borrados en el directorio de destino. Usando el parámetro --backup se activa el algoritmo delta-transfer, así que el uso del parámetro -u no es necesario.

De esta manera, terminamos con una carpeta completamente actualizada, así como un respaldo de los archivos que fueron modificados desde la última vez que fueron copiados, utilizando una transferencia de datos más pequeña, y sin una gran área en uso para un segundo respaldo completo. Si necesitáramos revertir a un respaldo previo, todo lo que necesitamos hacer es copiar de vuelta los archivos de el respaldo sobre los actualizados y borrar cualquier archivo que fuera creado después de que realizamos el respaldo que estamos restaurando.

Lo básico del parámetro --backup

Para crear un respaldo de los archivos que están por ser modificados utilizamos el parámetro --backup (o -b en su forma corta), por ejemplo:

rsync -a --backup origen/ destino/
rsync -ab origen/ destino/

Por default, el sufijo ~ es anexado al nombre del archivo que es guardado como respaldo, es decir, cuando actualizamos archivo01.txt, este archivo será copiado a un archivo llamado archivo01.txt~, y entonces archivo01.txt será actualizado con el nuevo contenido. Para cambiar el sufijo de el archivo que será creado como respaldo, usamos el parámetro --suffix, por ejemplo:

rsync -ab --suffix=.viejo origen/ destino/

Esto agregará el sufijo .viejo a los archivos de respaldo, es decir, archivo01.txt será copiado en archivo01.txt.viejo, y entonces archivo01.txt sería actualizado con la nueva información. Podemos agregar la fecha de el respaldo al sufijo utilizando el comando date junto con el parámetro sufijo:

rsync -ab --suffix=_`date +%F` origen/ destino/

Esto anexaría la fecha en el formato YYYY-MM-DD al nombre de el archivo, por ejemplo, si hoy fuera 12 de febrero de 2010, y respaldamos archivo01.txt, el archivo resultante sería llamado: archivo01.txt_2010-02-12

Para restaurar desde el respaldo si solo agregamos un sufijo, podemos usar los siguientes comandos:

for nombre in *[sufijo];
do
cp $nombre `basename $nombre [sufijo]`;
done

Puedes usar mv en lugar de cp si no quieres dejar el archivo de respaldo. Como ejemplo de esto, si usamos --suffix=.viejo con rsync:

for nombre in *.viejo;
do
cp $nombre `basename $nombre .viejo`;
done

Como una nota, si usas el sufijo para los respaldos, es recomendable agregar ~ al final del sufijo de todas maneras, así es más fácil diferenciar entre archivos normales y archivos de respaldo, así como manejar los respaldo por si se diera la necesidad de restaurar los archivos.

Usando un directorio de respaldo

Mientras que usar un sufijo para respaldar los contenidos de una carpeta es suficiente en algunos casos, para estructuras de archivos y directorios más complejas, podemos crear el respaldo de estos archivos y la estructura entera de directorios y subdirectorios en un directorio diferente en lugar de solo renombrar los archivos. Otro beneficio de esto es una mejor organización de los respaldos. Especificamos el directorio donde queremos guardar el respaldo utilizando el parámetro --backup-dir, esto usa la carpeta en el directorio de destino, a menos que se especifique otra cosa. Si el directorio especificado no existe, rsync lo creará. Por ejemplo:

rsync -ab --backup-dir=respaldo origen/ destino/

Esto creará un directorio llamado respaldo en la carpeta destino, y respaldará ahí cualquier archivo que esté por ser modificado. Si queremos que el directorio respaldo sea creado en un lugar diferente, podemos usar:

rsync -abv --backup-dir=/home/juan/Respaldos/respaldo_ejemplo origen/ destino/

Y esto tendría el respaldo guardado en un directorio llamado respaldo_ejemplo dentro de /home/juan/Respaldos

Usando los parámetros --backup y --delete juntos

Como se mencionó previamente en esta publicación, podemos también borrar los archivos de la carpeta destino que ya fueron borrados en la carpeta origen, esto sincronizaría los directorios. Con el parámetro --backup, estos archivos que van a ser borrados en la carpeta destino serían copiados en la carpeta respaldo también, o los sería dado un sufijo si esto es lo que estás utilizando, antes de que rsync los borre de el directorio, por ejemplo:

rsync -ab --delete origen/ destino/

Pero se cuidadoso con el parámetro --delete. Si estamos guardando los respaldos en la carpeta destino, o en un directorio dentro de la carpeta destino, el parámetro --delete va a borrar los viejos respaldos, ya que ellos no se encuentran el la carpeta origen. O a intentarlo como en la siguiente situación:

Supongamos que ya tenemos una carpeta llamada respaldo dentro de el directorio destino, y utilizamos rsync de nuevo, utilizando --backup-dir=respaldo una vez más. Ya que rsync va a intentar borrar todos los archivos y directorios que no están en el directorio origen, respaldaría el directorio respaldo, lo que crearía una carpeta respaldo dentro de nuestro ya existente directorio respaldo, y entonces intentaría borrar la carpeta respaldo y fallaría ya que la está usando para respaldar archivos. Ejecuta esto múltiples veces y podrías terminar con una carpeta respaldo dentro de una carpeta respaldo dentro de una carpeta respaldo, en resumen, un desorden indeseable.

Si estamos creando el directorio respaldo dentro de la carpeta destino, toma la precaución de crear reglas de exclusión para dejar la carpeta (o carpetas) de respaldo fuera de el rsync que está creando respaldos. Por ejemplo, en este caso en particular:

rsync --ab --backup-dir=respaldo --delete --exclude=respaldo origen/ destino/

Algo de esto podría no verse tan útil justo ahora, pero ya que la meta es automatizar el proceso de respaldo con cron, estos parámetros --backup-dir, --delete --exclude se volverá muy útiles para esto.

rsync y cron para respaldos incrementales automáticos

Esta sección asume que ya sabes como especificar el tiempo en un crontab, para más información sobre esto, puedes revisar mi publicación previa Usando cronjobs para automatizar tareas.

Si fuéramos a hacer un respaldo completo diario, podríamos utilizar el siguiente crontab:

@daily rsync -au --delete origen/ destino/

Esto sincronizará el directorio origen con el directorio destino, y tendríamos un respaldo completo en destino. Sin embargo, solo tendríamos los archivos en el mismo estado como estuvieron desde la última copia. No podríamos regresar a ningún otro punto en el tiempo. Para dejar una copia de los archivos como estaban antes de que actualizáramos los archivos, esto es, un respaldo incremental, debemos usar:

@daily rsync -ab --backup-dir=viejo_`date +%F` --delete --exclude=viejo_* origen/ destino/

Ahora tendríamos el respaldo completo en el directorio destino, y un respaldo de los archivos como estaban antes de el último respaldo en un directorio. Si este respaldo ocurrió el 12 de febrero de 2010, entonces el directorio sería llamado viejo_2010-02-12. Por supuesto esto crearía un diferente directorio de respaldo por todos y cada uno de los días en que se ejecute, y para restaurar archivos a su estado en cierto día, necesitaríamos copiar secuencialmente los archivos de el más reciente al día que queremos restaurar sobre el la carpeta de respaldo completo, y borrar cualquier nuevo archivo que fue creado tras ese día.

Respaldo incremental rotatorio con rsync y cron

Un respaldo incremental rotatorio se refiere al hecho de que reutilizamos un cierto número de carpetas de respaldo para los respaldos incrementales. En lugar de, por ejemplo, agregar la fecha completa al nombre de un directorio cuando estamos haciendo un respaldo diario, podemos utilizar solo el día de la semana. Esto crearía los directorios viejo_0, viejo_1 ... viejo_6 y entonces,usaría de nuevo la carpeta viejo_0 al comienzo de la siguiente semana. Eliminaríamos esta carpeta si existe, y la recrearíamos para el nuevo respaldo. De esta manera podríamos regresar en el tiempo hasta 7 días. La ventaja obvia de esto es que no terminaríamos con un gran número de carpetas de respaldo (y un montón de viejos datos) ya que estaríamos reutilizando el espacio y los nombres de las carpetas. Siete días puede ser muy corto, podemos usar el número del día en el mes para poder regresar alrededor de 30 días en el tiempo, o el número del día en el año para poder regresar hasta 365 días en tiempo.

Caso de ejemplo

Un respaldo incremental rotatorio diario en una computadora remota que tiene acceso a rsync sobre SSH, que nos permita regresar en el tiempo hasta un año.

Comenzamos configurando el entorno para esto. Necesitamos una computadora remota que por supuesto nos permita utilizar rsync sobre SSH. Voy a utilizar Dreamhost como un ejemplo ya que me da 50GB para hacer lo que quiera con ellos (además de el espacio ilimitado para hospedar sitios web), y me da este tipo de acceso, así que lo utilizo para respaldar información que no es extremadamente privada (cifro la información privada cuando la respaldo).

Ya que estoy utilizando Dreamhost como un ejemplo, tengo que decir que el usuario de respaldo no tiene un shell de SSH completo disponible, aunque los usuarios de hospedaje que creo lo tienen, pero podemos utilizar rsync sobre SSH, y podemos utilizar SFTP. Si tienen un shell de SSH completo disponible donde realizas los respaldos, puedes seguir la guía Conexión SSH sin contraseña utilizando firmas digitales en vez del procedimiento descrito aquí para crear authorized_keys por medio de SFTP.

Entonces, comencemos.

Primero que nada, creo una entrada en ~/.ssh/config como se ve en Definiendo servidores SSH, y la nombro respaldo_remoto. La entrada se ve algo así:

Host respaldo_remoto
HostName nombre_servidor.dreamhost.com
User nombre_usuario

Después de esto, creo la firma digital si todavía no tengo una:

ssh-keygen -t rsa -b 2048

Y me conecto por medio de SFTP, creo la carpeta .ssh, y copio mi id_rsa.pub recien generada o ya existente en .ssh/authorized_keys en el servidor remoto, y ya que estoy aquí, creo antes de salir las carpetas Respaldos y mi_trabajo que voy a utilizar:

sftp respaldo_remoto
(Escribe la contraseña. Después de entrar estamos en un shell de SFTP y usamos estos comandos)
mkdir .ssh
cd .ssh/
put -p /home/juan/.ssh/id_rsa.pub authorized_keys
cd ..
mkdir Respaldos
mkdir Respaldos/mi_trabajo
exit

Recuerda utilizar tu propia información (servidor, nombres de usuario). Si todo salió bien, ahora puedes simplemente escribir SFTP respaldo_remoto y te daría el shell de SFTP, sin la necesidad de escribir una contraseña.

Ya que necesito borrar las carpetas que voy a reciclar así como realizar el respaldo, voy a crear un script que haga ambos, y llamar este script diariamente con cron. La primera vez que este script corra va a crear el respaldo completo, ya que el directorio de destino en el servidor estará vacío. En el caso de Dreamhost, que estoy utilizando en el ejemplo, borraré la carpeta que voy a reciclar vía SFTP. Pero agregaré el comando para hacer esto en un shell de SSH en caso de que lo tengas disponible, solo sácalo del comentario y comenta el área de SFTP. Voy a llamar a este archivo respaldo_auto.sh, guardarlo en mi carpeta Scripts, y darle privilegios de ejecución:

touch ~/Scripts/respaldo_auto.sh
chmod u+x ~/Scripts/respaldo_auto.sh

Después abre el archivo en tu editor favorito, y agrega las siguientes lineas:

#!/bin/sh

SUFIJO=$(date +%j)

# Borrar el directorio en el servidor remoto vía SSH
# ssh respaldo_remoto 'ls Respaldos/mi_trabajo/respaldo_'$SUFIJO' && rm -r Respaldos/mi_trabajo/respaldo_'$SUFIJO

# Borrar el directorio en el servidor remoto vía SFTP
sftp -b /dev/fd/0 respaldo_remoto <<EOF
cd Respaldos/mi_trabajo
rmdir respaldo_$SUFIJO
exit
EOF

# Actualizar la información, creando una carpeta de respaldo e ignorando el
# el resto de los directorios de respaldo
rsync -ab --recursive --files-from='a_respaldar.txt' --backup-dir=respaldo_$SUFIJO --delete --filter='protect respaldo_*' /home/juan/ respaldo_remoto:Respaldos/mi_trabajo/

Y ahora, creo un archivo llamado a_respaldar.txt, y agrego las carpetas y los archivos que quiero respaldar en el servidor remoto, las direcciones son relativas al directorio que especificamos como directorio origen, en mi caso, ese sería mi directorio personal, así que estos archivos y directorios residen en ese directorio. Este archivo debe estar en el mismo directorio que el script que acabamos de hacer, de otra manera --files-from no lo encontrará:

touch ~/Scripts/a_respaldar.txt

Ábrelo en tu editor favorito y agrega los archivos y directorios que quieres respaldar, en mi caso estos serían:

.vimrc
.vim/
Proyectos/
Documentos/Trabajo/
Scripts/

En este punto, podemos probar el script con:

sh ~/Scripts/respaldo_auto.sh

El paso final es editar nuestro archivo crontab y hacerlo correr el script diariamente:

crontab -e

Y agrega hasta abajo una linea como esta:

@daily /home/juan/Scripts/respaldo_auto.sh

Y esto sería todo, si encuentras algún error o si algo quedó confuso envíame un correo electrónico con confianza.

Notas al pie

Este ejemplo usa Dreamhost por que es lo que tengo disponible por el momento, fuera de carpetas locales.

Como mencione en una publicación previa sobre rsync, la cantidad de opciones de este comando es enorme, y una publicación cubriendo cada aspecto sería demasiado larga, ya que rsync puede ser personalizado para manejar virtualmente cualquier necesidad particular que alguien pudiera tener. El propósito de esta guía es hacer simple el uso para lo que creo que es una necesidad particular común. Cualquiera de los artículos cubriendo rsync pueden ser expandidos en el futuro, o corregidos si encuentro algún error.