Shell : Backup local et ftp de bases de données Mysql

Comme beaucoup de personnes qui ont un ou plusieurs serveurs dédiés à gérer, j’ai eu l’envie (ou plutôt l’obligation) d’utiliser un système me permettant de sauvegarder mes bases de données tous les jours.
J’ai trouvé pas mal de scripts sur internet faisant ça, la plupart sont très bien, mais il manquait toujours un ou deux petits trucs ou des fois la méthode ne me plaisait pas. Je me suis donc retroussé les manches et je me suis codé mon script tout seul comme un grand. :)

L’idée du script

Mon idée était simple : partir avec une distribution Gentoo release 2 (ovh), n’ajouter aucune application dessus et ne se servir que de mon script pour sauvegardes les bases de données.

Le script devait pouvoir sauvegarder chaque base de données indépendemment et non pas toutes en un seul fichier comme on le voit souvent. En cas de réinstallation on ne doit pas être obligé de tout réinstaller à partir d’un dump. Si on a 10 clients ayant chacun une base de données pour son site, je me vois mal avoir à expliquer à 9 clients que leur base de données à été réinstallée avec les données de la veille parce que le 10ème client à perdu des données dans sa base. :)

J’avais aussi ces quelques exigences :

  • Le script devait aussi lister automatiquement toutes les bases de l’utilisateur et non pas inscrire les noms des bases en dur comme je l’ai vu souvent
  • Purger proprement les archives trop anciennes en local tout comme en FTP
  • Générer un rapport d’actions dans un fichier log temporaire, l’envoyer par mail et l’inscrire dans un log général
  • Donner la possibilité d’envoyer par mail en pièce jointe l’archive générée (enfin, si le backup n’est pas trop lourd)

Le script

Le script est beaucoup commenté, normalement c’est assez simple à comprendre.

#!/bin/bash

# ------------------------------------------------------------------- #
# 	BACKUP de toutes les bases MySQL d'un utilisateur
#	-------------------------------------------------
#
#	Auteur : Denis Métral-Deschamps (Denis Dee Jay)
#	Site : http://www.denisdeejay.com/
#	Date : 29/01/2010
#
#	Principe :
#	---------
#	Liste les bases de données d'un utilisateur et les sauvegarde
#	indépendamenent dans un fichier .sql puis compresse les
#	fichiers dans un tar.gz.
#	Permet d'uploader sur un serveur FTP, de purger les anciennes
#	archives en local et sur le ftp. Permet aussi de recevoir
#	l'archive par mail.
#	Renseigne ses actions dans un fichier log et poste par mail
#	un rapport de ses actions (activable ou non)
#
#	Dépendance :
#	-----------
#	Nécessite le client FTP NCFTP	: http://www.ncftp.com/ncftp/
#
#	Reste a faire :
#	--------------
#	- Activer ou non le backup FTP
#	- Trouver une meilleur solution pour purger
#	- les anciennes archives sur le FTP
# ------------------------------------------------------------------- #

# --------------------------------------------------------------------- #
#	PARAMETRES DU SCRIPT
# --------------------------------------------------------------------- #
USER="user"									# Nom d'utilisateur MySQL
DBPASS="pass"									# Pass utilisateur
EMAIL="mail@mail.com"								# Email
MAILSEND=1									# Post d'un mail? (1=Oui, 0=Non)
JOINDB=0										# Envoyer l'archive en piece jointe au mail? (1=Oui, 0=Non)
SUJETMAIL="Backup MySQL quotidien"					# Sujet du mail
BACKUPDIR="/home/backup/sql/"						# Rep de backup (temporaire si KEEPLOCAL = 0). Ne pas oublier le "/" à la fin (important !!)
OLD_DAYS=5;									# Nombre de jours où les backup sont conservés
K_DAYS=`date --date $OLD_DAYS' days ago' "+%Y-%m-%d"`;	# On construit la recherche des anciens fichiers
LOG="/home/log/backup_sql.log";						# Log des backup
DATE="$(date +"%Y-%m-%d")";						# Date du jour
DATE_NOW=`date +"%d/%m/%Y - %H:%M:%S"`;			# Date et heure en temps réel (pour le log)
ARCHIVE="mysql_backup_"$DATE".tar.gz";					# Noms des archives
OLD_ARCHIVE="mysql_backup_"$K_DAYS".tar.gz";			# Les anciennes archives
KEEPLOCAL=1									# Garder une copie locale? (1=Oui, 0=Non)
FTP_BACKUP=1									# Copie sur FTP? (1=Oui, 0=Non)
FTP_SERVER="ftp.backup.com"						# Serveur FTP
FTP_USER="login_ftp"								# Login utilisateur FTP
FTP_PASS="pass_ftp"								# Pass utilisateur FTP
FTP_DIR="/bdd"									# Répertoire sur le FTP (par exemple : /backups/serveur1/bdd)
NCFTPPUT="/usr/bin/ncftpput"						# Emplacement de ncftpput
NCFTP="/usr/bin/ncftp"								# Emplacement de ncftp

# --------------------------------------------------------------------- #
#	FIN DES PARAMETRES, ne rien changer ensuite		                 #
# --------------------------------------------------------------------- #
# On se place dans le rep
cd $BACKUPDIR;

# creation du log temporaire
TMP_LOG="tmp_backup.log";
>$TMP_LOG;
echo "-- Rapport généré automatiquement par $(basename $0)" >> $TMP_LOG;
echo "" >> $TMP_LOG;

# et c'est parti :)
echo "["$DATE_NOW"] - MySQL : DEBUT sauvegarde des bases" >> $TMP_LOG;

# On crée un rep temporaire qui contiendra les sauvegardes
if [ ! -d $DATE ]; then
       mkdir $DATE"/";
       chmod 500 $DATE;
fi

# On liste toutes les bases de données auquel l'utilisateur a access
DATABASES="$(mysql -u $USER -p$DBPASS -Bse 'show databases;')";

# Si une erreure survient pendant la liste des bases
if [ ! $? -eq 0 ]; then
       rm -rf $DATE;
       echo "["$DATE_NOW"] - Une erreure s'est produite pendant le listage des bases de données" >> $TMP_LOG;
       cat $TMP_LOG >> $LOG;
       rm $TMP_LOG;
       exit 1;
fi

# Boucle : pour chaque base ...
for BASE in $DATABASES
do
	# Analyse de la base
	mysqlcheck -u $USER -p$DBPASS -c -a $BASE > /dev/null

	echo "["$DATE_NOW"] - Sauvegarde de la base '"$BASE"'" >> $TMP_LOG;

	# Sauvegarde de la base
	mysqldump -u $USER -p$DBPASS --add-drop-database  --add-drop-table --complete-insert --routines --triggers --allow-keywords --max_allowed_packet=50M --force $BASE -R > $DATE"/"$BASE".sql";
	# Note sur les options de mysqldump :
	# --add-drop-database	= Ajoute en tete de fichier la requete "drop database"
	# --add-drop-table		= Ajoute devant chaque creation de table la requete "drop table"
	# --complete-insert		= Utilise les insertions etendues (compresse plus mais ATTENTION, si une erreure dans la requete, tous les champs sont bloques)
	# --routines			= http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_routines
	# --trigers			= http://dev.mysql.com/doc/refman/5.1/en/mysqldump.html#option_mysqldump_triggers
	# --allow-keywords		= Permet la création de colonnes ayant des noms de mots réservés.
	# --max_allowed_packet	= Lors de la création de commandes d'insertions etendues, mysqldump va créer des lignes ayant une taille maximale de max_allowed_packet octets
	# --force				= Continue même si une erreur SQL survient durant l'export.
	# --opt				= Identique à --quick --add-drop-table --add-locks --extended-insert --lock-tables. Obtient l'export le plus rapide à importer dans MySQL. 

		# Une erreure pendant la sauvegarde de la base
		if [ ! $? -eq 0 ]; then
			rm -rf $DATE;
			echo "["$DATE_NOW"] - Une erreure est survenue pendant la sauvegarde de la base '"$BASE"'" >> $TMP_LOG;
               cat $TMP_LOG >> $LOG;
               rm $TMP_LOG;
               exit 1;
		fi
done

# On construit l'archive contenant les sauvegardes
tar -czf $ARCHIVE $DATE"/";

# Si une erreure survient pendant la création de l'archive
if [ ! $? -eq 0 ]; then
	rm -rf $DATE;
	echo "["$DATE_NOW"] - Une erreur est survenue pendant la creation de l'archive : '"$ARCHIVE"'" >> $TMP_LOG;
	cat $TMP_LOG >> $LOG;
	rm $TMP_LOG;
	exit 1;
fi

FILESIZE=`ls -lh ${ARCHIVE} | awk '{print $5}'`
echo "["$DATE_NOW"] - L'archive '"$ARCHIVE"' ("$FILESIZE") a bien été crée" >> $TMP_LOG;

# On supprime le répertoire temporaire
rm -rf $DATE;

# On supprime les archives trop anciennes et on renseigne le log
echo "["$DATE_NOW"] - Recherche et supprime les archives ayant plus de "$OLD_DAYS" jours" >> $TMP_LOG;
find $BACKUPDIR -maxdepth 1 -type f -ctime +$OLD_DAYS  -exec rm -v {} \; >> $TMP_LOG ;

# Upload sur le serveur FTP
echo "["$DATE_NOW"] - Debut d'upload sur le FTP : "$FTP_SERVER >> $TMP_LOG;
$NCFTPPUT -m -u $FTP_USER -p $FTP_PASS $FTP_SERVER $FTP_DIR $ARCHIVE
OSTAT="$?"
case $OSTAT in
	0) MESS="Succès.";;
	1) MESS="Erreur : ne peut se connecter à l'hôte $FTPH.";;
	2) MESS="Erreur : ne peut se connecter à l'hote $FTPH - temps dépassé.";;
	3) MESS="Transfert échoué.";;
	4) MESS="Transfert échoué - temps dépassé.";;
	5) MESS="Changement de répertoire échoué.";;
	6) MESS="Changement de répertoire échoué - temps dépassé.";;
	7) MESS="URL mal formé.";;
	8 ) MESS="Erreur d'utilisation. Peut être que votre version de ncftpput ($NCFTPPUT) est trop ancienne";;
	9) MESS="Erreur dans le fichier de configuration de login.";;
	10)MESS="Initialisation de la librairie échouée.";;
	11) MESS="Initialisation de session échouée.";;
	*) MESS="Erreur inconnue";;
esac
echo "["$DATE_NOW"] - Fichier : $ARCHIVE --> $FTP_DIR" >> $TMP_LOG;
echo "["$DATE_NOW"] - Status de l'upload : $MESS" >> $TMP_LOG;

# on supprime la plus vieille archive sur le ftp
# pour l'instant, pas trouvé mieux que de supprimer la dernière archive ... pas trouvé de find ou autre sur ncftp
echo "["$DATE_NOW"] - Suppression de l'archive la plus vieille sur le ftp" >> $TMP_LOG;
$NCFTP -u $FTP_USER -p $FTP_PASS $FTP_SERVER << EOF
rm $FTP_DIR"/"$OLD_ARCHIVE
quit
EOF

# Suppression de la sauvegarde locale si demande
if [ "$KEEPLOCAL" = 0 ]; then
	rm $BACKUPDIR*
fi

#Envoi du log temporaire par mail
if [ "$MAILSEND" = 1 ]; then
	if [ "$JOINDB" = 1 ]; then
		(cat $TMP_LOG; uuencode $ARCHIVE $ARCHIVE) | mail -s "$SUJETMAIL ("$DATE")" $EMAIL
		echo "["$DATE_NOW"] - Email envoyé avec pièce jointe" >> $TMP_LOG;
	else
		mail -s "$SUJETMAIL ("$DATE")" $EMAIL < $TMP_LOG;
		echo "["$DATE_NOW"] - Email envoyé sans pièce jointe" >> $TMP_LOG;
	fi
fi

# On finalise le log temporaire, place son contenu dans le log permanent et supprimer le log temporaire
echo "["$DATE_NOW"] - FIN du backup" >> $TMP_LOG;
echo "" >> $TMP_LOG;
cat $TMP_LOG >> $LOG;
rm $TMP_LOG;

exit

Pour les prochaines mises à jour

  • Rendre activable le backup FTP ou non
  • Trouver une meilleur solution pour purger les anciennes archives sur le FTP ; Si quelqu’un à une idée en utilisant NCFTP ou non, elle est bienvenue :)

Utilisation du script

Pour utiliser ce script de backup c’est très simple, il vous suffit de faire seulement ces 3 choses :
1) Régler vos paramètres
2) Un chmod +x pour le rendre executable (a personnaliser au nom du fichier)

chmod +x /root/mes_scripts/mon_fichier.sh

3) Ajout d’une tache cron (a personnaliser bien entendu)

crontab -e
00 05 * * * /root/mes_scripts/mon_fichier.sh

Si vous avez des idées pour améliorer ce script, n’hésitez pas à poster des commentaires ;)

Cet article vous a plu ? Partagez le !
  • Twitter
  • Facebook
  • Google Bookmarks
  • Wikio
  • Digg
  • Sphinn
  • del.icio.us
  • Mixx
  • LinkedIn
  • Live
  • MySpace
  • Scoopeo
  • Wikio IT
  • Yahoo! Buzz

2 commentaires sur ce billet.

  1. Faire attention, à la ligne contenant :
    8 ) MESS= »Erreur d’utilisation.
    j’ai mis un espace entre 8 et ) qu’il faudra retirer.
    ca me mettait le smiley  » 8)  » dans le billet sinon ;)

  2. [...] le script shell de sauvegardes des bases de données mysql je m’attaque à la sauvegardes des comptes FTP liés aux sites internet sur l’un de mes [...]

Respond to this post