[MÉMO] Backup WordPress + RSYNC

Afin de prévenir une panne, un crash de son serveur ou quoi que ce soit d’autre, il est primordial de penser à faire des sauvegardes. Voici comment je procède pour sauvegarder mes sites (WordPress ou autres) et leur(s) bases de données.

Aujourd’hui j’ai définitivement, temporairement, décidé d’arrêter de me prendre la tête avec Docker. J’ai donc migré ce WordPress vers une VM Debian « standard » avec Nginx, PHP7.1-FPM et MariaDB. L’occasion de revoir mon système de sauvegarde, et d’écrire ce mémo. A chaque fois que je dois toucher RSYNC (~ 1 fois par an) je me prends la tête pour le remettre en place… Alors je vais essayer d’être le plus clair possible.

Au fait pourquoi je parle de RSYNC ? Car faire des sauvegardes de son WordPress (fichiers + BDD) c’est bien, les stocker ailleurs que sur le même serveur c’est mieux c’est INDISPENSABLE !

<parano>Les envoyer sur deux autres emplacements différents et distincts géographiquement parlant c’est encore +++ mieux</parano>

 

Sauvegarde de la base de données WordPress

Deux choses à distinguer pour sauvegarder un WordPress : la base de données et les fichiers du dossier wp-content.

On commence donc par la base de données. Comme on veut éventuellement récupérer une info sur l’état de la sauvegarde on va scripter ça en bash et s’envoyer un mail pour nous avertir en cas de pépins. 😉 On va également mettre en place une rétention/rotation des sauvegardes sur quelques jours au cas où. Cela fait maintenant un an que je procède de la sorte (à peu près) pour les sauvegardes de Météo06, et ça marche bien.

La première chose est de mettre en place les répertoires de travail :

Emplacement des répertoires
cd /home/
mkdir -p /home/{backup/{bdd/month,www},backup_temp,scripts/backup}

Le /home/backup/* sera l’emplacement des sauvegardes, le /home/backup_temp/ sera l’emplacement temporaire des sauvegardes et le /home/scripts/backup/ sera l’emplacement des scripts de sauvegardes.

 

Création d’un utilisateur pour le dump

Afin de sécuriser un peu le process de sauvegarde, il est préférable de créer un utilisateur MySQL spécifique pour le dump. Cela peut se faire de manière graphique avec PHPMyAdmin ou Adminer. Sinon tapez « mysql » pour se connecter en root, puis :

Création d'un user MySQL nommé 'dump'
CREATE USER 'dump'@'localhost' IDENTIFIED BY 'mon_mot_de_passe';
GRANT SELECT, LOCK TABLES, SHOW VIEW ON nom_bdd_wordpress.* TO 'dump'@'localhost';
\q

 

Création du script

On va créer le premier script pour la/les BDD :

cd /home/scripts/backup/
nano backup_bdd.sh

Et on y insère :

backup_bdd.sh
#!/bin/bash

# Inspiré d'un script trouvé sur phpnews.fr (plus en ligne)
# http://david.mercereau.info/script-de-sauvegarde-mysql-par-base-mysql_dump-sh/
# Et aussi la : http://www.planet-libre.org/index.php?post_id=13027
# Mise en place le 26/12/2017 pour blog perso

set -eu

## Paramètres
## Créer un user avec les droits SELECT, LOCK TABLES, SHOW VIEW sur la BDD correspondantes
USER='dump'
PASS='mon_mot_de_passe'
DB_NAME='nom_bdd_wordpress'
# Répertoire de stockage des sauvegardes
DATADIR="/home/backup/bdd"
# Répertoire de travail (création/compression)
DATATMP="/home/backup_temp"
# Nom du dump
DATANAME="dump_wp_$(date +%d.%m.%y@%Hh%M)"
# Compression
COMPRESSIONCMD="tar -jcf"
COMPRESSIONEXT=".tar.bz2"
# Rétention / rotation des sauvegardes en jours
RETENTION=7
# Email pour les erreurs
EMAIL=votre_email
# Expediteur de l'email
EMAIL_EXP=backup@exemple.org
EMAIL_EXP_NAME=Backup
# Log de sortie
exec 1> ${DATADIR}/out.log
# Log d'erreur
exec 2> ${DATADIR}/error.log


## Début du script

ionice -c3 -p$$ &>/dev/null
renice -n 19 -p $$ &>/dev/null

function cleanup {
if [ "`stat --format %s ${DATADIR}/error.log`" != "0" ] && [ "$EMAIL" != "0" ] ; then
cat ${DATADIR}/error.log | mail -s "Backup BDD - ERROR" -r "${EMAIL_EXP_NAME}<${EMAIL_EXP}>" ${EMAIL}
else echo "Aucune erreur survenue concernant la backup de la BDD du WordPress $DATANAME" | mail -s "Backup BDD OK" -r "${EMAIL_EXP_NAME}<${EMAIL_EXP}>" ${EMAIL}
fi
}
trap cleanup EXIT

# On crée sur le disque un répertoire temporaire
mkdir -p ${DATATMP}

mysqldump -u $USER -p$PASS --quick --add-locks --lock-tables --extended-insert $DB_NAME > ${DATATMP}/${DATANAME}.sql


# On tar le dump
cd ${DATATMP}
${COMPRESSIONCMD} ${DATANAME}${COMPRESSIONEXT} ${DATANAME}.sql
chmod 600 ${DATANAME}${COMPRESSIONEXT}

# On le déplace dans le répertoire month si on est le premier du mois
# afin de conserver une copie mensuelle (en plus de la rétention)
if [ $(date +%d) = "01" ] && [ "$DATATMP" != "$DATADIR" ] ; then
mv ${DATANAME}${COMPRESSIONEXT} ${DATADIR}/month
elif [ "$DATATMP" != "$DATADIR" ] ; then
mv ${DATANAME}${COMPRESSIONEXT} ${DATADIR}
fi


# On supprime le dump temporaire
rm -rf ${DATATMP}/${DATANAME}.sql

# On supprime les backup plus vieilles que la rétention
echo "Suppression des vieux backup : "
find ${DATADIR} -not -path "*/month/*" -name "*${COMPRESSIONEXT}" -mtime +${RETENTION} -print -exec rm {} \;

 

Les mails

Maintenant pour que le serveur puisse envoyer des mails, vérifier le hostname de votre serveur (le mieux est qu’il corresponde à votre domaine), puis :

apt update && apt install mailutils

Ensuite il faut dire à Exim qu’il a le droit d’envoyer des mails en dehors du localhost, pour cela suivre ce tuto et :

dpkg-reconfigure exim4-config

 

Test du script

Enfin pour lancer le script afin de tester son bon fonctionnement :

chmod +x backup_bdd.sh
/bin/bash backup_bdd.sh

 

Sauvegarde du répertoire WordPress

On va maintenant pouvoir refaire à peu près la même chose avec le répertoire web de WordPress. Pour ma part je ne sauvegarderai que le répertoire wp-content afin d’économiser de la place et du temps, étant donné que tout ce qui est en dehors de ce répertoire est censé rester générique et propre à l’installation même de WordPress (mis à part le fichier wp-config.php, mais on va l’intégrer à notre sauvegarde). On en profitera aussi pour ajouter le répertoire de scripts à notre sauvegarde.

Création du fichier backup_www.sh, et on y insère :

backup_www.sh
#!/bin/bash
## Mise en place le 26/12/2017 pour blog perso
# On determine les repertoires sources et cibles
BKPDIR="/home/backup/www/"
# La on va pouvoir integrer tous ce que l'on veut sauvegarder
SOURCEDIR="/var/www/html/wordpress/wp-config.php /var/www/html/wordpress/wp-content/ /home/scripts/"
# Nom du site pour le nom final du fichier
NAME_SITE=WordPress
TODAY=`date +%d`
DAY=`date +%A |tr 'A-Z' 'a-z'`
MONTH=`date +%B |tr 'A-Z' 'a-z'`
# Email pour les erreurs
EMAIL=votre_email
# Expediteur de l'email
EMAIL_EXP=backup@exemple.org
EMAIL_EXP_NAME=Backup
# Log de sortie
exec 1> ${BKPDIR}out.log
# Log d'erreur
exec 2> ${BKPDIR}error.log

## Début du script

ionice -c3 -p$$ &>/dev/null
renice -n 19 -p $$ &>/dev/null

function cleanup {
if [ "`stat --format %s ${BKPDIR}error.log`" != "0" ] && [ "$EMAIL" != "0" ] ; then
cat ${BKPDIR}error.log | mail -s "Backup WWW - ERROR" -r "${EMAIL_EXP_NAME}<${EMAIL_EXP}>" ${EMAIL}
else echo "Aucune erreur survenue concernant la backup des fichiers www de $NAME_SITE." | mail -s "Backup WWW - OK" -r "${EMAIL_EXP_NAME}<${EMAIL_EXP}>" ${EMAIL}
fi
}
trap cleanup EXIT

if [ $(date +%d) = "01" ]; then
nice -n 0 tar -jcf $BKPDIR/$NAME_SITE_month_$MONTH.tar.bz2 -P $SOURCEDIR
elif [ $(date +%d) = "15" ]; then
nice -n 0 tar -jcf $BKPDIR/$NAME_SITE_mimonth_$MONTH.tar.bz2 -P $SOURCEDIR
else
nice -n 0 tar -jcf $BKPDIR/$NAME_SITE_day_$DAY.tar.bz2 -P $SOURCEDIR
fi

 

Test du script

Pas grand-chose de plus à faire puisque les mails ont déjà été configurés avant, donc plus qu’à le tester :

chmod +x backup_www.sh
/bin/bash backup_www.sh

 

Vérification des sauvegardes effectuées

Pour finir sur la création des sauvegardes, on n’oublie pas de les contrôler en les décompressant :

Décompression
tar jxvf nom_de_mon.tar.bz2

 

Automatisation des scripts de sauvegardes

Pour que ces sauvegardes se fassent toutes seules la nuit (ou n’importe quand), on va simplement enregistrer une cache cron en faisant crontab -e :

crontab -e
# m h  dom mon dow   command
00 01 * * * /bin/bash /home/scripts/backup/backup_bdd.sh
30 01 * * * /bin/bash /home/scripts/backup/backup_www.sh

À 1 heure du matin, on exécute la sauvegarde de la base de données, puis à 1h30 on exécute la sauvegarde des fichiers.

 

RSYNC pour exporter les sauvegardes

Je le disais au départ, le plus important réside dans la disponibilité des sauvegardes. Si elles restent stockées sur le même serveur, elles ont peu de chance d’être utiles le jour ou on en aura le plus besoin ! C’est à dire le jour ou le disque dur de votre serveur pète par exemple…

Bref, pour répondre à ce besoin d’export des sauvegardes préalablement créées, j’utilise RSYNC pour copier chaque nuit les nouvelles sur un autre serveur. On a vu qu’on avait une rotation de 7 jours sur les backups de la BDD (+ un backup chaque début de mois conservé dans un dossier à part). Même chose pour les sauvegardes du répertoire web puisque les fichiers sont nommés avec le jour de la semaine (lundi, mardi, etc.) et s’écrasent petit à petit (+ un backup en milieu et début de mois).

On va donc configurer RSYNC pour qu’il compare le répertoire de stockage des sauvegardes avec le répertoire distant pour ensuite ne copier que ce qu’il y a de nouveau.

Pour ma part j’envoie mes sauvegardes sur un Kimsufi (KS-2), et sur mon NAS qui est chez moi. Peu de chances de tout perdre d’un coup. 😉

 

Serveur source

Sur le serveur ou se trouve WordPress on va installer RSYNC (apt install rsync) puis activer son démon :

nano /etc/default/rsync
RSYNC_ENABLE=true

 

Il faut maintenant créer un utilisateur rsync, puis créer une clé SSH que l’on copiera ensuite sur le serveur de backup (ici le KS avec un utilisateur exprès pour RSYNC également) :

adduser rsync
su rsync
ssh-keygen -b 4096
# On met cette clé dans le répertoire perso de cet user : /home/rsync/.ssh/id_rsa_backup_KS
# pas de passphrase
# Maintenant on peut la copier sur le serveur de backup :
ssh-copy-id -i /home/rsync/.ssh/id_rsa_backup_KS.pub rsync-wp@IP_BACKUP_KS
# On peut maintenant tester la connexion sans mot de passe :
ssh -i /home/rsync/.ssh/id_rsa_backup_KS 'rsync-wp@IP_BACKUP_KS'

exit

 

Voilà, nous avons maintenant une connexion SSH entre nos deux serveurs possible sans mot de passe.

On va donc pouvoir créer notre script :

/home/scripts/backup/rsync.sh
#!/bin/bash
#export LC_ALL="en_EN.UTF-8"
#
## Répertoire d'enregistrement des logs ou votre user rsync aura les bonnes permissions
LOGDIR="/home/rsync/log_rsync/"
## Répertoire de la clé SSH
KEYDIR="/home/rsync/.ssh/id_rsa_backup_KS"
## Nom et IP de l'utilisateur distant
DISTANT_USER=rsync-wp
DISTANT_IP=IP_BACKUP
## Répertoire distant de stockage des backup
DISTANT_DIR="/home/rsync-wp/backup/"
##Répertoire local de stockage des backup
LOCAL_DIR="/home/backup/"
## Email pour les erreurs
EMAIL=votre_email
## Expediteur de l'email
EMAIL_EXP=backup@example.org
EMAIL_EXP_NAME=Backup
## Log de sortie
exec 1> ${LOGDIR}out.log
## Log d'erreur
exec 2> ${LOGDIR}error.log

## Début du script

ionice -c3 -p$$ &>/dev/null
renice -n 19 -p $$ &>/dev/null

function cleanup {
if [ "`stat --format %s ${LOGDIR}error.log`" != "0" ] && [ "$EMAIL" != "0" ] ; then
cat ${LOGDIR}error.log | mail -s "RSYNC - ERROR" -r "${EMAIL_EXP_NAME}<${EMAIL_EXP}>" ${EMAIL}
else echo "Aucune erreur survenue concernant la copie des backups (files & BDD) sur le serveur de backup." | mail -s "RSYNC - OK" -r "${EMAIL_EXP_NAME}<${EMAIL_EXP}>" ${EMAIL}
fi
}
trap cleanup EXIT


rsync -avz --delete --max-delete=3 -e "ssh -i $KEYDIR" $LOCAL_DIR $DISTANT_USER@$DISTANT_IP:$DISTANT_DIR

 

Jusque-là tout est bon, mais comme nous allons faire tourner ce script avec l’utilisateur rsync il reste un petit détail à faire. Comme ce n’est pas cet utilisateur qui créer les sauvegardes dans le dossier /home/backup, il n’aura pas les droits suffisants pour les envoyer sur le serveur de backup. On va donc ajouter une ligne à la fin de backup_bdd.sh et backup_www.shpour changer les droits de ce dossier à chaque fin de sauvegarde :

## DROITS
echo "Attribution des droits a l user rsync sur le repertoire /home/backup/*"
chown -R rsync /home/backup/*

 

On va pouvoir le tester sans se connecter à notre utilisateur rsync, mais d’abord on attribue on créer le répertoire des logs et on attribue les bons droits :

# Création du répertoire de log et attribution des bons droits
mkdir /home/rsync/log_rsync/
chown -R rsync /home/rsync/log_rsync
# Attribution des bons droits au script
chmod +x /home/scripts/backup/rsync.sh
su -l rsync -c "/bin/bash /home/scripts/backup/rsync.sh"

 

Automatisation du script RSYNC

Si tout fonctionne manuellement on va pouvoir enregistrer une tache cron, mais cette fois-ci, il faut que ce soit l’utilisateur rsync qui effectue cette tâche.

Il faut donc créer un fichier nommé par exemple rsync_wordpress dans le répertoire /etc/cron.d/ :

/etc/cron.d/rsync_wordpress
#
# RSYNC CRON - WordPress vers KS
#
# m h dom mon dow user command
00 02 * * * rsync /bin/bash /home/scripts/backup/rsync_KS.sh

 


Voili voilou, normalement avec ça on doit pouvoir dormir plus paisiblement en attendant le crash de son serveur. 😉

Si vous utilisez RSYNC autrement, ou si vous avez des conseils à me donner, n’hésitez pas ! Il y a des tonnes de manières de faire pour les sauvegardes. Mais le plus important est probablement de ne pas oublier de vérifier de temps en temps que votre script fonctionne. Avec un mail dans votre boite pour vous indiquer le succès (ou l’échec) de l’opération, ça réduit déjà les risques d’oublier. Je détaillerais dans un autre article comment je procède pour envoyer ensuite ces sauvegardes sur mon NAS QNAP. Histoire de vraiment dormir tranquille ! 😉