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. ![]()
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 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 partiecho "["$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 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
Faire attention, à la ligne contenant :
» dans le billet sinon
8 ) MESS= »Erreur d’utilisation.
j’ai mis un espace entre 8 et ) qu’il faudra retirer.
ca me mettait le smiley »
Salut,
Merci pour ton script. Je viens de le tester, je l’ai lancé directement pour vérifier le bon fonctionnement avant de laisser l’automatisme faire son travail.
Mais je commence à m’inquiéter.
Le script à été lancé il y a 35 minutes et dans le répertoire de sauvegarde il n’y a qu’une base sauvegardée pour l’instant de 47 méga et une autre qui devrait être beaucoup plus petite qui pour l’instant reste à 0 octet alors que le script est dessus depuis 30 minutes au moins.
Du coup aucun site n’est disponible bien sur puisqu’ils ne peuvent plus se connecter au serveur sql. Je voulais arreter le processus mais j’ai peur de faire une bêtise, qu’en pense tu ?
Salut,
Désolé de ne pas avoir répondu plus tôt, j’ai enchainé les réunions hier. Le script ne fait qu’effectuer des dumps de bdd (donc appelle mysqldump, donc il faut que l’utilisateur ait les droits d’execution sur mysqldump) + génération d’un tar.gz + upload sur un FTP + suppresion de la plus ancienne sauvegarde sur le FTP.
Quand on le fait tourner les 1eres fois il peut te retourner des « erreurs » car il n’aura pas trouvé les anciens fichiers à supprimer (normal puisqu’ils n’y sont pas). Quand tu le lances, tu peux très bien l’arretter si tu le souhaites. Si il reste des processus qui tournent (genre mysqldump) tu peux les « tuer » aussi (kill ID_process).
Personnellement, je le fais tourner avec l’utilisateur root directement, comme ça je me pose pas de questions sur les droits de lecture / écriture / execute. mais il faut bien que ton fichier par contre soit executable (chmod +x).
Ce script n’est plus tout jeune mais je continue de l’utiliser. Cette nuit encore il m’a généré une archive contenant les 24 dumps de 24bdd. Certaines sont vides mais d’autres dépassent les 50mo et ça ne pose pas de problème. J’ai testé un dump sur une bdd de 500mo, sur un site en production et de jour en plus (mon site sur star wars, http://www.swg1.net) et le dump n’a pris que quelques secondes. Je ne l’ai pas testé avec mon script car il est sur une autre machine (red hat) où les sauvegardes sont gérées différemment mais quand tu vois mon script, et surtout son minimalisme, il n’y a pas vraiment de raisons que ça plante. ^^
En espérant que ça ait pu répondre a tes questions. Si tu en as d’autres n’hésites pas
Merci d’avoir répondu. Mais tu n’as pas ni à t’excuser ni à te justifier lol, chacun son boulot et tu n’est pas non là pour assurer un sav de ton script, c’est déjà très gentil de l’avoir partagé et d’avoir pris le temps de me répondre.
Pour l’histoire, ce n’est pas le script qui avait planté mais le service mysql, du coup le script ne pouvait plus tourner.
J’ai relancé mysql puis le script et c’est passé comme une lettre à la poste en quelques secondes upload compris pour une archives de 180Mo au total quand même.
Je vais maintenant tester ton script pour la sauvegarde du ftp
Merci encore de ta générosité.
Bonjour denis,
Merci pour ce script ! J’ai d’ailleurs quelques petites questions que je t’ai posé sur ton mail @aol.com
Si tu en as changé, fais le moi savoir
Julien
Personne ? Tu n’as pas eu mon précédent message ?
Julien
J’ai répondu au mail avant d’activer les commentaires à ce que je vois ^^
Une piste concernant mon message du 09 décembre ? Peut être stoper apache avant la sauvegarde ?
Mince MDR, désolé, mon message en question est celui que j’ai posté coté backup des comptes FTP, désolé.
Re,
Je t’ai répondu ici :
http://www.denisdeejay.com/langages/shell/shell-backup-multiples-separes-ftp-181.html
++
Bonjour denis,
Je suis en train d’utiliser ton script pour mes sites.
Juste une petite erreur :
si on met KEEPLOCAL à 0 ca supprime le log temporaire en même temps et ca génère une erreur sur la ligne du mail car il cherche un fichier qui n’existe plus ($TMP_LOG).
j’ai juste déplacez le code :
————————————————————————–
# Suppression de la sauvegarde locale si demande
if [ "$KEEPLOCAL" = 0 ]; then
rm $BACKUPDIR*
fi
————————————————————————–
avant le exit et c’est bon.
Sinon pour ceux qui gère sous plesk et qui veulent activer ce script en CRON :
Se rendre au niveau serveur puis ‘taches planifiées’ puis sur l’utilisateur ‘root’ et enfin créer une nouvelle tache. Ne pas passer par les taches planifiées au niveau domaine.
Merci pour ce script
Salut sylvain,
J’ai mis un mois a activer ce commentaire, je crois vraiment que je vais mettre l’activation par défaut maintenant ^^
Effectivement, il faut déplacer la portion de code comme tu l’indiques si KEEPLOCAL = 0
Une solution plus propre serait de :
- déplacer le log temporaire
- remplacer la valeur de $TMP_LOG avec la nouvelle valeur du log temporaire
- rm -v $BACKUPDIR >> $TMP_LOG; (comme ça on indique dans le log la suppresion)
- on poste le mail si $MAILSEND = 1
C’est du vite faire hein ^^ Comme ce scrpt d’ailleurs. Mais bon, il tourne bien depuis le temps que je l’ai mis en service j’ai toujours pas eu de problème.
Vu que tu es sur Plesk, tu peux te servir de « lftp » qui est plus puissant que ncftp. sur la gentoo release 2 d’OVH on a pas le choix, c’est ncftp ou ftp « de base ». LFTP permet de faire de la copie « mirroir » du rep local au rep FTP, faire des recherches et tri par dates sur le serveur FTP etc.
M’enfin, c’est qu’une suggestion.
@+
Petit EDIT : je viens de mettre à jour ma Release et que vois-je : LFTP est maintenant installé !!!
Ca va me simplifier la vie ça ^^
Bonjour,
Alors on va avoir une revisite du code avec lftp ?
Cordialement,
Surement
Mais pas tout de suite non plus ^^
lftp me facilitera la vie dans pas mal de traitements, ce serait dommage de ne pas en profiter