Backups del web amb bash i dropbox

Un dels problemes amb els que t'has d'enfrontar sí o sí en el món de la informàtica és el de les còpies de seguretat: no et pots permetre viure sense elles. Un altre dels problemes és trobar el millor sistema: efectiu, econòmic i automatitzat. Per un sistema domèstic, amb accés directe a la màquina, hi ha moltes solucions, però com fer-ho en un servidor que gestiones remotament?

Si treballes amb Drupal/Backdrop pots utilitzar el módul Backup and Migrate (per Drupal i per Backdrop) que et permet guardar una còpia del teu lloc web i la base de dades en un directori privat del teu servidor, la qual cosa no et serveix de gaire si el servidor peta, o en un servei extern, amb cost afegit, com NodeSquirrel.

Però si també vols guardar altres arxius del servidor, com un web fet amb Grav (un gestor de continguts d'arxius plans, sense base de dades, que recomano molt), la cosa es complica.

Bash al poder

La solució que he trobat, i que em funciona força bé, és utilitzar un arxiu de bash, que crea els arxius comprimits de les bases de dades i/o els arxius plans dels servidor, i un altre script, també de bash, que gestiona les còpies d'aquests arxius comprimits al meu dropbox. Finalment només em cal fer que el Crontab executi els processos un cop al dia.

L'arxiu de bash per a la creació dels arxius comprimits l'he trobat a https://www.adrikodde.nl/blog/2016/bash-backup-script-to-dropbox i és aquest:

 #!/bin/bash

function backupFiles()
{
  echo "Start backup files"

  if [ -d "/var/www/" ]; then
    tar -zcf "www-$(date '+%F').tar.gz" -C / var/www
  fi
}


function backupDb()
{
  echo "Start backup databases"

  mysqldump -u root -p****** --all-databases | gzip > "db-$(date '+%F').sql.gz"
}

function uploadBackups()
{
  echo "Start uploading"

  ~/bin/Dropbox-Uploader/dropbox_uploader.sh upload *.gz /
}

function deleteBackups()
{
  echo "Delete temporary files"

  rm *.gz
}

echo `date`

backupFiles
backupDb
uploadBackups
deleteBackups

echo "$(tput setaf 2) Backup task has finished"

L'aplicació per gestionar les còpies amb Dropbox, que com heu vist mencionat al codi anterior, és el Dropbox-Uploader. Si teniu el vostre web al directori /var/www del vostre servidor no us caldrà modificar-lo massa, però sinó haureu d'ajustar-lo a les vostres necessitats. De fet, jo el tinc modificat de tal manera que tinc un arxiu bash per a cada lloc web que en faig còpia. També l'he modificat perquè no contingui la contrasenya de la base de dades i hi he afegit una funció per purgar les còpies del Dropbox més antigues de dues setmanes.

Un arxiu per directori/base de dades

La primera modificació que li faig és simple: en la primera funció backupFiles() hi poso el directori d'arxius que vull guardar. Encara que pugui tenir diversos webs al mateix servidor que poden penjar tots d'un mateix directori arrel, faig un arxiu per a cada espai web, per tal de poder tenir diferents arxius de backup per a cada web.

Usuari i contrasenya per cada base de dades

El problema del bash original és que has de tenir totes les bases de dades amb el mateix usuari i contrasenya, a part que has de tenir les dades al mateix arxiu. Per evitar això modifico la funció backupDb() de la següent manera:

function backupDb()
{
  echo "Start backup databases"

  mysqldump --defaults-extra-file=/<directori_privat>/.my.cnf -u <usuari_db> <base_dades> | gzip > "db-<base_dades>-$(date '+%F').sql.gz"
}

Bash crida mysqldump amb el paràmetre --defaults-extra-file per dir-li que faci servir un arxiu de configuració extra en el moment d'executar l'acció. Aquest arxiu conté les següents dades:

[mysqldump]
user=<usuari>
password=<contrasenya>

Així evito posar les dades al mateix arxiu bash i puc tenir dades diferents per a cada bash: només cal tenir un arxiu de configuració extra my.cnf per a cada base de dades amb les dades d'usuari i contrasenya corresponents.

Purgar arxius antics

Per tal d'evitar que el meu dropbox es col·lapsi, purgo els arxius de còpia més antics de dues setmanes. Per això afegeixo una funció nova:

function purgeBackups()
{
  echo "Purging dropbox backups of $(date '+%F' --date='1 fortnight ago')"
 
  ~/Dropbox-Uploader/dropbox_uploader.sh delete backup-webs/<web>/<web>-$(date '+%F' --date='1 fortnight ago').tar.gz
  ~/Dropbox-Uploader/dropbox_uploader.sh delete backup-webs/<web>/db-<base_dades>-$(date '+%F' --date='1 fortnight ago').sql.gz

}

Recordeu a purgar els dos arxius comprimits, el dels arxius i el de la base de dades. Finalment recordeu a afegir la crida a la nova funció de purga al final de l'arxiu bash:

backupFiles
backupDb
uploadBackups
deleteBackups
purgeBackups

echo "$(tput setaf 2) Backup task has finished"

Configuració del crontab

Ara només que el servidor executi els arxius de backup cada dia. Jo els faig executar cada dia a les dues de la matinada:

0 2 * * * /<el_vostre_directori>/<arxiu_bash_backup>.sh

Recordeu també a fer que l'arxiu bash de backup sigui executable.

Pros i contres

Els avantatges d'aquest sistema són la seva senzillesa i economia, tens les còpies al dropbox i, per tant, a qualsevol altre ordinador on tinguis configurat el dropbox, amb còpies redundants. Un desavantatge per Drupal i Backdrop és que hi ha taules a la base de dades que tenen moltes dades que poden ocupar força espai. Són les taules de cache i index de cerca, de les que només cal guardar-ne l'estructura. Fer una còpia selectiva amb mysqldump és força complicat, perquè se li ha d'especificar taula per taula, i, per tant, els arxius de les bases de dades, encara que comprimits, poden ocupar bastant. Una solució passaria per usar el mòdul Backup Migrate, que permet configurar quines taules es guarden amb dades i quines no, per guardar una còpia de la base de dades en local i després fer servir l'arxiu de bash per carregar els arxius comprimits al dropbox.

Espero que pugui ser d'utilitat a algú. Si el feu servir i/o el milloreu no dubteu a compartir-ho als comentaris!