Sauvegarde automatisé en Bash

On m’a demandé hier soir sur un canal IRC comment je procédais à ma sauvegarde sur Dedibox via le FTP distant mis à disposition. L’idée étant de transmettre sur le FTP distant des fichiers à sauvegarder et ce régulièrement…

Il existe des programmes de sauvegardes assez complets à paramétrer et configurer, mais étant donnée le peu de répertoire que je cherche à sauver, je ne voyais pas trop l’intérêt dans mon cas. Un simple script bash bien bricolé peut suffir, et c’est ce que je vais vous montrer.

Nous allons créer un petit script bash qui va créer une archive de nos données à sauvegarder (www et un dump Mysql), puis se connecte au FTP et envoit les fichiers. L’automatisation des sauvegardes de fichier n’est pas le plus compliqué, là où on commence à se poser des questions c’est le moment où il faut se connecter au FTP, s’authentifier et envoyer les fichiers…

Voilà donc la solution que j’ai trouvé. Elle n’est pas parfaite, mais elle a le mérite de fonctionner.

Archivage des fichiers

Commençons par le début, l’idée est de faire un .tar, ou tar.gz ou tag.bz2 (appelé tarball) de nos répertoires à sauver. Pour /var/www nous ferons, dans notre script, quelque chose du genre:

` #on crée un répertoire de sauvegarde temporaire mkdir -p /tmp/save_dir #on entre dedans cd /tmp/savedir

#puis on archive nos données tar cf www.tar /var/www #si nous voulions le compresser en gz (plus lent): #tar cfz www.tar.gz /var/www #ou en bz2 (encore plus compressé, mais encore plus lent) #tar cfj www.tar.bz2 /var/www

`

L’option “cf” veut dire “**c**ompress //in// **f**ile”. L’option “z” signifie “gzip”, et “j” signifie “bz2”. C’est dans la page de man de ma commande tar que vous pouvez trouver ces informations.

Cela crée donc une archive de notre répertoire /var/www.

Dump de MySQL

Passons au dump mysql, une commande permet de créer une archive des données au format SQL: mysqldump.

Remplacez user par votre nom d’utilisateur mysql qui a le plus de droits, et donnez le mot de passe à la place de password:

mysqldump -u user -p password

On peut redirriger la sortie dans un fichier afin de sauvegarder tout ça:

mysqldump -u user -p password > /tmp/savedir/mysqldump.sql

Sauf que voilà… j’ai pas mal de bases de données et je préfèrerai les séparer à raison de un fichier SQL par base. Donc, par exemple, si j’ai une base “Foo” je devrai faire:

mysqldump -u user -p password Foo > /tmp/savedir/mysqldump.Foo.sql

Sauf que je ne veux pas m’amuser à changer mon script de sauvegarde chaque fois que je crée ou que je supprime une base… Il faut donc automatiser aussi la récupération des noms de base de données et envoyer la commande mysqldump pour chacune d’elle. C’est en fait plus simple que vous ne le pensez.

Mysql vous afficher les noms de bases de données via la commande “show databases;“, il suffit de faire ceci pour s’en rendre compte:

echo "show databases;" | mysql-u user -p password
Database
Foo
Bar
Baz

Aïe… zut… j’ai “Database” qui apparait mais c’est en fait le nom de la colonne qui apparait pour ma demande… “Database” n’est pas une base… et bien on va la supprimer de la sortie avec sed:

echo "show databases;" | mysql-u user -p password | sed "s/Database//"

Foo
Bar
Baz

Beaucoup mieux! Bon, pour chaque élement de la liste on va sauver notre base dans un fichier… `` #je crée un répertoire pour mes bases et j’y entre mkdir -p /tmp/save_dir/dbs cd /tmp/save_dir/dbs

#je récupère la liste: DBS=echo "show databases;" | mysql-u <user> -p <password> | sed "s/Database//" for db in $DBS do mysqldump -u user -p password $db > $db.sql done ``

Reste à compresser le répertoire: ` #on remonte d’un cran dans les répertoires: cd .. #ou alors directment #cd /tmp/save_dir

#on compresse tar cf dbs.tar dbs #ou en tar.gz #tar cfz dbs.tar.gz dbs #ou en bz2 #tar cfj dbs.tar.gz dbs `

Envoit au FTP

Voilà, en ce qui concerne la récupération des données et leur archivage, c’est bon… Reste à nous connecter au FTP, s’authentifier, puis envoyer les données. Il va falloir user d’une //astuce// pour envoyer les commandes ftp. On va imaginer qu’on utilise le FTP de Dedibox, adaptez cela à votre guise pour d’autres FTP.

Normalement, en mode “interactif”, nous devrions faire, :

[root@sd-XXXXX ~]# ftp dedibackup.dedibox.fr
Connected to dedibackup2.dedibox.fr.
220- 
Je backup ... 
  __                                  _   
 /  )  _  _/  '  /  _  _  /           _)  
/(_/  (- (/  /  () (/ (  /( (/  /)   /__  
                               /          
                             ... donc je suis

Espace disque  : 40To                    
En cas de difficulte de connexion, contactez
l'assistance technique Dedibox

RAPPEL : Les uploads ne sont pas autorises
depuis une adresse IP externe au reseau Dedibox.

220 Bienvenue sur Dedibackup 2 - Identifiez-vous
530 login first
530 login first
KERBEROS_V4 rejected as an authentication type

Arrrive alors l’invite de commande FTP, on nous demande un login (ici par exemple “sd-XXXX”) et un mot de passe à taper quand on nous le demande:

Name (dedibackup.dedibox.fr:root): sd-XXXX
331 Mot de passe obligatoire
Password: (je tape mon mot de passe)
230 Identifiants Dedibackup acceptes
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> 

Tout est interactif… automatiser cela parait compliqué… en fait… non. Il suffit d’envoyer le flux de commande à la volée. On la refait… d’abord sortez de l’invite ftp en tapant quit

ftp> quit
221 A bientot
[root@sd-XXXXX ~]# 

Regardez:

# ftp -i -n dedibackup.dedibox.fr << EOF
quote user sd-XXXX
quote pass MON_MOT_DE_PASSE
quit
EOF

Ok, on a quitté après authentification et on revient à l’invite de commande du Shell. Les option “-i” et “-n” font respectivement “couper le mode interactif” et “utilise l’auto-login”.

Donc, si nous somme dans le répertoire de sauvegarde, on pourrait envoyer le dump MySQL pour tester:

# ftp -i -n dedibackup.dedibox.fr << EOF
quote user sd-XXXX
quote pass MON_MOT_DE_PASSE
put dbs.tar
quit
EOF

Après un petit moment, le fichier se retrouve sur le ftp…

Premier script simple

Bien. On récapitule: -on sait archiver notre répertoire /var/www -on sait dumper les bases mysql -on sait se connecter au FTP et envoyer un fichier.

Il serait bon de ne pas écraser nos sauvegardes, et donc de garder une sauvegarde par jour. On va donc “dater” nos fichiers, par exemple en leur donnant un nom qui contient la date.

Allons-y, on regarde ce que propose Linux pour afficher une date.:

# date
jeu oct  9 23:36:59 CEST 2008

Un peu long comme nom, et puis avec les espaces on va avoir du mal à “échapper” tout ça… sachant que nous utilisons une sauvegarde par jour, l’heure ne nous importe pas… et il serait préférable d’avoir une date au forma “Ymd” (Anneée sur 4 chiffre, mois sur deux chiffre et jour sur deux chiffre). Donc d’avoir en résultat pour le 9 octobre 2008: “20081009”.

Un petit tour dans la page de man de la commande date et on trouve ce que nous cherchons.

# date +"%Y%m%d"
20081009

J’ai donc ce qu’il me faut pour commencer un bon script… **attention la suite n’est pas le script définitif, allez jusqu’au bout de l’article !!!**

Voilà, donc un premier scrip qui peut marcher: ` #!/bin/bash DATE=date +“%Y%m%d”` MYSQLUSER=“myuser” MYSQLPASS=“mypass” MYSQLOPTS=“-u $MYSQLUSER -p $MYSQLPASS” FTPSERVER=“dedibackup.dedibox.fr” FTPUSER=“sd-XXXXX” FTPPASSWD=“MON_MOT_DE_PASSE”

#cette commande crée les répertoire save_dir et mysqldump mkdir -p /tmp/save_dir/mysqldump

tar cf www.$DATE.tar /var/www

#dump des bases DBS=echo "show databases;" | mysql $MYSQLOPTS | sed "s/Database//" for db in DBS do mysqldump $MYSQLOPTS > mysqldump/$db.sql done

#archivage des dumps tar cf mysqldump.$DATE.tar mysqldump

ftp -i -n $FTPSERVER << EOF quote user $FTPUSER quote pass $FTPPASSWD put www.$DATE.tar put mysqldump.$DATE.tar quit EOF

#Reste à supprimer le répertoire temporaire que vous utilisé #pour les sauvegarde après le transfère: cd /tmp rm -rf /tmp/save_dir

``

Second script amélioré

Ceci fonctionne très bien… sauf que voilà… Après un certain temps, votre espace FTP va réduire, réduire… et réduire tellement que vous n’aurez plus de place. Dedibox m’offre 5Go d’espace, croyez moi ça se rempli très vite. J’ai donc décidé de ne garder que 2 ou 3 jours de sauvegardes. Il faut donc trouver le moyen de supprimer les fichiers du FTP qui ont une date inférieur à 2 jours.

Comme nous nommons nos fichier avec la date, cela va être très très simple. Seul souci: trouver comment avoir la date datant de deux jours, en gérant les mois de 30, 31, 28 ou 29 jours… Et encore une fois, la page de man de la commande date nous donne la solution. L’option “-d” nous permet de taper littéralement (en anglais) ce que nous cherchons.

# date
jeu oct  9 11:51:51 CEST 2008
# date -d "-2 day"
mar oct  7 11:51:40 CEST 2008
# date -d "-2 day" +"%Y%m%d"
20081007

Donc, maintenant, on peut faire: ``

[… snip …]

OLD=date -d "-2 day" +"%Y%m%d"

[… snip …]

ftp -i -n $FTPSERVER << EOF quote user $FTPUSER quote pass $FTPPASSWD delete www.$OLD.tar delete mysqldump.$OLD.tar put www.$DATE.tar put mysqldump.$DATE.tar quit EOF

#Reste à supprimer le répertoire temporaire que vous utilisé #pour les sauvegarde après le transfère: cd /tmp rm -rf /tmp/save_dir

``

Ce qui a pour effet de supprimer le fichier qui a la date du jour moins 2 jours.

Crontab pour automatiser

Automatiser ceci dans un crontab. Posez votre script de sauvegarde dans /usr/local/bin et rendez le exécutable:

chmod +x /usr/local/bin/save.sh

Puis tapez:

#Pour changer d'éditeur si vous ne comprennez rien à vi (vim), et que vous préférez "nano":
#EDITOR="nano"

crontab -e

Vous arrivez sur l’édition du “crontab”, on va imaginer que nous voulons sauvegarder toutes les nuits à 4h00 du matin:

0 4 * * * /usr/local/bin/save.sh >> /var/log/savesystem

Enregistrez le fichier, puis relancez crontab:

/etc/init.d/crond restart

Script complet

Pour terminer, voici ce que j’utilise sur ma Dedibox: ` #!/bin/bash echo "Process save begin atdate`”

#FTP Auth FTPSERVER=“dedibackup.dedibox.fr” FTPUSER=“sd-XXXXX” FTPPASSWD=“MOT_DE_PASSE”

#MySQL Auth MYSQLUSER=“MySQLUser” MYSQLPASSWD=“MySQLPassword”

#number days to keep save files NBRDAYS=2

—– PROCESS SAVE —–

#don’t touch this LASTDATE=date -d "-$NBRDAYS day" +"%Y%m%d" DATETIME=date +"%Y%m%d" PASSMYSQL=“-u$MYSQLUSER -p$MYSQLPASSWD” WWW=“www.$DATETIME.tar” MYSQLDUMP=“mysqldump.$DATETIME.tar”

LASTMYSQL=“mysqldump.$LASTDATE.tar” LASTWWW=“www.$LASTDATE.tar”

#create save directories mkdir -p /tmp/save_dir/mysql #go to /tmp cd /tmp/save_dir

echo “Tar www…” tar cf $WWW /var/www

#dump sql echo “dump databases” DBS=echo "show databases;" | mysql $PASSMYSQL | sed "s/Database//" for db in $DBS do echo “dumping $db…” mysqldump $PASSMYSQL “$db” > mysql/$db.sql done

tar cvf $MYSQLDUMP mysql ftp -i -n $FTPSERVER << ENDF quote user $FTPUSER quote pass $FTPPASSWD delete $LASTMYSQL delete $LASTWWW put $MYSQLDUMP put $WWW quit ENDF

cd /tmp rm -rf save_dir echo “Save ended at date“ ``

Ce qui me permet de sauvegarder 2 jours de données. Vous pouvez changer le nombre de jours en haut dans la variable “NBRDAYS”. N’oubliez pas de changer les login et mot de passe de MySQL et FTP, ainsi que l’adresse du serveur FTP que vous utilisez.

comments powered by Disqus