🎨 Sysprovider Code
Sysprovider LogoWiki
🇪🇸Hosting español para ecommerce

Automatización de backups incrementales con rsync y snapshots LVM

Actualizado el 3 de diciembre de 2025

La gestión de datos en entornos de producción exige estrategias de backup que minimicen la ventana de respaldo, reduzcan el consumo de almacenamiento y garanticen la consistencia de los datos. La combinación de rsync para la transferencia eficiente de diferencias a nivel de archivo y los snapshots LVM (Logical Volume Manager) para obtener puntos de restauración consistentes a nivel de bloque constituye una de las arquitecturas más robustas, flexibles y económicas para servidores Linux.

Este artículo desglosa la implementación de un sistema de backups incrementales que aprovecha la atomicidad de LVM y la eficiencia de rsync, proporcionando un flujo de trabajo profesional, reproducible y auditable.

Fundamentos Técnicos: Por qué LVM + rsync

Antes de sumergirnos en la implementación, es crucial entender el "por qué" de cada componente.

El problema del backup en caliente (Hot Backup)

Realizar una copia de seguridad de un sistema de archivos montado mientras está en uso activo (por ejemplo, una base de datos MySQL o un directorio de correos) puede resultar en una copia inconsistente. Los archivos pueden estar siendo escritos en el momento exacto de la copia, resultando en datos corruptos o transacciones incompletas.

Snapshot LVM resuelve esto proporcionando una vista congelada y consistente del volumen lógico en un instante T. No es una copia física de los datos, sino un "enlace" a los bloques originales que registra los cambios posteriores (Copy-on-Write). Esto permite montar el snapshot como un sistema de archivos de solo lectura y realizar el backup sin detener los servicios.

La eficiencia de rsync vs. copias completas

  • cp o tar: Cada backup es una copia completa. Ineficiente en espacio y tiempo.
  • rsync: Transfiere solo los bloques de datos que han cambiado entre el origen y el destino. Utiliza un algoritmo de checksum para identificar diferencias a nivel de bloque y luego a nivel de archivo. Esto reduce drásticamente el tráfico de red y el tiempo de ejecución.

La sinergia

  1. LVM proporciona la instantánea consistente.
  2. Montamos el snapshot como solo lectura.
  3. rsync sincroniza el contenido del snapshot con un directorio de destino (local o remoto).
  4. Eliminamos el snapshot para liberar espacio.

Este flujo garantiza que rsync siempre vea un sistema de archivos consistente, incluso si el servidor está bajo alta carga de escritura.

Arquitectura de la Solución

Diseñaremos un sistema que realiza backups incrementales diarios con retención configurable (por ejemplo, 7 días, 4 semanas, 12 meses).

Estructura de Directorios

/backup/
├── daily/
│   ├── 2023-10-27/
│   ├── 2023-10-26/
│   └── ...
├── weekly/
│   ├── 2023-10-23/
│   └── ...
└── monthly/
    └── 2023-10-01/

Flujo de Trabajo General

  1. Pre-snapshot: Forzar sync de FS y bloquear escrituras (si es necesario, ej: FLUSH TABLES WITH READ LOCK en MySQL).
  2. Crear snapshot LVM: lvcreate --snapshot.
  3. Post-snapshot: Liberar bloqueos de escritura.
  4. Montar snapshot: mount -o ro.
  5. Ejecutar rsync: Sincronizar snapshot con el directorio daily/YYYY-MM-DD/.
  6. Desmontar y eliminar snapshot: umount y lvremove.
  7. Rotación: Mover backups diarios a semanales y mensuales según política.

Implementación Paso a Paso

1. Preparación del Entorno LVM

Asumimos que los datos a respaldar residen en un volumen lógico llamado datos dentro del grupo de volúmenes vg_servidor.

# Verificar la estructura LVM
sudo lvs
sudo vgs
sudo pvs

Salida esperada:

  LV     VG         Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  datos  vg_servidor -wi-ao---- 100.00g

Nota técnica: El atributo -wi-ao---- indica que el volumen está "writeable", "active" y "open". Es fundamental que el volumen esté activo.

2. Script de Backup Incremental (Backup.sh)

Crearemos un script en /usr/local/bin/backup_lvm_rsync.sh.

#!/bin/bash
# Script de backup incremental con LVM snapshot y rsync
# Versión: 2.1
# Autor: Sysadmin

# --- Configuración ---

VG_NAME="vg_servidor"

LV_NAME="datos"

SNAPSHOT_NAME="datos_snap"

SNAPSHOT_SIZE="10G"  # 10% del tamaño del LV original es una regla general

MOUNT_POINT="/mnt/snapshot"

BACKUP_ROOT="/backup"

RSYNC_USER="backup_user"

RSYNC_SERVER="192.168.1.100"  # Servidor de backups remoto

RSYNC_PATH="/remote_backup"   # Ruta en el servidor remoto

LOG_FILE="/var/log/backup_lvm.log"

RETENTION_DAYS=7

RETENTION_WEEKS=4

RETENTION_MONTHS=12

# --- Funciones ---
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

cleanup() {
    log_message "Iniciando limpieza por error..."
    # Desmontar si está montado
    if mountpoint -q "$MOUNT_POINT"; then
        umount "$MOUNT_POINT" && log_message "Snapshot desmontado."
    fi
    # Eliminar snapshot si existe
    if lvs --noheadings -o lv_name | grep -q "^$SNAPSHOT_NAME$"; then
        lvremove -f "/dev/$VG_NAME/$SNAPSHOT_NAME" && log_message "Snapshot eliminado."
    fi
    exit 1
}

# --- Ejecución Principal ---
log_message "=== INICIO BACKUP ==="

# 1. Crear el snapshot LVM
log_message "Creando snapshot LVM de $LV_NAME..."
lvcreate --snapshot --name "$SNAPSHOT_NAME" --size "$SNAPSHOT_SIZE" "/dev/$VG_NAME/$LV_NAME"
if [ $? -ne 0 ]; then
    log_message "ERROR: No se pudo crear el snapshot."
    cleanup
fi
log_message "Snapshot creado: /dev/$VG_NAME/$SNAPSHOT_NAME"

# 2. Montar el snapshot como solo lectura
mkdir -p "$MOUNT_POINT"
mount -o ro "/dev/$VG_NAME/$SNAPSHOT_NAME" "$MOUNT_POINT"
if [ $? -ne 0 ]; then
    log_message "ERROR: No se pudo montar el snapshot."
    cleanup
fi
log_message "Snapshot montado en $MOUNT_POINT"

# 3. Ejecutar rsync incremental
# La fecha se usa para el directorio de destino

BACKUP_DATE=$(date +%Y-%m-%d)

DEST_DIR="$BACKUP_ROOT/daily/$BACKUP_DATE"

log_message "Iniciando rsync hacia $DEST_DIR..."

# Opciones de rsync explicadas:
# -a: Modo archivo (recursivo, preserva permisos, timestamps, dueños, etc.)
# -v: Verboso para logs detallados
# -z: Comprime datos durante la transferencia
# --delete: Elimina archivos en destino que ya no existen en origen
# --link-dest: Crea enlaces duros a backups anteriores para ahorrar espacio
# -e: Especifica el shell remoto (ssh en este caso)

# Buscar el último backup exitoso para usar como base para enlaces duros

LAST_BACKUP=$(ls -1dt "$BACKUP_ROOT/daily"/*/ 2>/dev/null | head -n 1)

if [ -n "$LAST_BACKUP" ]; then
    LINK_DEST_OPTION="--link-dest=$LAST_BACKUP"
    log_message "Usando backup anterior como base: $LAST_BACKUP"
else
    LINK_DEST_OPTION=""
    log_message "Primer backup. No hay backup anterior para enlazar."
fi

# Ejecutar rsync hacia destino local
rsync -avz --delete $LINK_DEST_OPTION "$MOUNT_POINT/" "$DEST_DIR/" 2>&1 | tee -a "$LOG_FILE"

# Opción remota (descomentar si se usa servidor remoto)
# rsync -avz --delete -e "ssh -i /root/.ssh/backup_key" $LINK_DEST_OPTION "$MOUNT_POINT/" "$RSYNC_USER@$RSYNC_SERVER:$RSYNC_PATH/daily/$BACKUP_DATE/" 2>&1 | tee -a "$LOG_FILE"

if [ $? -ne 0 ]; then
    log_message "ERROR: rsync falló."
    cleanup
fi
log_message "rsync completado exitosamente."

# 4. Limpieza del snapshot
umount "$MOUNT_POINT"
lvremove -f "/dev/$VG_NAME/$SNAPSHOT_NAME"
log_message "Snapshot eliminado."

# 5. Rotación de backups (política de retención)
log_message "Iniciando rotación de backups..."
# Rotación diaria: eliminar backups de hace más de RETENTION_DAYS días
find "$BACKUP_ROOT/daily" -maxdepth 1 -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \; -print >> "$LOG_FILE"

# Promover backup semanal (cada domingo)
if [ "$(date +%u)" -eq 7 ]; then
    WEEKLY_DIR="$BACKUP_ROOT/weekly/$(date +%Y-%m-%d)"
    cp -al "$DEST_DIR" "$WEEKLY_DIR"  # Enlace duro, no copia datos
    log_message "Backup promovido a semanal: $WEEKLY_DIR"
    # Rotación semanal
    find "$BACKUP_ROOT/weekly" -maxdepth 1 -type d -mtime +$((RETENTION_WEEKS * 7)) -exec rm -rf {} \; -print >> "$LOG_FILE"
fi

# Promover backup mensual (primer día del mes)
if [ "$(date +%d)" -eq 1 ]; then
    MONTHLY_DIR="$BACKUP_ROOT/monthly/$(date +%Y-%m)"
    cp -al "$DEST_DIR" "$MONTHLY_DIR"
    log_message "Backup promovido a mensual: $MONTHLY_DIR"
    # Rotación mensual
    find "$BACKUP_ROOT/monthly" -maxdepth 1 -type d -mtime +$((RETENTION_MONTHS * 30)) -exec rm -rf {} \; -print >> "$LOG_FILE"
fi

log_message "=== FIN BACKUP EXITOSO ==="

Explicación de Opciones Críticas de rsync

OpciónFunciónImpacto en el Backup
-aModo archivo. Preserva todo (permisos, dueños, grupos, timestamps, enlaces simbólicos, dispositivos).Garantiza que la restauración sea idéntica al original.
-vVerboso. Muestra progreso en stdout.Útil para depuración y monitoreo en tiempo real.
-zCompresión durante la transferencia.Reduce el ancho de banda usado, pero consume CPU. Recomendado para WAN.
--deleteElimina archivos en el destino que no existen en el origen.Esencial para mantener el backup como un espejo exacto. Sin esto, los archivos eliminados en origen persistirían en el backup.
--link-destCrea enlaces duros a archivos idénticos en un directorio de referencia.Clave para backups incrementales eficientes en espacio. Si un archivo no ha cambiado, no se copia, solo se crea un enlace duro.

3. Automatización con Cron

Ejecutaremos el script diariamente en un horario de baja actividad.

# Editar crontab del root
sudo crontab -e

Añadir la línea:

# Backup incremental LVM+rsync todos los días a las 2:00 AM
0 2 * * * /usr/local/bin/backup_lvm_rsync.sh

Mejora profesional: Implementa flock para evitar ejecuciones concurrentes si el backup se alarga más de 24 horas. Ejemplo: 0 2 * * * /usr/bin/flock -n /var/lock/backup.lock /usr/local/bin/backup_lvm_rsync.sh

Gestión de Consistencia de Aplicaciones (Pre/Post Snapshot)

Para bases de datos como MySQL/MariaDB, es necesario garantizar que todas las transacciones en memoria se escriban en disco antes del snapshot.

Ejemplo para MySQL

# En el script, justo antes de lvcreate:
log_message "Bloqueando tablas MySQL..."
mysql -u root -p"$MYSQL_PASSWORD" -e "FLUSH TABLES WITH READ LOCK;"
# Inmediatamente después, crear el snapshot
lvcreate --snapshot ...
# Una vez creado, liberar el lock
mysql -u root -p"$MYSQL_PASSWORD" -e "UNLOCK TABLES;"
log_message "Tablas MySQL liberadas."

Alternativa para XFS: Si usas XFS, puedes usar xfs_freeze antes del snapshot para forzar la consistencia del sistema de archivos.

xfs_freeze -f /mount/point
lvcreate --snapshot ...
xfs_freeze -u /mount/point

Verificación y Restauración

Un backup sin verificación no es un backup. Implementa una rutina de prueba de restauración.

Script de Verificación (Check_restore.sh)

#!/bin/bash
# Verificar integridad del backup más reciente

LATEST_BACKUP=$(ls -1dt /backup/daily/*/ | head -n 1)

echo "Verificando backup: $LATEST_BACKUP"

# 1. Listar archivos críticos
echo "Verificando archivos de configuración..."
if [ -f "$LATEST_BACKUP/etc/nginx/nginx.conf" ]; then
    echo "OK: nginx.conf presente"
else
    echo "ERROR: nginx.conf faltante"
fi

# 2. Verificar integridad con tar (si se usó tar) o con rsync --dry-run
echo "Simulando restauración con rsync..."
rsync -avz --dry-run "$LATEST_BACKUP/" /dev/null

# 3. Verificar checksums (si se generaron)
if [ -f "$LATEST_BACKUP/checksums.md5" ]; then
    echo "Verificando checksums..."
    cd "$LATEST_BACKUP"
    md5sum -c checksums.md5 --quiet
fi

Monitoreo y Alertas

Integra el script de backup con un sistema de monitoreo como Nagios, Zabbix o simplemente notificaciones por correo.

Notificación por Email

Añadir al final del script:

# Enviar notificación por correo
if [ -x /usr/bin/mail ]; then
    tail -n 5 "$LOG_FILE" | mail -s "Backup Status - $(hostname) - $(date +%Y-%m-%d)" sysadmin@example.com
fi

Métricas Clave a Monitorear

MétricaHerramientaUmbral de Alerta
Duración del backuptime en script> 4 horas
Tamaño del snapshot LVMlvs -o data_percent> 80% de SNAPSHOT_SIZE
Espacio en disco del destinodf -h< 20% libre
Código de salida del script$?!= 0

Consideraciones de Seguridad

  1. Permisos del script: chmod 700 /usr/local/bin/backup_lvm_rsync.sh y propiedad root:root.
  2. Clave SSH: Si usas backup remoto, genera una clave SSH sin passphrase para root y limita los comandos permitidos en authorized_keys del servidor remoto usando command= y from= para restringir IP.
  3. Cifrado: Para datos sensibles, considera cifrar el backup con gpg antes de la transferencia remota.
  4. Aislamiento del snapshot: El tamaño del snapshot debe ser suficiente para contener

¿Necesitas ayuda?Son dos de nuestros técnicos, Agustín y Mikel, y están disponibles para resolver cualquier problema.

Hablar con ellos ahora
Agustín y Mikel