Backups con tar + rsync + rotación

En estos días tuve que configurar unos backups para un cliente. Algo bastante sencillo. Tenía dos servidores y necesitaba hacer backups de distintas cosas en cada equipo y copiarlos al otro. Por supuesto, la herramienta clave para esto es rsync. Googleando un poco vi que existe esta página donde se puede ver cómo generar snapshots incrementales automáticos usando hard links y rsync. Realmente es una solución muy interesante, pero a mí no me terminaba de convencer porque yo quería tener archivos comprimidos que fueran rotando.

Por lo tanto lo que hice fue, seguramente, reinventar la rueda una vez más. Hice una pequeña librería en BASH que luego reutilicé en los distintos scripts de backup que hice. La clave aquí es la función “create_archive” que toma un path, un nombre de archivo base y una lista de archivos, y se ocupa de ir al path, ver todos los archivos que haya con ese patrón de nombre básico, rotarlos y generar un nuevo tar.gz. Después agregué un par de funciones más que me venían bien, “log” para registrar todo lo que pasaba en un archivo, “mail_notification” para mandarme mails y “sync_backup_servers” para ejecutar rsync.

El código:

#!/bin/bash
CONF_FILE="/home/sysbackup/scripts/backup.conf"
if [ -f $CONF_FILE ]; then
    . $CONF_FILE
else
    echo "Configuration file $CONF_FILE not found"
    exit 1
fi

RM_BIN="/bin/rm"
TAR_BIN="/bin/tar"
MAIL_BIN="/bin/mail"
RSYNC_BIN="/usr/bin/rsync"

function log
{
    echo "`date`: $*" >> $LOG_FILE
}

function mail_notification
{
    subject="$1"
    message="$2"

    if [ -z "$subject" ]; then
        subject="Backup Notification"
    fi

    if [ -z "$NOTIFICATIONS_MAILS" ]; then
        log "mail_notification: empty recipients list"
        return 1
    fi

    if [ -z "$message" ]; then
        log "mail_notification: empty message"
        return 1
    fi

    sysdata="Date: `date`\nHost: `hostname`"

    echo -e "$message\n\n$sysdata" | $MAIL_BIN -s "$subject" "$NOTIFICATIONS_MAILS"
}

function sync_backup_servers
{
    log "Running rsync from $LOCAL_DIR to $REMOTE_DIR as $RSYNC_USER"
    su -c "$RSYNC_BIN -aq –delete -e ssh $LOCAL_DIR/ $REMOTE_DIR/ 2>> $LOG_FILE" $RSYNC_USER
    if [ $? -eq 0 ]; then
        log "Syncronization complete successfuly"
    else
        log "Syncronization finished with errors"
    fi
}

function create_archive
{
    file_path="$1"
    file_name="$2"
    files="${*:3}"

    if [ -z $file_path ]; then
        echo "Missing file path"
        return 1
    fi

    if [ -z $file_name ]; then
        echo "Missing file name"
        return 1
    fi

    if [ -z $files ]; then
        echo "Missing file list"
        return 1
    fi

    if [ -z $MAX_BKP_FILES ]; then
        MAX_BKP_FILES=4
    fi

    archive_name="${file_name}_`date +%Y%m%d`.tar.gz"
    log "Preparing to create $archive_name archive"

    # Begin to Rotate files
    # Sort files by date and keep the latest ones
    # MAX_BKP_FILES will determine how many to store
    dir=`ls -t $file_path/${file_name}_*.tar.gz 2> /dev/null`
    if [ $? -eq 0 ]; then
        N=0
        for file in $dir; do
            if [ $N -lt $MAX_BKP_FILES ]; then
                let N++
            else
                log "Removing old file $file"
                $RM_BIN -f $file
            fi
        done
    else
        log "No other archive found on $file_path"
    fi

    log "Creating tar archive $file_path/$archive_name"
    $TAR_BIN zcf "$file_path/$archive_name" $files 2>> $LOG_FILE
}
 

Algunas consideraciones importantes.

Configuración

Al principio del script lo que hago es incluir un archivo de configuración con algunas variables básicas que voy a utilizar. Este archivo debería ser algo similar a esto:

#!/bin/bash
LOCAL_DIR="/home/sysbackup/backup/server1.domain.com"
REMOTE_DIR="sysbackup@server1.domain.com:/home/sysbackup/backup/server1.domain.com"
LOG_FILE="/var/log/sysbackup.log"
MAX_BKP_FILES=4
NOTIFICATIONS_MAILS="admin@email.com"
RSYNC_USER="sysbackup"
 

LOCAL_DIR es el directorio local base donde van a estar mis backups. REMOTE_DIR es el directorio equivalente en el otro servidor. Lo que hice fue crear un usuario “sysbackup” (un poco menos obvio que “backup”, pero también self-explanatory) en los dos servidores y generar llaves para que se puedan conectar por SSH entre los dos servidores. Luego en la /home de cada usuario creé una carpeta “backup” y adentro de ella una carpeta para cada host que iba a ser backupeado “server1.domain.com” y “server2.domain.com”.

LOG_FILE indica el archivo donde se van a guardar los logs del backup y MAX_BKP_FILES determina la cantidad máxima de archivos de backup que va a haber por cada elemento.

Rotación

Como dije, la función más importante es create_archive. La pensé para logs que se crean una vez por día (o cada más tiempo), por lo que si la quieren usar para rotar logs generados en intervalos de tiempo más cortos van a tener que modificarla. Lo que hace es generar un nombre de archivo con una raiz elegida por nosotros (digamos “db”) y la fecha actual (la función pone la fecha, pero si necesitan intervalos más cortos van a tener que poner fecha y hora). Luego hace un `ls -t` del directorio donde hay que generar el backup para ver si hay otros archivos que coincidan con el patrón del nombre correspondiente (por ejemplo, ‘db_*.tar.gz’). Si hay archivos, va a conservar los últimos 4 (en realidad la cantidad la determina MAX_BKP_FILES) y va a borrar los más viejos. Por último, va a generar el tar.gz con la lista de archivos que le hayamos pasado.

Ejemplo:

create_archive "$LOCAL_DIR/web" "web" "/var/www/html"
 

Va a generar con un archivo de nombre como “web_20100902.tar.gz” en la carpeta $LOCAL_DIR/web con todos los contenidos de /var/www/html.

Rsync

La función sync_backup_servers hace la sincronización entre los dos servidores usando rsync por SSH. Una cosa a tener en cuenta es que en mi caso, yo necesitaba que el script de backup corriera como root (para poder acceder a todos los archivos que había que backupear) pero quería que el rsync se ejecutara con el usuario sysbackup (para mayor seguridad). Por lo tanto la función usa “su -c sysbackup”. Si a uds. este enfoque no les sirve pueden poner en la función directamente:

$RSYNC_BIN -aq –delete -e ssh $LOCAL_DIR/ $REMOTE_DIR/ 2>> $LOG_FILE
 

Notificaciones por mail

Es muy útil poder tener notificaciones por mail, sobre todo cuando las cosas andan mal. El comando “mail” de Linux/Unix es muy útil para esto. La funcion mail_notification usa ese comando para mandar un mail con el mensaje que queramos y le agrega dos datos útiles: la fecha y el hostname desde el que se manda. En realidad son datos que siempre viajan en los headers del mail, pero a mí me parecía útil que estuvieran en el body.

Espero que les sirvan estos recursos. No son una solución completa y cerrada, sino herramientas que quizás les sirvan para implementar o al menos pensar cómo hacer sus propios sistemas de backup. Les recomiendo enfáticamente que lean este artículo sobre cómo generar backups con rsync porque explica muchas cosas fundamentales.

Comentar

1 Comentarios.

  1. Buenisimo!!! hace rato que quería hacer lo mismo pero no tenía tiempo xD, los actuales scripts de backup que uso no contemplan rotación (y aveces ni andar).

    Saludos

Comentar


[ Ctrl + Enter ]