Automatització pràctica de backups diaris de MySQL amb S3
Automatitzar els backups de bases de dades MySQL i pujar-los a S3 sembla una cosa resolta fa temps, però a la pràctica continua sent un punt en que moltes infraestructures mitjanes fallen de forma silenciosa. No per falta de scripts o eines, sinó per com s’ implementa: sense alertes reals, sense verificacions d’ integritat, sense rotació efectiva i amb supòsits implícits que no sempre es compleixen.
Qüestions que cal tancar abans d’automatitzar res
Abans de parlar de scripts, cron o S3, cal tenir clar què s’està copiant i què significa “backup correcte” en el teu context. He vist massa automatitzacions on el dump es genera amb èxit tots els dies… però els restores fallen, o la informació està incompleta.
1. Què fas servir a MySQL?
No és el mateix fer dumps de taules InnoDB que de MyISAM. Amb InnoDB pots fer backups consistents sense aturar la base si fas servir –single-transaction, però això només cobreix el que està en aquest punt del temps. No cobreix arxius fora de la base, ni garanteix sincronització entre bases si tens diverses dependents (per exemple, relacions entre dues bases que s’alteren en la mateixa transacció lògica). MyISAM sí que requereix bloqueig d’escriptura o una parada del servei per a alguna cosa consistent.
2. Estàs fent un dump lògic o físic?
La majoria opta per mysqldump, però depenent de la mida i la criticitat, pot que mysqlhotcopy (per a MyISAM), o xtrabackup (per a InnoDB sense downtime) sigui més adequat. En el meu cas, quan la base supera els 10 GB i hi ha finestra de manteniment limitada, és l’únic que escala bé. Si fas servir Aurora o RDS, un altre món: no hi ha accés al sistema d’arxius, així que cal adaptar-se a snapshots gestionats o fer servir dumps i pujar-los via Lambda o scripts amb IAM.
Consideracions amb S3 que de vegades s’ignoren
1. IAM: menys permisos dels que creus
Donar accés total al bucket és temptador, però no cal. El que faig sempre és generar una política específica per host o per entorn que només permeti PutObject sobre una ruta tipus s3://backups/mysql/prod-host-a/*. I si la política és per restaurar, llavors GetObject, però mai totes dues si no és necessari.
2. S3 no té “overwrites”, són versions noves
Molts scripts pugen amb noms fixos: backup-latest.sql.gz. Si fas això sense versionat activat i sense pensar en condicions de carrera (per exemple, dos cronjobs executant-se per error), pots sobrescriure alguna cosa sense adonar-te’n. Prefereixo sempre pujar amb timestamp en el nom i després gestionar un àlies o symlink si cal apuntar a l’últim.
3. Verifica post-upload
No n’hi ha prou amb que aws s3 cp en retorni 0. Pot tornar èxit i l’arxiu estar truncat (rar, però he vist a S3 inconsistències temporals). La solució: pujar i després fer un aws s3 ls amb el nom de l’arxiu per verificar que la mida és coherent. O directament generar i pujar un hash .md5 al costat de l’arxiu. Si automatitzes això en el procés de backup, t’estalvia disgustos després.
Retenció i rotació
Aquest és el punt on més scripts envelleixen malament. Hi ha qui esborra backups antics amb find en local, però no fa el mateix a S3. Tres anys després, tens 900 GB de dúmps vells que ningú s’atreveix a esborrar perquè no hi ha un criteri clar.
El que es fa ara són dues coses:
- Lifecycle policies de S3, si no necessites retenció per taula o per host. Configura des d’AWS: tot el que estigui a /mysql/ més de 60 dies, a Glacier. Més de 180, eliminació.
- Scripts de rotació propis, si necessites lògica més fina: per exemple, guardar un backup diari per 30 dies, però un setmanal durant 6 mesos, i un mensual per 1 any. Ho implemento llegint els noms en S3, parsejant dates i aplicant regles. És més feina, però dona control.
Alertes i monitoratge
Qualsevol sistema de backup que no t’avisi quan alguna cosa surt malament és només un element decoratiu. No n’hi ha prou amb escriure un arxiu a /var/log. Necessites alertes actives. Hi ha diverses formes:
- Si fas servir cron, redirigeix la sortida i els errors a un sistema que pugui enviar notificacions. Per exemple, un script wrapper que escrigui a stdout i stderr, i cron l’enviï a un correu monitoritzat (no al root del sistema, sinó a un grup real).
- Millor encara, integra amb alguna cosa com Prometheus + node_exporter per llançar alertes si no apareix un backup nou a X hores. També pots fer servir S3 Inventory o CloudWatch per detectar absència de nous arxius.
Una tècnica que aplico molt: al final de l’script de backup, escric una marca (arxiu buit) en un bucket tipus backups-monitoring, amb un nom tipus mysql/host-a/last-backup-2025-05-31. Després una Lambda escaneja aquest bucket i verifica que tots els hosts hagin generat backup el dia anterior. Si no ho van fer, alerta.
Restauració: el backup que no es prova, no serveix
Pots tenir la millor automatització del món, però si no fas restauracions regulars, és loteria. Cada trimestre fes restauracions completes a un entorn temporal. No cal que estiguin 100% automatitzats (tot i que ajuda), però han d’existir. I no n’hi ha prou amb que l’arxiu es descomprimeixi: cal aixecar la base i provar de verificar la integritat.
Exemple de script de backup diari de MySQL a S3
Aquest script està pensat per executar-se des de cron en un host Linux amb accés al servidor MySQL i a AWS S3 (via awscli amb un perfil d’IAM configurat). Inclou compressió en stream, verificació, metadades, i logs útils. Està basat en casos reals i el pots fer servir amb petites variacions segons l’ entorn.
#!/bin/bash
# backup_mysql_to_s3.sh
set -euo pipefail
# Variables de configuració
DATE=$(date +%F)
HOSTNAME=$(hostname -s)
DB_NAME="nombre_de_tu_base"
BACKUP_NAME="${DB_NAME}-${DATE}.sql.gz"
TMP_DIR="/tmp/mysql_backups"
TMP_FILE="${TMP_DIR}/${BACKUP_NAME}"
S3_BUCKET="s3://tu-bucket-backups/mysql"
S3_PATH="${S3_BUCKET}/${HOSTNAME}/${BACKUP_NAME}"
AWS_CLI_PROFILE="backups-profile"
# Per monitoritzar (marca de darrera execució)
MONITORING_TAG="${HOSTNAME}/last-backup-${DATE}"
# Crear directori temporal
mkdir -p "$TMP_DIR"
# Dump + compressió
echo "[$(date)] Iniciant dump i compressió per a ${DB_NAME}..."
mysqldump \
--defaults-file=/home/backupuser/.my.cnf \
--single-transaction \
--quick \
--lock-tables=false \
"${DB_NAME}" \
| gzip > "${TMP_FILE}"
# Validació local bàsica (sense corrupció aparent)
if ! gunzip -t "${TMP_FILE}"; then
echo "[$(date)] Error: l’ arxiu gzip generat està corrupte."
exit 1
fi
# Pujar a S3
echo "[$(date)] Pujant backup a S3: ${S3_PATH}"
aws s3 cp "${TMP_FILE}" "${S3_PATH}" --profile "${AWS_CLI_PROFILE}"
# Verificació de mida a S3 (bàsica)
REMOTE_SIZE=$(aws s3 ls "${S3_PATH}" --profile "${AWS_CLI_PROFILE}" | awk '{print $3}')
LOCAL_SIZE=$(stat -c%s "${TMP_FILE}")
if [[ "${REMOTE_SIZE}" -ne "${LOCAL_SIZE}" ]]; then
echo "[$(date)] Error: la mida remota (${REMOTE_SIZE}) no coincideix amb la local (${LOCAL_SIZE})."
exit 2
fi
# Marca per a monitorització (pot ser un objecte buit)
aws s3 cp /dev/null "s3://tu-bucket-backups-monitoring/${MONITORING_TAG}" --profile "${AWS_CLI_PROFILE}"
# Neteja local
rm -f "${TMP_FILE}"
echo "[$(date)] Backup finalitzat correctament."
Arxiu ~/.my.cnf segur
Per evitar contrasenyes exposades en processos o scripts, configurem les credencials d’accés a MySQL en un arxiu .my.cnf amb permisos restrictius:
[client]
usuari=backup_user
contrasenya = clave_supersecreta
Permisos:
chmod 600 /home/backupuser/.my.cnf
L’usuari que executa l’script (backupuser, per exemple) ha de ser l’únic propietari de l’arxiu i tenir els permisos adequats a MySQL per executar dumps (habitualment SELECT, LOCK TABLES, SHOW VIEW, i accés a mysql.* si necessites usuaris i permisos).
Comentaris sobre decisions de l’ script
1. set -euo pipefail: Aquest encapçalat assegura que qualsevol fallada interromp l’script immediatament. Molt útil per evitar que es pugi un arxiu buit perquè un pas anterior va fallar sense aturar l’execució.
2. Sincronització amb –single-transaction i –quickClave per a InnoDB: evita bloquejar taules durant el dump i permet que sigui consistent mentre altres operacions continuen corrent.
3. gunzip -t com a prova mínima: No és una verificació completa, però garanteix que l’arxiu generat no està trencat. Si falla aquí, no es puja res.
4. Comparació de mida després de l’aws s3 cp: He vist uploads truncats que el CLI donava per vàlids. Això almenys alerta si hi ha una discrepància òbvia. Per a entorns més crítics, es pot afegir generació i comparació de hash.
5. Marca per a monitorització: Aquest pas genera un arxiu buit en un altre bucket o ruta, pensat perquè una Lambda o procés de revisió diària verifiqui la seva existència. No exposa dades sensibles i és molt útil per a dashboards o alertes.
6. Sense logs en disc, tot va a stdout/stderr: Això permet que si l’integres amb cron, els errors es notifiquin per correu (si el sistema està ben configurat), o pots redirigir la sortida a sistemes com syslog, Loki o el que facis servir per a l’ observabilitat.
7. Evita ús d’arxius temporals intermedis quan no és necessari: En aquest cas, s’escriu a disc només perquè volem validar i comparar abans de pujar. Si la prioritat és velocitat o hi ha poc espai, es pot fer el pipeline en streaming directament.
Variants útils segons context
- Multiples bases: si tens moltes bases i en vols una per arxiu, n’hi ha prou amb fer servir un bucle: mysql -i ‘SHOW DATABASES’ i excloure information_schema, performance_schema, etc.
- Retenció en S3: pots fer servir tags en pujar (–metadata en aws s3 cp) i aplicar lifecycle policies basades en ells.
Aquest script i el seu enfocament et dona un punt de partida que pots estendre sense reinventar la roda. És compacte, comprensible i prou explícit per saber què està passant a cada pas. La clau no és fer-ho perfecte a la primera, sinó que cada part es pugui trencar de forma visible i detectable. Qualsevol automatització que pugui fallar sense avisar, no serveix.
Si treballes en una infraestructura amb diverses bases crítiques i un equip que rota, aquest tipus de script autoexplicatiu i sense dependències ocultes es torna fàcil de mantenir i d’auditar. I sobretot, et dona confiança quan arriba el dia de restaurar. Perquè aquell dia sempre arriba.
Conclusió pràctica
El backup diari de MySQL a S3 no és en sí un problema tècnic complicat. El problema real és que sovint es resol amb solucions superficials i després s’assumeix que estan bé. En molts casos, ni tan sols es comprova si el que es puja és el que realment es necessita.
Automatitzar això amb solidesa requereix acceptar que no n’hi ha prou amb tenir un cronjob i un mysqldump. Cal pensar en la integritat, en la restauració, en les credencials rotant, en les alertes, en la rotació, en l’ ús real de S3 i en les limitacions del propi entorn.
Cada sistema tindrà particularitats (multi-tenant, multibase, rèpliques, RDS, etc.), però les pràctiques que més m’han salvat el coll són sempre les mateixes: backups consistents, verificables, monitoritzatss, amb restauracions reals i regles clares de retenció.
Si alguna cosa et falla, que no sigui el backup.