sábado, 25 de julio de 2020

Copia de Seguridad Incremental con Tar

Con el comando tar de GNU además de agrupar archivos para distribuirlos podemos realizar copias de seguridad incrementales que nos permitan salvaguardar los datos utilizando el menor espacio posible. Las copias de seguridad incrementales tienen la ventaja de que no hace falta copiar continuamente todos los datos, solo es necesario hacer una copia completa cada cierto tiempo y luego copiar los archivos nuevos o que han sido modificados. Además utilizar el comando tar permite hacer un sistema de copias de seguridad totalmente personalizado e integrado con el resto de comandos y servicios del sistema operativo. Yo para este artículo voy a utilizar el sistema operativo Debian GNU/Linux.

El comando tar de GNU tiene la opción --listed-incremental, abreviada -g. Cuando se agrupan archivos con esta opción se crea un archivo con datos sobre los archivos copiados para más adelante detectar los archivos nuevos o modificados y poder hacer copias incrementales. A continuación se puede ver como se guardan los archivos del directorio /data en el archivo full-backup.tar.bz2 y se guarda la información de los archivos en backup-incremental-data.

# tar cjpf full-backup.tar.bz2 -g backup-incremental-data /data

Los parámetros que se pasan a tar tienen las siguientes funciones:

  • c: Crear un archivo.
  • j: Comprimir con bzip2.
  • p: Conservar los permisos del archivo.
  • f: Indicar el archivo donde se va a guardar el contenido del directorio /data.
  • g: Indicar el archivo con los datos necesarios para la copia incremental.

La primera vez que se ejecuta el comando se realiza una copia completa. Si pasado el tiempo, después de realizar cambios en los archivos del directorio /data o añadir nuevos archivos volvemos a ejecutar el comando tar para crear una nueva copia de seguridad solo se hará una copia incremental de los archivos modificados y nuevos.

# tar cjpf incremental-backup-1.tar.bz2 -g backup-incremental-data /data

Para restaurar los archivos del directorio /data solo tendremos que descomprimir la copia completa full-backup.tar.bz2 y la copia incremental incremental-backup-1.tar.bz2. Para extraer los archivos hay que cambiar el parámetro c por el x.

# tar xjpf full-backup.tar.bz2 incremental-backup-1.tar.bz2

Para volver a crear una nueva copia de seguridad completa solo es necesario eliminar los datos del archivo backup-incremental-data o crear un nuevo archivo de datos incrementales.

Cuando se crea una copia de un directorio utilizando la ruta completa el comando tar elimina la barra inicial de las rutas para que al extraer los archivos no haya peligro de que se sobreescriban los archivos originales por error. El comando tar muestra el siguiente mensaje:

tar: Eliminando la `/' inicial de los nombres

Si no queremos que se muestre ese mensaje, por ejemplo al hacer "scripts", podemos utilizar el parámetro -C para que el comando tar vaya al directorio raíz y desde ahí haga la copia del directorio usando la ruta relativa.

# tar cjpf full-backup.tar.bz2 -C / datos

Ese es el funcionamiento básico de la copia incremental con el comando tar. Para crear un sistema de copias de seguridad necesitamos crear algunos "scripts" y utilizar algunos servicios del sistema operativo.

Lo primero que necesitamos es un directorio donde guardar las copias. Para ello podemos crear un directorio en /var con el nombre incremental-backup. Ya que se van a guardar muchos datos y el disco duro puede sufrir es recomendable que en este directorio esté montada una partición dedicada distinta a la del sistema operativo y si es posible de un disco duro distinto.

# mkdir /var/incremental-backup
# ls -ld /var/incremental-backup

drwxr-xr-x 2 root root 4096 jul 20 18:14 /var/incremental-backup

Las copias de seguridad hay que realizarlas con un usuario del sistema, por ejemplo en Debian existe el usuario backup y es el que yo voy a utilizar. Lo primero que debemos hacer es dar permisos de escritura a este usuario sobre el directorio /var/incremental-backup. Para ello podemos asignar el grupo backup al directorio y dar permisos de escritura al grupo.

# chgrp backup /var/incremental-backup
# chmod g+w /var/incremental-backup
# ls -ld /var/incremental-backup

drwxrwxr-x 2 root backup 4096 jul 20 18:14 /var/incremental-backup

También necesitamos un directorio donde guardar los archivos de configuración, podemos crear el directorio /etc/incremental-backup. En este directorio no debe tener el usuario backup permisos de escritura, solo de lectura, ya que no es necesario que modifique la configuración, solo que pueda leerla.

Los archivos de configuración dependerán de los "scripts" que creemos según las necesidades del sistema de copias de seguridad. Yo a modo de ejemplo he creado un "script" con dos archivos de configuración. En primer lugar el archivo incremental-backup.conf donde fijar las siguientes variables del sistema:

  • DATETIME: Fecha y hora al ejecutar el "script".
  • DIRECTORIES: Archivo con un listado de los directorios de los que hay que hacer copia de seguridad.
  • INCREMENTAL_BACKUP_DIRECTORY: Directorio donde guardar las copias de seguridad.

Para la variable DATETIME se utiliza el comando date y se le pide que muestre la hora en formato año-mes-día_hora-minutos-segundos. No se usa el carácter ":" para el tiempo porque es problemático para utilizarlo en los nombres de archivo.

DATETIME=`date +%F_%H-%M-%S`
DIRECTORIES=/etc/incremental-backup/directories.conf
INCREMENTAL_BACKUP_DIRECTORY=/var/incremental-backup

El archivo de directorios contiene por cada línea la ruta completa de un directorio del que se quiere hacer copia precedido del nombre que se quiere dar al archivo de copia. Los dos valores están separados por ":". Para el archivo de copia se podría utilizar el nombre del directorio del que se hace copia pero poder elegir el nombre da más libertad de organización. Todo esto depende de las necesidades de cada sistema y hay que buscar lo más conveniente.

data-users:/data/users
company-database:/var/database

Por último hace falta el "script" que realice la copia. Un buen lugar para guardarlo es /usr/local/bin para que esté en la ruta de ejecución ($PATH) pero no se mezcle con los ejecutables del sistema operativo. Como nombre le puse incremental-backup.sh", igual que los directorios de copias y configuración seguido de la extensión .sh comúnmente usada en los "scripts".

#!/bin/bash

. /etc/incremental-backup/incremental-backup.conf

while IFS=":" read file directory
do
        backup_file=$INCREMENTAL_BACKUP_DIRECTORY/$file
        incremental_data_file=$backup_file-incremental-data
        
        case "$1" in

                full)
                        rm -f $incremental_data_file
                        backup_file=$backup_file-full
                        echo "$DATETIME - FULL BACKUP: $file"
                        ;;

                incr)
                        backup_file=$backup_file-incr
                        echo "$DATETIME - INCREMENTAL BACKUP: $file"
                        ;;
                *)
                        echo "Usage: incremental-backup.sh {full | incr}"
                        exit 1
        esac

        tar cjpf $backup_file-$DATETIME.tar.bz2 -g $incremental_data_file -C / ${directory:1}

done < $DIRECTORIES

La primera línea del archivo indica que es un "script" y que debe ejecutarse con /bin/bash.

#!/bin/bash

La primera acción del "script" es cargar el archivo de configuración incremental-backup.conf donde se fijan las variables del sistema. En ese archivo se ejecuta el comando date para fijar la variable DATETIME y ese valor es usado durante todo el funcionamiento del "script"

. /etc/incremental-backup/incremental-backup.conf

A continuación se lee el archivo indicado en la variable DIRECTORIES línea a línea mediante un bucle while. Cada línea se divide mediante la marca ":" en el archivo de copia (file) y el directorio del que hay que hacer la copia (directory).

while IFS=":" read file directory
do

...

done < $DIRECTORIES

Por cada directorio se definen dos variables: backup_file con la ruta completa del archivo de copia e incremental_data_file con el archivo con información para copia incremental añadiendo "-incremental-data" a backup_file.

backup_file=$INCREMENTAL_BACKUP_DIRECTORY/$file
incremental_data_file=$backup_file-incremental-data

Al "script" se le puede pasar como parámetro el tipo de copia y este utiliza una sentencia case para realizar diferentes acciones.

case "$1" in

    full)
            ...
    incr)
            ...

    *)

esac

El valor del parámetro puede ser "full" para hacer una copia completa o "incr" para hacer una copia incremental. Al elegir "full" se elimina el archivo con la información de los archivos copiados anteriormente para que al hacer la copia se haga completa.

rm -f $incremental_data_file

Según la opción elegida se añade "full" o "incr" al nombre del archivo para identificarlo como un archivo de copia completa o de copia incremental. Se utiliza la abreviatura "incr" para "incremental" para que el nombre de los archivos tenga el mismo tamaño para los dos tipos de copia y facilite su manejo con otras herramientas. A continuación se muestra un mensaje con la fecha, hora, tipo de copia y nombre del archivo.

backup_file=$backup_file-full
echo "$DATETIME - FULL BACKUP: $file"

...

backup_file=$backup_file-incr
echo "$DATETIME - INCREMENTAL BACKUP: $file"

Por último se ejecuta el comando tar para crear el archivo de copia de seguridad. Al nombre del archivo de copia se le añade la fecha y la hora. Al directorio se le quita la barra inicial de la ruta y se utiliza el parámetro -C para que el comando tar vaya al directorio raíz y utilice una ruta relativa. De esta forma el comando tar no muestra el mensaje "tar: Eliminando la `/' inicial de los nombres".

tar cjpf $backup_file-$DATETIME.tar.bz2 -g $incremental_data_file -C / ${directory:1}

Con todo preparado podemos ejecutar el "script" usando el usuario backup. Comenzaremos por realizar una copia "full" aunque como es la primera y no hay archivo de datos de copia incremental el resultado va a ser el mismo que si usáramos "incr", la única diferencia va a ser el nombre del archivo.

# su backup -s /bin/sh -c "incremental-backup.sh full"

2020-07-20_20-17-53 - FULL BACKUP: data-users
2020-07-20_20-17-53 - FULL BACKUP: company-database

Si miramos el contenido del directorio de copias de seguridad podremos ver como se han creado los archivos de copia y de información de copia incremental.

# ls -lh /var/incremental-backup

total 178M
-rw-r--r-- 1 backup backup 22K jul 20 20:23 company-database-incremental-data
-rw-r--r-- 1 backup backup 89M jul 20 20:23 company-database-full-2020-07-20_20-17-53.tar.bz2
-rw-r--r-- 1 backup backup 22K jul 20 20:20 data-users-incremental-data
-rw-r--r-- 1 backup backup 89M jul 20 20:20 data-users-full-2020-07-20_20-17-53.tar.bz2

Si a continuación añadimos un archivo nuevo a /data/users y hacemos una copia incremental veremos como se han creado nuevos archivos con solo los cambios.

# su backup -s /bin/sh -c "incremental-backup.sh incr"

2020-07-20_23-37-50 - INCREMENTAL BACKUP: data-users
2020-07-20_23-37-50 - INCREMENTAL BACKUP: company-database

# ls -lh /var/incremental-backup

total 179M
-rw-r--r-- 1 backup backup  22K jul 20 23:37 company-database-incremental-data
-rw-r--r-- 1 backup backup  89M jul 20 20:23 company-database-full-2020-07-20_20-17-53.tar.bz2
-rw-r--r-- 1 backup backup 9,0K jul 20 23:37 company-database-incr-2020-07-20_21-37-50.tar.bz2
-rw-r--r-- 1 backup backup  22K jul 20 23:37 data-users-incremental-data
-rw-r--r-- 1 backup backup  89M jul 20 20:20 data-users-full-2020-07-20_20-17-53.tar.bz2
-rw-r--r-- 1 backup backup 1,5M jul 20 23:37 data-users-incr-2020-07-20_21-37-50.tar.bz2

A continuación se muestra un resumen de los archivos y directorios creados:

  • Configuración del sistema: /etc/incremental-backup/incremental-backup.conf
  • Directorios de los que hay que hacer copia: /etc/incremental-backup/directories.conf
  • Programa que realiza las copias: /usr/local/bin/incremental-backup.sh
  • Directorio donde se guardan las copias: /var/incremental-backup

Una vez probado el "script" el último paso es hacer que se ejecuten automáticamente las copias completas e incrementales en los días y horas necesarios. También es conveniente guardar registros de las ejecuciones eliminando los registros antiguos. Esto lo podemos hacer usando los programas cron y logrotate.

Con el uso de todas estas tecnologías podemos tener un sistema de copias de seguridad completamente adaptado a nuestras necesidades y muy fácil de mantener ya que tenemos control sobre cada uno de sus componentes. Además el conocimiento de estos sistemas nos puede servir para otro tipo de tareas distinto de las copias de seguridad. Otro programa que se puede utilizar para hacer copias incrementales es rsync. Para otros artículos me queda la copia de seguridad con monitorización contínua de cambios y grabación en cinta.

2 comentarios:

  1. Muy interesante. Tengo una pregunta: en tu ejemplo, en la segunda ejecución del script, ¿por qué se general el fichero "company-database-incr-2020-07-20_21-37-50.tar.bz2", siendo así que no has hecho cambios en el directorio "/var/database"? Gracias.

    ResponderEliminar
    Respuestas
    1. tar siempre genera el fichero de copia de seguridad. Si no ha habido cambios solo incluye en el la estructura de directorios.

      Eliminar