Préparation


On va créer deux dossiers, un pour les scripts, l'autre pour les sauvegardes. Pour ma part, ces deux dossiers se trouvent dans /root. Suivant comment vous avez partionné votre système, vous pouvez adapter les chemin.

Création de l'arborescence :
cd /root
mkdir scripts
mkdir -p save/www
mkdir -p save/mysql


Script de sauvegarde des fichiers


Ce premier script ne s'occupe de sauvegarder que les fichiers d'un site web, c'est à dire le document root d'un vhost. Si vous avez suivi mon article de configuration Apache, les fichiers pour un vhost spécifique se trouve dans /var/www/<cname>/. Le script crée une archive en tar.gz et effectue une rotation pour garder les N dernières sauvegardes. Enfin, il log dans un fichier dédié.

Le script save_www.sh


Ce script crée une archive tar.gz d'un répertoire dans /var/www, puis il fait une rotation de ces archives tar.gz afin de conserver un historique de N jours.

On crée le fichier :
vim /root/scripts/save_www.sh

Copiez collez le script suivant :
#!/bin/bash

# Parametres dossiers
www_root="/var/www"
save_root="/root/save/www"
script_root="/root/scripts"
# Parametres fichiers
www_filter="$script_root/save_www.filter"
www_list="$script_root/save_www.list"
save_error="/root/script.error"
save_log="/var/log/save_www.log"

# On se place dans www_root
cd $www_root

for www_site in `cat "$www_list"`
do
        # Archivage fichiers du site
        debug=`tar zcvfh "$save_root/$www_site.tar.gz.tmp" "$www_site" --exclude-from "$www_filter"`

        # Check reussite commande TAR
        if [[ $? -ge "1" ]]
        then
                # Commande echouee
                echo "`date +%d/%m/%y-%H:%M:%S` : Erreur sauvegarde du site $www_site" >> "$save_log"
                rm "$save_root/$www_site.tar.gz.tmp"
                echo "$debug" > $save_error
                exit
        fi

        # Rotation des archives
        for (( i=8; i>0; i-- ))
        do
                # L'archive precedente
                j=`expr $i - 1`
                if [[ $j -ge "1" ]]
                then
                        # Check archive existe
                        if [[ -e "$save_root/$www_site.tar.gz.$j" ]]
                        then
                                # La premiere archive ne s'appelle pas .tar.gz.0
                                debug=`mv "$save_root/$www_site.tar.gz.$j" "$save_root/$www_site.tar.gz.$i"`
                                # Check reussite commande MV
                                if [[ $? -ge "1" ]]
                                then
                                        # Commande echouee
                                        echo "`date +%d/%m/%y-%H:%M:%S` : Erreur mv $www_site.tar.gz.$j vers $www_site.tar.gz.$i" >> "$save_log"
                                        echo "$debug" > $save_error
                                        exit
                                fi
                        fi
                else
                        # Check archive existe
                        if [[ -e "$save_root/$www_site.tar.gz" ]]
                        then
                                debug=`mv "$save_root/$www_site.tar.gz" "$save_root/$www_site.tar.gz.$i"`
                                # Check reussite commande MV
                                if [[ $? -ge "1" ]]
                                then
                                        # Commande echouee
                                        echo "`date +%d/%m/%y-%H:%M:%S` : Erreur mv $www_site.tar.gz vers $www_site.tar.gz.$i" >> "$save_log"
                                        echo "$debug" > $save_error
                                        exit
                                fi
                        fi
                fi
        done
       
        # Nouvelle archive
        debug=`mv "$save_root/$www_site.tar.gz.tmp" "$save_root/$www_site.tar.gz"`
        # Check reussite commande MV
        if [[ $? -ge "1" ]]
        then
              # Commande echouee
              echo "`date +%d/%m/%y-%H:%M:%S` : Erreur mv $www_site.tar.gz.tmp vers $www_site.tar.gz" >> "$save_log"
              echo "$debug" > $save_error
              exit
        fi

        # Taille nouvelle archive
        size_site=`du -hs $save_root/$www_site.tar.gz | awk '{ print $1 }'`

        # Log l'archivage
        echo "`date +%d/%m/%y-%H:%M:%S` : Sauvegarde du site $www_site [$size_site]" >> "$save_log"

done

# Permissions sur archives
cd $save_root
chmod 400 *


Pensez à ajouter les droits d'exécution :
chmod 754 /root/scripts/save_www.sh

Le fichier save_www.list


Ce fichier liste les sites à sauvegarder. Je considère que chaque site à son propre répertoire dans /var/www. Il suffit de mettre le nom du répertoire pour qu'il soit sauvegarder. Un nom par ligne séparé par un retour chariot.

On crée le fichier :
vim /root/scripts/save_www.list

Exemple de fichier :
blog
wiki
galerie


Le fichier save_www.filter


Ce fichier liste les extensions de fichiers qui ne seront pas sauvegardées. J'exclue en général les .mp3 et les .avi qui peuvent peser très très lourd. A ajuster en fonction de votre besoin et de votre volumétrie.

On crée le fichier :
vim /root/scripts/save_www.filter

Exemple de fichier :
*.mp3
*.MP3
*.avi
*.AVI


Mise en place du cron


On édite la crontab :
crontab -e

On ajoute cette ligne :
# Sauvegarde WWW
0 0 * * * bash /root/scripts/save_www.sh > /dev/null


Rotation des logs


Le script écrit un événement dans un journal à chaque sauvegarde. On va utiliser logrotate.d pour s'assurer que ces journaux gardent une taille raisonnable.

On crée un fichier de conf pour nos journaux :
vim /etc/logrotate.d/save_www

Avec le contenu suivant :
/var/log/save_www.log {
monthly
missingok
rotate 6
}


On recharge la configuration du démon logrotate :
logrotate -f /etc/logrotate.conf

Script de sauvegarde des bases de données


Le script save_mysql.sh


Ce script ressemble à son copain, il fait un dump par base et fait une rotation des dumps afin de garder N jours d'historique.

On crée le fichier :
vim /root/scripts/save_mysql.sh

Copiez collez le script suivant :
#!/bin/bash

# Parametres dossiers
save_root="/root/save/mysql"
script_root="/root/scripts"
# Parametres fichiers
mysql_list="$script_root/save_mysql.list"
save_error="/root/script.error"
save_log="/var/log/save_mysql.log"
# Compte dump mysql
mysql_user="root"
mysql_pass="<password>"

# Dump base par base
for mysql_site in `cat "$mysql_list"`
do

        # Dump de la base
        debug=`mysqldump -u $mysql_user --password=$mysql_pass --add-drop-table $mysql_site > "$save_root/$mysql_site.sql.tmp"`

        # Check reussite commande MYSQLDUMP
        if [[ $? -ge "1" ]]
        then
                # Commande echouee
                echo "`date +%d/%m/%y-%H:%M:%S` : Erreur sauvegarde de la base $mysql_site" >> "$save_log"
                rm "$save_root/$mysql_site.sql.tmp"
                echo "$debug" > $save_error
                exit
        fi

        # Rotation des archives
        for (( i=8; i>0; i-- ))
        do
                # Le dump precedent
                j=`expr $i - 1`
                if [[ $j -ge "1" ]]
                then
                        # Check dump existe
                        if [[ -e "$save_root/$mysql_site.sql.$j" ]]
                        then
                                # Le premier dump ne s'appelle pas .sql.0
                                debug=`mv "$save_root/$mysql_site.sql.$j" "$save_root/$mysql_site.sql.$i"`
                                # Check reussite commande MV
                                if [[ $? -ge "1" ]]
                                then
                                        # Commande echouee
                                        echo "`date +%d/%m/%y-%H:%M:%S` : Erreur mv $mysql_site.sql.$j vers $mysql_site.sql.$i" >> "$save_log"
                                        echo "$debug" > $save_error
                                        exit
                                fi
                        fi
                else
                        # Check dump existe
                        if [[ -e "$save_root/$mysql_site.sql" ]]
                        then
                                debug=`mv "$save_root/$mysql_site.sql" "$save_root/$mysql_site.sql.$i"`
                                # Check reussite commande MV
                                if [[ $? -ge "1" ]]
                                then
                                        # Commande echouee
                                        echo "`date +%d/%m/%y-%H:%M:%S` : Erreur mv $mysql_site.sql vers $mysql_site.sql.$i" >> "$save_log"
                                        echo "$debug" > $save_error
                                        exit
                                fi
                        fi
                fi
        done

        # Nouveau dump
        debug=`mv "$save_root/$mysql_site.sql.tmp" "$save_root/$mysql_site.sql"`
        # Check reussite commande MV
        if [[ $? -ge "1" ]]
        then
              # Commande echouee
              echo "`date +%d/%m/%y-%H:%M:%S` : Erreur mv $mysql_site.sql.tmp vers $mysql_site.sql" >> "$save_log"
              echo "$debug" > $save_error
              exit
        fi

        # Taille nouveau dump
        size_mysql=`du -hs "$save_root/$mysql_site.sql" | awk '{ print $1 }'`

        # Log le dump
        echo "`date +%d/%m/%y-%H:%M:%S` : Sauvegarde de la base $mysql_site [$size_mysql]" >> "$save_log"

done

# Permissions sur dumps
cd $save_root
chmod 400 *


Pensez à changer le mot de passe root de la base au début du script. Il est conseillé néanmoins de créer un utilisateur dédié à cette tache.

Pensez à ajouter les droits d'exécution :
chmod 754 /root/scripts/save_mysql.sh

Le fichier save_mysql.list


Dans ce fichier, on liste les bases à sauvegarder. Un nom de base par ligne, avec un retour chariot pour séparer.

On crée le fichier :
vim /root/scripts/save_mysql.list

Exemple de fichier :
mysql
test
blog


Mise en place du cron


On édite la crontab :
crontab -e

On ajoute cette ligne :
# Sauvegarde MySQL
30 0 * * * bash /root/scripts/save_mysql.sh > /dev/null


Rotation des logs


Le script écrit un événement dans un journal à chaque sauvegarde. On va utiliser logrotate.d pour s'assurer que ces journaux gardent une taille raisonnable.

On crée un fichier de conf pour nos journaux :
vim /etc/logrotate.d/save_mysql

Avec le contenu suivant :
/var/log/save_mysql.log {
monthly
missingok
rotate 6
}


On recharge la configuration du démon logrotate :
logrotate -f /etc/logrotate.conf

Conclusion


Deux scripts bash, deux cron et on a une sauvegarde facile a restaurer de ses sites oueb. Il ne faut pas oublier de sauvegarder ensuite le dossier /root/save sur un support externe. Ces sauvegardes servent principalement à remonter un site web qui a été attaqué ou cassé par une modification. En cas de crash serveur, il est souvent plus simple de restaurer la machine entière.

Pour restaurer un site, lancez les commandes suivantes :
cp -p /root/save/www/<site_web>.tar.gz.<chiffre> /tmp/
cp -p /root/save/mysql/<site_web>.sql.<chiffre> /tmp/
tar zxvf /root/save/www/<site_web>.tar.gz.<chiffre>
cd /var/www
rm -rf <site_web>
mv /tmp/<site_web> .
cd /tmp/
mysql -u root -p <site_web> < <site_web>.sql.<chiffre>
rm /tmp/<site_web>.tar.gz.<chiffre>
rm /tmp/<site_web>.sql.<chiffre>


Références


Image Tuxs Siamois : http://tux.crystalxp.net/fr.id.2091-masterlud-tuxs-siamois.html