Aplicación web Flask para gestionar backups y restauraciones MySQL, incluyendo restauración histórica y PITR (Point-In-Time Recovery) por base de datos.
Este documento unifica la operación diaria y las decisiones técnicas principales del proyecto.
Resolver desde web lo mismo que hacen los scripts operativos:
restaurar_historico.sh: restauración desde backup histórico (.sql.gz)restaurar-single-granular.sh: restauración PITR por base (backup base + binlogs)
Los scripts back-*.sh y rotate_binlogs.sh se usan para generación/rotación de backups (normalmente por cron).
- Backend: Flask monolítico en
app.py - Frontend: templates Jinja + Bootstrap
- Ejecución: desarrollo (
python app.py) o Gunicorn/systemd - Datos fuente:
- Backups históricos en
DIR_DESTINO - Backups base PITR en
DIR_DESTINO_INC - Binlogs desde
BINLOG_BACKUP_DIR(si existe y contiene binlogs) o fallback automático adatadirMySQL
- Backups históricos en
- Python 3.8+
- MySQL/MariaDB con binlogs habilitados
- Usuario MySQL con permisos para:
- listar bases
- consultar variables (
datadir,binlog_format) - restaurar por
mysql
- Acceso de lectura del usuario de la app a los binlogs (ej.
/var/lib/mysql)
Variables mínimas:
MYSQL_USER="usuario"
MYSQL_PASS="password"
MYSQL_HOST="127.0.0.1"
DIR_DESTINO="/mnt/backup/mysql"
DIR_DESTINO_INC="/mnt/backup/mysql/incremental"
# Opcional: si no se define, la app usa datadir de MySQL
BINLOG_BACKUP_DIR="/mnt/backup/mysql/binlogs"
# Hora de inicio para listar binlogs PITR del día actual
HORA_INICIO="07:00:00"
# Bases a excluir globalmente (separadas por '|')
EXCLUDE_DB="information_schema|performance_schema|mysql|sys"
# Sincronización de backups (origen -> destino remoto/local)
DIR_DESTINO_ORIGEN="/mnt/backup/"
DIR_DESTINO_REMOTO="/mnt/backup/"Notas:
- Si
BINLOG_BACKUP_DIRno está definido o está vacío/sin binlogs, la app consultaSHOW VARIABLES LIKE 'datadir'y usa esa ruta. - En
/pitr, el Paso 2 muestra solo binlogs del día actual desdeHORA_INICIO. EXCLUDE_DBaplica globalmente en la GUI para MySQL: evita mostrar/listar/seleccionar esas bases en listados y restauraciones.sync_backup.shusaDIR_DESTINO_ORIGENyDIR_DESTINO_REMOTOpara mantener una copia espejo conrsync --delete.- Proteger
.env(chmod 600 .env).
source venv/bin/activate
python3 app.pygunicorn -w 4 -b 0.0.0.0:8200 app:appSe incluye el script install_systemd_service.sh para crear/instalar el servicio
mysql-backup-web.service y ejecutarlo con Gunicorn.
chmod +x install_systemd_service.sh
./install_systemd_service.shComandos útiles:
sudo systemctl status mysql-backup-web
sudo journalctl -u mysql-backup-web -f
sudo systemctl restart mysql-backup-webEjemplo de crontab sin rutas reales:
# Backup MySQL
0 23 * * * /usr/bin/bash /ruta/proyecto/back-sql-no-data.sh
10 23 * * * /usr/bin/bash /ruta/proyecto/back-sql-single.sh
0 5 * * * /usr/bin/bash /ruta/proyecto/back-sql-single-inc.sh
*/15 7-18 * * * /usr/bin/bash /ruta/proyecto/rotate_binlogs.sh
# Backup MongoDB
20 23 * * * /usr/bin/bash /ruta/proyecto/back-mongo.sh
# Sincronización continua de backups
* * * * * /usr/bin/bash /ruta/proyecto/sync_backup.sh-
MySQL histórico diario (noche)
back-sql-no-data.shyback-sql-single.shgeneran el respaldo completo histórico del día.
-
MySQL base para PITR (madrugada)
back-sql-single-inc.shgenera la base sobre la que luego se aplican binlogs.
-
Rotación de binlogs en horario operativo
rotate_binlogs.shcada 15 minutos entre 07:00 y 18:59 para granularidad de recuperación durante el día.
-
MongoDB histórico diario
back-mongo.shgenera unmongodumphistórico al cierre del día.
-
Sincronización continua de repositorio de backups
sync_backup.shcorre cada minuto y replicaDIR_DESTINO_ORIGENhaciaDIR_DESTINO_REMOTO.- Usa
rsync --delete, por lo que el destino queda en espejo del origen.
Con esta combinación se cubren dos necesidades:
- Recuperación histórica (snapshots diarios)
- Recuperación fina MySQL tipo PITR (base + binlogs)
- Replicación operativa del repositorio de backups (sincronización continua)
- Seleccionar backup histórico en
/historical - Confirmar escribiendo
SI - Ejecuta restauración completa del archivo seleccionado
- Seleccionar base
- Seleccionar binlogs (solo del día actual desde
HORA_INICIO) - Definir hora de corte (opcional)
- Confirmar escribiendo
SI
Comodidad de selección en UI:
- Al seleccionar un binlog, se marcan automáticamente todos los binlogs anteriores de la lista (secuencia acumulada).
Implementación:
- Restaura primero
${DB}-back.sqldeDIR_DESTINO_INC - Aplica binlogs con:
mysqlbinlog --database="<db_seleccionada>" --start-datetime ... [--stop-datetime ...]
Esto está diseñado para aplicar cambios de la base seleccionada, no de forma global.
Nueva sección en menú: MongoDB (/mongodb)
Flujo disponible:
- Seleccionar backup histórico de
mongodump(carpetabackup_*) - Elegir modo:
- Restauración Total: aplica todo el backup Mongo
- Restauración Parcial: permite seleccionar una o más bases dentro del backup
- Confirmar escribiendo
SI
Detalles técnicos:
- Usa
mongorestorecon credenciales de.env(HOST,PUERTO,USUARIO,CONTRASENA,AUTH_DB). - Modo total:
mongorestore --drop <backup_path> - Modo parcial: por cada base seleccionada usa
mongorestore --drop --db <db> <backup_path>/<db>
Disponible en UI en:
/historical(MySQL)/mongodb(MongoDB)
Acciones rápidas:
- Borrar backups con más de 7 días
- Borrar backups con más de 15 días
- Borrar backups con más de 30 días
Notas:
- Siempre requiere confirmación escribiendo
SI. - MySQL: elimina archivos
*-back_*.sql.gzantiguos deDIR_DESTINO. - MongoDB: elimina carpetas
backup_*antiguas deDESTINO.
En my.cnf:
[mysqld]
log-bin = mysql-bin
binlog_format = STATEMENT
expire_logs_days = 7Reiniciar MySQL:
sudo systemctl restart mysqlSTATEMENT habilita el filtrado por base (--database) necesario para PITR granular.
Pero hay un trade-off:
- Si tus sentencias usan funciones no deterministas (
NOW(),RAND(),UUID(), etc.), al reproducir binlogs esos valores pueden no coincidir exactamente con los originales.
Recomendación para datos críticos:
- Evitar funciones no deterministas en operaciones sensibles, o
- Calcular/persistir valores explícitos antes de guardar.
No usar sudo dentro de la app web.
Preferir permisos de mínimo privilegio para el usuario que corre la app (ej. jose):
- agregar al grupo
mysql, y/o - aplicar ACL puntual de lectura en datadir/binlogs.
Comando recomendado (usuario actual de entorno):
sudo usermod -aG mysql "$(id -un)"El instalador
install_systemd_service.shya intenta ejecutar este paso automáticamente para el usuario que instala el servicio.
Ejemplo ACL:
sudo setfacl -m u:jose:rx /var/lib/mysql
sudo setfacl -m u:jose:r /var/lib/mysql/mysql-bin.*mysql_backup/
├── app.py
├── back-sql-single.sh
├── back-sql-single-inc.sh
├── rotate_binlogs.sh
├── restaurar_historico.sh
├── restaurar-single-granular.sh
├── templates/
├── .env
├── .env.example
├── README.md <- documento unificador
├── README_WEB_INTERFACE.md <- detalle operativo extendido
└── documento.md <- detalle técnico extendido
README_WEB_INTERFACE.md: instalación operativa extensa, servicio, troubleshooting.documento.md: estrategia PITR detallada, cron y fundamentos técnicos.
Si hay contradicción, usar este orden de prioridad:
- Código (
app.pyy scripts) README.md(este documento)README_WEB_INTERFACE.md/documento.md






