--- comments_url: https://com.richard-dern.fr/post/526 date: '2012-02-19 00:14:00' dossier: - Créer son propre Cloud tags: - Backup - rsync - Sauvegarde - Serveur - Cloud - Sécurité - Plus - NAS - Libre - GNU/Linux - Espace - Documents - Ingnu - Source - Droit - État title: Sauvegarder et restaurer son serveur weather: humidity: 96 illuminance: 0.0 precipitations: true pressure: 1012.4 source: - open-meteo temperature: 3.6 wind_direction: 226 wind_speed: 16.5 weight: 14 --- Maintenant que nous disposons d'un serveur pratiquement complet pour [avoir son propre cloud](/interets/informatique/2012/02/05/creer-son-propre-cloud-introduction/), que nous avons [renforcé sa sécurité](/interets/informatique/2012/02/13/renforcer-la-securite-de-son-serveur/) et que nous avons vu comment [opérer un monitoring basique](/interets/informatique/2012/02/13/trucs-et-astuces-pour-son-serveur-prive/), nous allons voir comment sauvegarder et restaurer son serveur. Nous allons faire appel au fameux [rsync](http://rsync.samba.org/), pour ne pas encombrer notre serveur de solutions aussi lourdes à installer qu'à configurer, d'autant que je vous propose un script qui va simplifier tout ça, et même plus encore... Vous pouvez placer ce script n'importe où : sur le serveur qui héberge vos services, sur votre propre machine, sur un serveur séparé, un NAS qui supporte rsync et l'authentification par clé, etc. Il suffit de configurer un peu le script pour que la sauvegarde se fasse presque toute seule. Elle est pas belle la vie Libre ? ## Choisir son dispositif de stockage Communément, on sauvegarde sur une partition séparée, de préférence sur un disque séparé, peut être dans une machine séparée, ultimement dans un local séparé. Il n'y a véritablement que deux prérogatives à l'utilisation du script que je vous propose : la machine doit pouvoir utiliser rsync, et l'authentification par clé. Toute distribution GNU/Linux en est capable, vous ne devriez donc pas avoir trop de mal à disposer d'une plateforme adéquate. Évidemment, une autre prérogative s'impose d'elle-même : l'espace disque disponible. À titre d'exemple, sachez que j'utilise ce script pour sauvegarder deux serveurs et les documents d'une dizaine de personnes. Le premier, celui qui héberge ingnu.fr, héberge aussi tout un tas d'autres sites, un serveur mail d'une dizaine d'utilisateurs, les bases de données, etc. Au total, un snapshot des deux machines occupe à l'heure actuelle 7.6Go. Mon script vous permet de disposer de deux types de sauvegardes : un instantané (appelé _snapshot_) et un instantané de chaque heure de chaque jour. Le premier instantané est en réalité vieux au maximum d'une minute, tandis que les autres instantanés vous permettent de remonter dans le temps par tranche d'une heure, sur un nombre de jours que vous pouvez déterminer. Au final, sur cinq jours (qui est le réglage par défaut), l'espace disque total occupé par les sauvegardes n'est que doublé : il est actuellement de 13Go. Seuls les fichiers modifiés occupent la différence entre l'espace total et l'espace initial. Vous n'avez donc pas besoin d'un disque de plusieurs téraoctets, à moins que vous ne vouliez sauvegarder aussi des fichiers comme des vidéos ou des distributions GNU/Linux en quantité industrielle... ## Créer la clé d'authentification La communication entre le serveur et la machine qui exécute la sauvegarde (qui peut aussi être la même machine donc) se fait via rsync, donc ssh. La connexion est par conséquent chiffrée, et les transferts sont intelligents : la bande passante est minimisée. Pour pouvoir tout sauvegarder d'un coup, on va donc sauvegarder en tant que root. Si vous sauvegardez depuis une machine distante, créez une clé avec la commande suivante : ```bash ssh-keygen -t rsa -f ~/.ssh/id_rsa_backup ``` Que vous enverrez au serveur via la commande : ```bash ssh-copy-id -i ~/.ssh/id_rsa_backup.pub root@exemple.fr ``` Vous devrez saisir le mot de passe root de la machine distante. Testez enfin l'accès : ```bash ssh root@exemple.fr ``` Vous devriez être connecté sans avoir eu besoin de saisir votre mot de passe. ## Créer le script de sauvegarde Créez un répertoire dédié au script et à sa configuration : ```bash mkdir -p /scripts/backup cd /scripts/backup mkdir conf.d include.d exclude.d ``` Pour respecter le [Standard de Hiérarchie des Systèmes de Fichiers](http://www.pathname.com/fhs/), nous devrions placer le script dans _/usr/bin_, et la configuration dans _/etc/backup_. Rien ne vous empêche de procéder de la sorte, à condition de modifier les chemins d'accès dans le script que nous allons voir tout de suite : ```bash nano backup ``` ```text #!/bin/bash ############################################################################### # Variables # ############################################################################### my_pid=$$ my_dir=$(dirname $0) pid_file="/var/run/backup.pid" conf_dir="$my_dir/conf.d" include_dir="$my_dir/include.d" exclude_dir="$my_dir/exclude.d" log_dir="/var/log/backup" log_file="$log_dir/backup" rsync_args="--archive --recursive --delete --delete-excluded" backup_root="/mnt/backup" rsync=$(which rsync) current_date=$(date "+%Y-%m-%d") current_minute=$(date +%M) current_hour=$(date +%H) yesterday_date=$(date --date "yesterday" "+%Y-%m-%d") max_age=5 oldest_backup=$(date --date "$max_age days ago" "+%Y-%m-%d/%H") oldest_log=$(date --date "$max_age days ago" "+%Y-%m-%d") ############################################################################### # Fonctions # ############################################################################### function check_pid() { if [ -f "$pid_file" ] then old_pid=$(cat "$pid_file") old_pid_exists=$(ps -p $old_pid | grep -v TTY | awk -F " " '{print $1}') if [ "$old_pid_exists" != "" ] then exit fi fi } function log() { if [ "$2" == "true" -o "$2" == "TRUE" -o "$2" == "yes" -o "$2" == "YES" -o "$2" == "1" ] then log_date=$(date "+%d/%m/%Y %H:%M:%S") echo "[$log_date] $1" >> "$log_file" else echo "$1" >> "$log_file" fi } function check_dir() { if [ ! -d "$1" ] then mkdir -p "$1" fi } ############################################################################### # Lancement de la procédure # ############################################################################### ##### Vérification des journaux ############################################### check_dir "$log_dir" if [ ! -f "$log_file" ] then touch "$log_file" fi ##### Déplacement des anciens journaux ######################################## if [ "$current_hour" == "00" -a "$current_minute" == "00" ] then mv "$log_file" "$log_file-$yesterday_date" fi ##### Démarrage de la procédure ############################################### check_pid echo $my_pid > "$pid_file" start_date=$(date "+%d/%m/%Y %H:%M:%S") log " " log "###############################################################################" log "# Début de la procédure : $start_date #" log "###############################################################################" log " " list=$(ls "$conf_dir") for host in $list; do conf_file="$conf_dir/$host" source "$conf_file" host_rsync_args="$rsync_args" host_snapshot="$backup_root/$host/snapshot" if [ "$ENABLED" != "yes" -a "$ENABLED" != "YES" -a "$ENABLED" != "true" -a "$ENABLED" != "TRUE" -a "$ENABLED" != "1" ] then log "**** L'hôte $host est désactivé ****" log " " continue fi log "**** Traitement de l'hôte $host ****" log " " source="$SOURCE" target="$host_snapshot" check_dir "$target" log "Source : $SOURCE" true log "Destination : $target" true if [ -f "$include_dir/$host" ] then log "Un fichier d'inclusion existe : $include_dir/$host" true host_rsync_args="$host_rsync_args --files-from=$include_dir/$host" fi if [ -f "$exclude_dir/$host" ] then log "Un fichier d'exclusion existe : $exclude_dir/$host" true host_rsync_args="$host_rsync_args --exclude-from=$exclude_dir/$host" fi log "Commande : $rsync $host_rsync_args $source $target" true log "Démarrage de la création ou mise à jour de l'instantané..." true $rsync $host_rsync_args $source $target log "Création ou mise à jour de l'instantané terminée" true ##### Sauvegarde périodique ############################################### if [ "$current_minute" == "00" ] then log " " log "Sauvegarde périodique" true source="$host_snapshot/" target="$backup_root/$host/$current_date/$current_hour/" check_dir "$target" log "Source : $source" true log "Destination : $target" true log "Commande : $rsync $host_rsync_args --links-dest=$source $SOURCE $target" true log "Démarrage de la sauvegarde périodique..." true $rsync $host_rsync_args --link-dest="$source" "$SOURCE" "$target" log "Sauvegarde périodique terminée" true fi ##### Suppression des anciennes sauvegardes ############################### log " " log "Recherche d'une ancienne sauvegarde ($backup_root/$host/$oldest_backup)..." true if [ -d "$backup_root/$host/$oldest_backup" ] then log "Une ancienne sauvegarde existe : $backup_root/$host/$oldest_backup" true log "Suppression de la sauvegarde la plus ancienne..." true rm -rf "$backup_root/$host/$oldest_backup" log "Suppression terminée" true else log "Il n'existe pas d'ancienne sauvegarde" true fi log " " done end_date=$(date "+%d/%m/%Y %H:%M:%S") log "###############################################################################" log "# Fin de la procédure : $end_date #" log "###############################################################################" log " " rm -f "$pid_file" ``` Important : Veuillez noter que ce script me convient bien : rien ne vous empêche de faire des ajustements pour qu'il vous corresponde à VOUS ! Notice : Modifiez impérativement la variable _backup_root_ ! Quelques explications. Si vous voulez respecter les FHS, modifiez les variables _conf_dir_, _include_dir_ et _exclude_dir_. Vous pouvez également modifier la variable _max_age_, dont la valeur (un entier) représente le nombre de jours à conserver. Ensuite, quelques fonctions classiques : recherche d'un pid existant (pour ne pas lancer deux sauvegardes simultanées), journalisation, et recherche et création de répertoire. La procédure commence alors par la vérification de l'existence des journaux et leur archivage. Puis, le script récupère la liste des fichiers contenus dans _conf.d_, lit chacun d'entre eux pour obtenir deux directives de configuration (_$ENABLED_ true/false et _$SOURCE_ représentant la source à sauvegarder), recherche s'il existe un fichier d'inclusion (il vaut mieux sinon vous sauvegardez toute la partition, se trouve dans le répertoire _include.d_) et un fichier d'exclusion (facultatif, se trouve dans _exclude.d_). Chaque minute (via une tâche cron), le script se lance, et créé ou met à jour l'instantané (le contenu du répertoire _$backup_root/$host/snapshot_). Chaque heure, ce snapshot est créé dans un répertoire distinct, en faisant appel aux hard links : le fichier n'est pas copié, mais un hard link est créé. Autrement dit, le fichier est accessible depuis deux adresses différentes. Même si la source (dans le _snapshot_) est supprimée, le fichier existe toujours grâce au hard link. Dans notre cas, cela nous permet d'économiser de l'espace disque. La conséquence de cette façon de procéder est de disposer à tout instant d'une image complète du système de fichiers sauvegardé. Et le but, c'est de le restaurer en une ligne de commande (une simple copie suffira). Enfin, on supprime les instantanés les plus anciens. Attribuez le droit d'exécution au script : ```bash chmod +x backup ``` ## Configuration Vous disposez donc d'un répertoire _conf.d, include.d_ et _exclude.d_. Le premier contiendra un fichier par machine à sauvegarder. Vous pouvez nommer ce fichier comme bon vous semble. Une seule règle à respecter : ce nom devra être le même que celui du fichier d'inclusions et du fichier d'exclusions. Créons la configuration de notre cloud : ```bash nano conf.d/my_cloud ``` ```text ENABLED=true SOURCE=/ ``` Si le serveur est une machine différente, on utilisera la notation suivante : ```text SOURCE="root@exemple.fr:/" ``` Avec cette configuration, on va sauvegarder l'ensemble de la machine concernée (ou plus exactement, l'intégralité de la partition _/_). Ce n'est probablement pas ce que vous voulez, alors nous devons créer un fichier d'inclusions : ```bash nano include.d/my_cloud ``` À peu de chose près, vous devriez mettre ceci : ```text /etc/amavis /etc/apache2 /etc/bind /etc/clamav /etc/dovecot /etc/php5 /etc/postfix /etc/prosody /etc/rc.local /etc/spamassassin /etc/ssl /opt /scripts /var/vmail /var/www /var/lib/mysql ``` Avec ce contenu, vous devriez sauvegarder l'intégralité du serveur que nous avons configuré jusqu'à maintenant. Pour éviter de sauvegarder des choses inutiles, créons un fichier d'exclusions : ```bash nano exclude.d/my_cloud ``` ```text log ``` ## Lancement On édite la crontab de l'utilisateur qui procède à la sauvegarde : ```bash crontab -e ``` ```text * * * * * /scripts/backup/backup ``` Et on vérifie le contenu du journal : ```bash tail -f /var/log/backup/backup ``` ## Restauration Pour restaurer votre serveur, il suffit de copier vers lui tout ou partie de la dernière sauvegarde valide, via _scp_, tout simplement. Inutile de tergiverser : grâce à ce système de sauvegarde, il suffit d'une simple copie sécurisée pour restaurer le dernier état valide du serveur; contrairement à d'autres solutions de sauvegarde, pas besoin d'installer un agent sur la machine distante, une simple connexion ssh suffit ! ## Conclusion Nous voici arrivés à la fin de la série d'articles sur la création de son cloud personnel. Plus qu'un article à publier d'ici quelques minutes, alors à tout de suite !