Automatización de Respaldos de Bases de Datos PostgreSQL con Bash y Notificaciones por Correo

Backups Bash Linux

La administración de bases de datos PostgreSQL requiere soluciones confiables para garantizar la seguridad y disponibilidad de la información. En este artículo, exploraremos cómo implementar un sistema automatizado para respaldar bases de datos PostgreSQL utilizando scripts en Bash y Python, incluyendo notificaciones por correo electrónico en caso de errores.


El Script Principal: Automatización en Bash

El siguiente script en Bash se encarga de automatizar el proceso de respaldo para bases de datos PostgreSQL y realizar tareas de mantenimiento. Aquí te explicamos las principales funcionalidades:

1. Configuración inicial y estructura de directorios

  • Define variables importantes como las rutas locales y remotas para almacenar los respaldos.
  • Organiza los respaldos por fecha en directorios específicos, facilitando la gestión y recuperación.
DIR_BASE_RESPALDO="/Respaldos/respaldo-local"  # Ruta base para los respaldos
FECHA=$(date +"%d-%m-%Y")
DIR_RESPALDO="$DIR_BASE_RESPALDO/$FECHA"

2. Obtención de bases de datos PostgreSQL

Se ejecuta una consulta en el sistema PostgreSQL para listar todas las bases de datos disponibles que no sean plantillas, utilizando el comando psql como usuario postgres:

DATABASES=$(su - postgres -c "$PSQL -d postgres -t -c \"SELECT datname FROM pg_database WHERE datistemplate = false;\"")

3. Generación de respaldos comprimidos

Por cada base de datos obtenida, se utiliza el comando pg_dump para generar un respaldo comprimido en formato gzip:

ARCHIVO_RESPALDO="$DIR_RESPALDO/${DB_NAME}_$FECHA.sql.gz"
su - postgres -c "$PG_DUMP --format=c --no-owner --no-acl $DB_NAME" | gzip > "$ARCHIVO_RESPALDO"

4. Copia remota de los respaldos

Una vez generados los respaldos, el script copia la carpeta de respaldo a un directorio remoto, asegurando una segunda copia en caso de fallos en el servidor local:

cp -r "$DIR_RESPALDO" "$DIR_BACKUP_REMOTO"

5. Limpieza de respaldos antiguos

El script elimina automáticamente respaldos locales y remotos con más de 7 días de antigüedad:

find "$DIR_BASE_RESPALDO" -type d -mtime +7 -exec rm -rf {} +
find "$DIR_BACKUP_REMOTO" -type d -mtime +7 -exec rm -rf {} +

6. Registro de logs

Todos los eventos y errores se registran en un archivo de log para facilitar el monitoreo del sistema:

LOG_FILE="$DIR_RESPALDO/respaldo_$FECHA.log"
echo "[$FECHA $HORA] Inicio del respaldo de las bases de datos." > "$LOG_FILE"

Gestión de Errores y Notificaciones: Script en Python

Para informar al administrador sobre posibles errores durante el proceso, se utiliza un script en Python que envía notificaciones por correo electrónico. A continuación, describimos sus características principales.

1. Envío de notificaciones por correo

El script utiliza la biblioteca smtplib para conectarse a un servidor SMTP y enviar correos electrónicos:

servidor_smtp = 'xxx.xxx.xxx.xx'
puerto_smtp = 587
usuario = 'usuario'
contrasena = 'contraseña'

2. Notificación de estado

El cuerpo del mensaje detalla si el proceso de respaldo fue exitoso o si se encontraron errores, incluyendo un resumen del log generado:

if estado_salida == 0:
    cuerpo_mensaje = "El proceso se completó sin errores."
else:
    cuerpo_mensaje = "Se encontraron errores en el proceso:\n\n" + mensaje_error

3. Enlace con el script Bash

El script Bash llama al script Python en caso de detectar errores, pasando como argumentos el correo del destinatario, el estado del proceso y el mensaje de error:

python enviar_correo.py "$EMAIL" "$ESTADO" "$ERROR_MESSAGE"

 

Scripts completos

Script que realiza el backup:

respaldar.sh

#!/bin/bash

# Variables
DIR_BASE_RESPALDO="/Respaldos/respaldo-local"  # Ruta base para los respaldos
FECHA=$(date +"%d-%m-%Y")
HORA=$(date +"%H:%M:%S")
DIR_RESPALDO="$DIR_BASE_RESPALDO/$FECHA"
DIR_BACKUP_REMOTO="/BKP_REMOTO/"
LOG_FILE="$DIR_RESPALDO/respaldo_$FECHA.log"
PG_DUMP="/usr/bin/pg_dump"
PSQL="/usr/bin/psql"
EMAIL="[email protected]"  # Correo destinatario

# Crear directorios de respaldo y log si no existen
mkdir -p "$DIR_RESPALDO" || { echo "[$FECHA $HORA] Error al crear el directorio de respaldo." >> "$LOG_FILE"; exit 1; }

# Inicializar el archivo de log
echo "[$FECHA $HORA] Inicio del respaldo de las bases de datos." > "$LOG_FILE"

# Variable de estado
ESTADO=0

# Obtener todas las bases de datos como usuario postgres
DATABASES=$(su - postgres -c "$PSQL -d postgres -t -c \"SELECT datname FROM pg_database WHERE datistemplate = false;\"")
if [ $? -ne 0 ]; then
    echo "[$FECHA $HORA] Error al obtener la lista de bases de datos." >> "$LOG_FILE"
    ESTADO=1
else
    echo "[$FECHA $HORA] Lista de bases de datos obtenida exitosamente." >> "$LOG_FILE"
fi

# Respaldar cada base de datos
for DB_NAME in $DATABASES; do
    ARCHIVO_RESPALDO="$DIR_RESPALDO/${DB_NAME}_$FECHA.sql.gz"

    if su - postgres -c "$PG_DUMP --format=c --no-owner --no-acl $DB_NAME" | gzip > "$ARCHIVO_RESPALDO"; then
        echo "[$FECHA $HORA] Respaldo completado exitosamente: $ARCHIVO_RESPALDO" >> "$LOG_FILE"
    else
        echo "[$FECHA $HORA] Error en el respaldo de la base de datos: $DB_NAME" >> "$LOG_FILE"
        ESTADO=1
    fi
done

# Copiar la carpeta de respaldo a la ubicación remota
if cp -r "$DIR_RESPALDO" "$DIR_BACKUP_REMOTO"; then
    echo "[$FECHA $HORA] Copia de respaldos a $DIR_BACKUP_REMOTO completada." >> "$LOG_FILE"
else
    echo "[$FECHA $HORA] Error al copiar respaldos a $DIR_BACKUP_REMOTO." >> "$LOG_FILE"
    ESTADO=1
fi

# Eliminar respaldos de más de 7 días en el directorio local
if find "$DIR_BASE_RESPALDO" -type d -mtime +7 -exec rm -rf {} +; then
    echo "[$FECHA $HORA] Eliminación de respaldos antiguos en local completada." >> "$LOG_FILE"
else
    echo "[$FECHA $HORA] Error al eliminar respaldos antiguos en local." >> "$LOG_FILE"
    ESTADO=1
fi

# Eliminar respaldos de más de 7 días en el directorio remoto
if find "$DIR_BACKUP_REMOTO" -type d -mtime +7 -exec rm -rf {} +; then
    echo "[$FECHA $HORA] Eliminación de respaldos antiguos en remoto completada." >> "$LOG_FILE"
else
    echo "[$FECHA $HORA] Error al eliminar respaldos antiguos en remoto." >> "$LOG_FILE"
    ESTADO=1
fi

# Registrar el estado de salida y enviar correo si hay errores
if [ $ESTADO -eq 0 ]; then
    echo "[$FECHA $HORA] Proceso completado sin errores." >> "$LOG_FILE"
else
    echo "[$FECHA $HORA] Proceso finalizado con errores." >> "$LOG_FILE"
    # Enviar correo en caso de error usando Python
    ERROR_MESSAGE=$(cat "$LOG_FILE")  # Captura el contenido del log para enviar
    python enviar_correo.py "$EMAIL" "$ESTADO" "$ERROR_MESSAGE"  # Llama al script de envío de correo
fi

echo "[$FECHA $HORA] Fin del respaldo" >> "$LOG_FILE"

# Copiar el log al directorio remoto al finalizar
LOG_COPY_PATH="$DIR_BACKUP_REMOTO/$FECHA/respaldo_$FECHA.log"

if cp "$LOG_FILE" "$LOG_COPY_PATH"; then
    echo "[$FECHA $HORA] Log copiado a $LOG_COPY_PATH." >> "$LOG_FILE"
else
    echo "[$FECHA $HORA] Error al copiar el log a $LOG_COPY_PATH." >> "$LOG_FILE"
    ESTADO=1
fi

exit $ESTADO

 

Script para enviar correos:

enviar_correo.py

#!/usr/bin/env python2.6
# -*- coding: utf-8 -*-
import smtplib
import sys
from email.mime.text import MIMEText

def enviar_correo(correo_destinatario, estado_salida, mensaje_error):
    # Cuerpo del mensaje
    if estado_salida == 0:
        cuerpo_mensaje = "El proceso se completó sin errores."
    else:
        cuerpo_mensaje = "Se encontraron errores en el proceso:\n\n" + mensaje_error

    msg = MIMEText(cuerpo_mensaje)
    msg['Subject'] = 'Informe de Respaldo'
    msg['From'] = '[email protected]'
    msg['To'] = correo_destinatario

    # Configuración de autenticación
    servidor_smtp = 'xxx.xxx.xxx.xxx'  # Servidor SMTP
    puerto_smtp = 587  # Usa 587 para TLS o 465 para SSL
    usuario = 'usuario'  # Usuario SMTP
    contrasena = 'contraseña'  # Contraseña SMTP

    servidor = None
    try:
        servidor = smtplib.SMTP(servidor_smtp, puerto_smtp)
        servidor.starttls()  # Inicia la conexión TLS
        servidor.login(usuario, contrasena)  # Autenticación
        servidor.sendmail(msg['From'], [correo_destinatario], msg.as_string())  # Enviar el correo
        print "Correo enviado a %s" % correo_destinatario  # Uso de % para formatear
    except smtplib.SMTPException as e:
        print "Error al enviar correo: %s" % e  # Uso de % para formatear
    finally:
        if servidor is not None:
            try:
                servidor.quit()  # Asegúrate de cerrar la conexión
            except Exception as e:
                print "Error al cerrar la conexión SMTP: %s" % e  # Uso de % para formatear

if __name__ == '__main__':
    if len(sys.argv) != 4:
        print "Uso: python send_email.py <correo_destinatario> <estado_salida> <mensaje_error>"
        sys.exit(1)

    correo_destinatario = sys.argv[1]
    estado_salida = int(sys.argv[2])
    mensaje_error = sys.argv[3]

    enviar_correo(correo_destinatario, estado_salida, mensaje_error)

 


Ventajas de Esta Solución

  1. Automatización completa: Desde la creación de respaldos hasta la limpieza de archivos antiguos.
  2. Notificaciones oportunas: Los administradores reciben información inmediata sobre el estado del proceso.
  3. Copia remota: Garantiza la disponibilidad de los respaldos en caso de fallos locales.
  4. Simplicidad y escalabilidad: Los scripts son personalizables y fáciles de integrar en infraestructuras existentes.

Conclusión

Este sistema automatizado para respaldar bases de datos PostgreSQL es una herramienta poderosa que combina Bash para la gestión del respaldo y Python para las notificaciones. Asegúrate de personalizar las rutas, credenciales y configuraciones según tu entorno. Con este enfoque, puedes optimizar la seguridad y disponibilidad de tus datos críticos de manera eficiente.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *