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

Una vez probado el "script" el siguiente paso es hacer que se ejecuten automáticamente las copias completas e incrementales en los días y horas necesarios. Para ello debemos añadir unas programaciones a la aplicación cron creando el archivo incremental-backup en el directorio /etc/cron.d con el siguiente contenido:

# Full Backup - Saturday 04:00
0  4 * * 6   backup /usr/local/bin/incremental-backup.sh full 

# Incremental Backup Tuesday - Friday 04:00
0  4 * * 2-5 backup /usr/local/bin/incremental-backup.sh incr 

# Incremental Backup Monday - Friday 12:00
0 12 * * 1-5 backup /usr/local/bin/incremental-backup.sh incr 

Suponemos que estamos haciendo un sistema de copias de seguridad para una empresa en la que se trabaja de lunes a viernes. Así que necesitamos hacer una copia incremental todas las madrugadas de martes a viernes con los datos nuevos del día anterior. Llegado el sábado en lugar de una copia incremental hacemos una copia completa, de esta manera para recuperar todos los datos de un día determinado solo necesitaremos extraer el contenido de una copia completa y entre una y cuatro incrementales.

Podemos aumentar el número de copias incrementales haciendo una a media jornada, de esta forma no se perderán datos creados a primera hora y borrados antes de la siguiente copia. Esto todavía deja la posibilidad de perder datos creados y borrados o modificados entre dos copias incrementales. Si necesitamos que quede registro de cualquier cosa que se escriba ya tendríamos que usar otro tipo de sistema de copias de seguridad que estuviera monitorizando continuamente los archivos. Esto lo dejaré para otro artículo.

Como hoy día disponemos de discos duros inmensos y además podemos hacer RAIDs con ellos, no hay problema en guardar copias de seguridad de varios años. Si tenemos limitación de espacio cada cierto tiempo podemos eliminar manualmente las copias antiguas, archivarlas en otro lugar o por ejemplo dejar una sola copia completa por mes de años anteriores. También podemos crear un "script" que haga este trabajo por nosotros y ejecutarlo manualmente o a través de cron. Por ejemplo para eliminar o archivar copias más antiguas a un año podemos usar el comando find para encontrar las copias de más de 365 días y ejecutar sobre cada una de ellas el comando rm para eliminarlas o mv para moverlas a otro lugar.

# find /var/incremental-backup/*.tar.bz2 -mtime +365 -exec rm {} \;

Que el borrado se haga automáticamente tiene el peligro de que si por alguna razón el ordenador tiene una fecha errónea se pueden eliminar archivos que no queremos que se borren. Hacer el borrado o archivado manualmente nos da más seguridad de que los archivos tienen la fecha deseada.

Si queremos que quede un registro de cada ejecución del "script" o de los errores que pudieran suceder podemos redirigir la salida estándar y la de errores a un archivo "log", por ejemplo /var/log/incremental-backup.log. Cron utiliza por defecto el interprete de comandos /bin/sh, donde >> redirige la salida estándar al archivo y la escribe a continuación de lo ya escrito y 2>&1 redirige la salida de errores a la salida estándar, haciendo que todos los mensajes acaben agregándose al archivo "log".

# Full Backup - Saturday 04:00
0  4 * * 6   backup /usr/local/bin/incremental-backup.sh full >> /var/log/incremental-backup.log 2>&1

# Incremental Backup Tuesday - Friday 04:00
0  4 * * 2-5 backup /usr/local/bin/incremental-backup.sh incr >> /var/log/incremental-backup.log 2>&1

# Incremental Backup Monday - Friday 12:00
0 12 * * 1-5 backup /usr/local/bin/incremental-backup.sh incr >> /var/log/incremental-backup.log 2>&1

Al principio del archivo /etc/cron.d/incremental-backup se puede indicar el interprete de comandos con la variable $SHELL. Por ejemplo se puede usar /bin/bash para poder redirigir la salida estándar y la de errores usando solamente &>>.

SHELL=/bin/bash

Para que el usuario backup pueda escribir en ese archivo "log" debemos crearlo y darle los permisos necesarios de forma similar a la creación del directorio /var/incremental-backup.

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

-rw-rw-r-- 1 root backup 99 jul 20 20:34 /var/log/incremental-backup.log

De esta forma cuando se cree otra copia de seguridad los mensajes que se muestran por pantalla al ejecutar el "script" serán grabados en el archivo "log".

# cat /var/log/incremental-backup.log

2020-07-21_18-34-05 - INCREMENTAL BACKUP: data-users
2020-07-21_18-34-05 - INCREMENTAL BACKUP: company-database

Para la fecha y hora de los mensajes que se muestran por pantalla o se redirigen al archivo "log" he utilizado la misma fecha que para los archivos. También se podría modificar el "script" para que en la fecha de los mensajes utilizara el formato usado en otros archivos "log". Al ejecutar date se debe fijar la variable LC_ALL=POSIX o C para que no utilice el idioma del sistema para escribir la abreviatura del nombre del mes.

# LC_ALL=POSIX date +"%b %e %T"

Jul 21 18:44:38

Como solo se van a hacer unas cuantas copias al día el archivo "log" no crecerá mucho pero tenemos la posibilidad de configurar logrotate para que vaya archivando los registros antiguos en otros archivos y los elimine con el tiempo. Para configurarlo podemos crear el archivo incremental-backup en el directorio /etc/logrotate.d con el siguiente contenido:

/var/log/incremental-backup.log {
        monthly
        rotate 12
        compress
        delaycompress
        notifempty
        create 664 root backup
}

Según esta configuración cada mes (monthly) va a renombrar el archivo incremental-backup.log, si no está vacío (notifempty), a incremental-backup.log.1 y no lo va a comprimir (delaycompress). A continuación va a crear un nuevo archivo incremental-backup.log con dueño root, grupo backup y permisos de escritura para el dueño y el grupo (create 664 root backup) para que el usuario backup pueda seguir escribiendo en él.

Antes de eso, si ya existía el archivo incremental-backup.log.1, lo va a renombrar a incremental-backup.log.2.gz y lo va a comprimir con gzip (compress). Si existía el archivo incremental-backup.log.2.gz lo renombrará previamente a incremental-backup.log.3.gz y así sucesivamente. Los archivos van rotando y cuando se han creado doce archivos (rotate 12) el número 12 es eliminado y el 11 es renombrado a 12.

Al cabo de doce meses tendremos los siguientes archivos:

# ls -1 /var/log/incremental-backup.log*

/var/log/incremental-backup.log
/var/log/incremental-backup.log.1
/var/log/incremental-backup.log.10.gz
/var/log/incremental-backup.log.11.gz
/var/log/incremental-backup.log.12.gz
/var/log/incremental-backup.log.2.gz
/var/log/incremental-backup.log.3.gz
/var/log/incremental-backup.log.4.gz
/var/log/incremental-backup.log.5.gz
/var/log/incremental-backup.log.6.gz
/var/log/incremental-backup.log.7.gz
/var/log/incremental-backup.log.8.gz
/var/log/incremental-backup.log.9.gz

A continuación se muestra un resumen de los archivos 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
  • Programación de copias completas e incrementales en cron: /etc/cron.d/incremental-backup
  • Archivo de registros: /var/log/incremental-backup.log
  • Configuración de rotación del archivo de registros en logrotate: /etc/logrotate.d/incremental-backup

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. Para otros artículos me queda la copia de seguridad con monitorización continua de cambios, uso de rsync para copias de seguridad, grabación en cinta y uso de los servicios de systemd.

No hay comentarios:

Publicar un comentario