Crea y restaura copias incrementales con GNU tar
Usa la opción --listed-incremental de GNU tar para crear una copia de seguridad completa
seguida de archivos tar con los cambios. Esta guía te permite crear una cadena de copias de seguridad
locales y realizar una prueba de restauración que verifica el contenido de los archivos, incluido lo
que ocurre cuando añades, modificas o eliminas archivos. Cada copia de seguridad tiene su propia
copia de la instantánea, por lo que una ejecución fallida no puede hacer avanzar el estado de la
última copia de seguridad correcta.
Prepara un pequeño conjunto de archivos de origen
Usa Linux, Bash, GNU tar, gzip y las utilidades estándar diff y de archivos.
Los ejemplos se probaron con GNU tar 1.35 y Bash 5; comprueba que
tar --version identifique GNU tar. Los archivos tar incrementales de GNU usan
extensiones que otras implementaciones de tar podrían no admitir. Consulta el
manual de copias de seguridad incrementales de GNU tar.
tar --version
bash --version
gzip --version
Ejecuta los siguientes bloques en Bash desde el mismo directorio inicial. Los paréntesis mantienen
los cambios de directorio y las opciones del shell dentro de cada bloque. La preparación rechaza la
reutilización de un directorio tar-demo existente.
(
set -euo pipefail
mkdir tar-demo
cd -- tar-demo
mkdir -p project/cache backups
printf 'version one\n' > project/notes.txt
printf 'keep this file\n' > project/unchanged.txt
printf 'remove this file later\n' > project/obsolete.txt
printf 'rebuildable cache\n' > project/cache/item.txt
)
Mantén backups fuera del árbol de origen, como se muestra aquí, para que los
archivos tar no puedan incluir su propia salida. Usa un origen en el que no se esté escribiendo
durante la copia de seguridad. Este ejercicio abarca archivos comunes de un directorio local; no
crea una copia de seguridad coherente de una base de datos en ejecución ni conserva todos los tipos
de metadatos del sistema.
Guarda cada copia de seguridad con su instantánea
El archivo de instantánea registra lo que tar encontró durante la copia de seguridad anterior. Si no hay una instantánea, se inicia una copia de seguridad completa, o de nivel cero. Al reutilizar una, se registran los cambios desde esa instantánea, y tar la actualiza durante la creación. Copia la instantánea anterior antes de crear el siguiente archivo tar; mantén intacta la copia anterior.
Guarda este script como tar-demo/backup.sh. Su primer argumento indica el nombre de un
nuevo directorio de copia de seguridad. El segundo argumento, opcional, indica la copia de seguridad
anterior que se completó correctamente. Los nombres pueden contener letras ASCII, dígitos, guiones
bajos y guiones.
#!/usr/bin/env bash
set -euo pipefail
unset TAR_OPTIONS
cd -- "$(dirname -- "$0")"
if (( $# < 1 || $# > 2 )); then
printf 'Usage: bash backup.sh NAME [PREVIOUS]\n' >&2
exit 2
fi
for name in "$@"; do
case "$name" in
''|*[!a-zA-Z0-9_-]*)
printf 'Invalid backup name: %s\n' "$name" >&2
exit 2
;;
esac
done
output="backups/$1"
previous=${2:-}
if [[ -n "$previous" ]]; then
for file in complete archive.tar.gz state.snar; do
if [[ ! -f "backups/$previous/$file" ]]; then
printf 'Previous backup is incomplete: %s\n' "$previous" >&2
exit 1
fi
done
fi
# Reserve a new directory before installing cleanup that can remove it.
mkdir -- "$output"
trap 'rm -rf -- "$output"' EXIT
trap 'exit 1' HUP INT TERM
if [[ -n "$previous" ]]; then
cp -- "backups/$previous/state.snar" "$output/state.snar"
fi
tar --create --file=- \
--listed-incremental="$output/state.snar" \
--exclude='project/cache' \
-- project | gzip > "$output/archive.tar.gz"
gzip --test -- "$output/archive.tar.gz"
touch -- "$output/complete"
trap - EXIT
printf 'Created %s\n' "$output"
Ejecútalo una vez sin una copia predecesora:
bash tar-demo/backup.sh 00-full
Esto crea backups/00-full/archive.tar.gz, state.snar y un marcador vacío
complete dentro de tar-demo. El marcador registra que
se completaron la creación del archivo tar y la comprobación con gzip. No sustituye una prueba de
restauración. Si el directorio de salida ya existe, el script falla antes de cambiar su contenido,
incluso cuando la entrada no es interactiva. Ejecuta solo una copia de seguridad a la vez.
Mantén las mismas exclusiones
La expresión entre comillas --exclude='project/cache' omite ese directorio y su contenido.
Mantén la misma ruta de origen y las mismas exclusiones en toda la cadena. Si cambias lo que debe
incluirse en la copia de seguridad, inicia una nueva copia completa con un nombre nuevo. Seleccionar
archivos recientes con find -mtime no proporciona el registro de entradas de
directorio eliminadas de este flujo de trabajo; deja que tar recorra el directorio de origen.
Gzip comprime cada archivo tar de forma independiente. No decide qué archivos han cambiado. Para conocer las opciones de compresión por separado, consulta tar con Zstd y LZ4.
Captura los cambios en orden
Modifica un archivo, añade otro y elimina el archivo obsoleto. La breve pausa separa las marcas de tiempo de este pequeño ejemplo en sistemas de archivos con baja resolución temporal. La selección incremental de GNU tar depende de las marcas de tiempo, así que no atrases el reloj durante una cadena.
(
set -euo pipefail
cd -- tar-demo
sleep 1
printf 'version two\n' > project/notes.txt
printf 'new file\n' > project/added.txt
rm -- project/obsolete.txt
bash backup.sh 01-change 00-full
)
Inspecciona el archivo tar que contiene los archivos modificados:
tar --list --gzip --file=tar-demo/backups/01-change/archive.tar.gz
Contiene project/, project/notes.txt y
project/added.txt; el orden puede variar. La entrada de directorio también contiene
metadatos incrementales. unchanged.txt sigue dependiendo de la copia de seguridad
completa, mientras que la eliminación de obsolete.txt se aplicará durante la
extracción incremental.
Haz un cambio más y usa 01-change como copia predecesora:
(
set -euo pipefail
cd -- tar-demo
sleep 1
printf 'version three\n' > project/notes.txt
bash backup.sh 02-change 01-change
)
La cadena resultante es 00-full → 01-change → 02-change. Para llegar al estado final, conserva los
tres archivos tar. Elegir de nuevo 00-full como copia predecesora crearía otra
copia de seguridad relativa a esa copia completa, en lugar de extender esta secuencia.
Restaura la cadena completa en un directorio vacío
La extracción incremental de GNU tar puede eliminar archivos que no estaban presentes en un
directorio archivado. Restaura solo tus propios archivos tar de confianza en un destino nuevo,
nunca sobre tu origen en uso. Usa --listed-incremental=/dev/null en cada extracción, incluida
la de la copia completa. La extracción lee el historial de directorios de los archivos tar; no
necesita los archivos .snar guardados.
(
set -euo pipefail
cd -- tar-demo
mkdir restore
for generation in 00-full 01-change 02-change; do
test -f "backups/$generation/complete"
gzip --test -- "backups/$generation/archive.tar.gz"
tar --extract --gzip --listed-incremental=/dev/null \
--file="backups/$generation/archive.tar.gz" --directory=restore
done
diff --recursive --exclude=cache project restore/project
test ! -e restore/project/obsolete.txt
printf 'Restored files match the source, excluding cache.\n'
)
Si todo sale bien, diff no imprime nada y la última línea confirma la
comparación. El archivo restaurado notes.txt contiene
version three, added.txt contiene
new file, unchanged.txt se conserva y
obsolete.txt ya no está. No se restaura ningún directorio
cache. Esta operación compara los nombres y el contenido de los archivos,
no sus propietarios, permisos ni atributos extendidos.
Una nueva ejecución rechaza un directorio restore existente antes de extraer
cualquier contenido. Tras una restauración fallida, el directorio puede contener resultados
parciales; inspecciona el error y elige un destino nuevo y vacío para la siguiente prueba. No
consideres una restauración parcial como datos recuperados.
Rechaza las copias de seguridad incompletas
Que un compresor termine correctamente no demuestra que el proceso que genera su entrada también
lo haya hecho. La opción pipefail de Bash
hace que un fallo en tar | gzip provoque el fallo del pipeline, aunque gzip
termine correctamente de comprimir un archivo tar parcial. El script elimina entonces solo el
directorio nuevo que reservó. El archivo tar y la instantánea anteriores permanecen sin cambios,
así que vuelve a intentarlo con la misma copia predecesora después de resolver el problema.
No añadas --ignore-failed-read a este script. La documentación de GNU tar indica que hace
que las entradas faltantes o ilegibles y los archivos que cambian mientras se leen dejen de afectar
al estado de salida. Eso es lo contrario de detectar una copia de seguridad incompleta. Consulta
--ignore-failed-read.
Mantén visibles los mensajes de diagnóstico y rechaza cualquier salida distinta de cero, incluido
el estado 1 para archivos modificados durante la creación. Detén el proceso que escribe o usa una
instantánea adecuada del sistema de archivos antes de volver a intentarlo con un origen que cambia.
tar --list comprueba si tar puede leer la estructura del archivo tar; las sumas
de comprobación de las cabeceras de tar no cubren el contenido de los archivos.
gzip --test
comprueba la integridad del flujo comprimido, pero no puede determinar si se omitió un archivo de
entrada. Por eso este flujo de trabajo comprueba los fallos de los comandos y restaura el
resultado para compararlo. Las
notas de GNU tar sobre la corrupción de datos
explican esta distinción.
Mantén juntas las cadenas completas de copias de seguridad
Conserva juntos el archivo tar y la instantánea de cada directorio creado correctamente. El archivo
tar se necesita para la recuperación; la instantánea se necesita para continuar la cadena. Un corte
de energía o SIGKILL pueden impedir que se ejecute el manejador de limpieza y
dejar un directorio sin complete. Una vez que no haya ningún proceso de copia
de seguridad en ejecución, inspecciona ese directorio incompleto y elimínalo antes de volver a
intentarlo; no lo uses como copia predecesora.
Estas copias locales no protegen contra la pérdida del disco. Copia los directorios de copias de seguridad completadas a un almacenamiento separado y repite la prueba de restauración desde esas copias. Este ejemplo no implementa una transferencia por SSH ni la división de archivos tar. Si un sistema de almacenamiento divide un archivo tar, todas sus partes deben volver a unirse antes de la comprobación con gzip y la restauración.
Conserva la copia de seguridad completa y cada archivo tar incremental que necesite un punto de recuperación conservado. Eliminar archivos tar individuales por su antigüedad puede romper esa cadena. Inicia una nueva copia de seguridad completa periódicamente, prueba su proceso de recuperación y retira las cadenas antiguas como unidades según tu política de retención.
