Linux : faire une sauvegarde incrémentale avec rsync

Publié le 14 April 2023

C’est peu connu, mais rsync permets de faire des sauvegardes incrémentales plutôt efficaces. L’option --link-dest permets de garder plusieurs version d’une même arborescence, mais en créant des liens en dur (hardlink) entre les fichiers qui n’ont pas changé.

L’expérience

Pour l’exemple, je créé un dossier www avec trois fichiers :

ls -hl www/
total 12K
-rw-r--r-- 1 root root 4 Apr 14 10:25 fichier1.txt
-rw-r--r-- 1 root root 4 Apr 14 10:25 fichier2.txt
-rw-r--r-- 1 root root 5 Apr 14 10:25 fichier3.txt

Si je veux en faire une sauvegarde avec rsync, je vais faire :

rsync -arv www/ sauvegarde1/
sending incremental file list
created directory sauvegarde
./
fichier1.txt
fichier2.txt
fichier3.txt

sent 272 bytes  received 109 bytes  762.00 bytes/sec
total size is 13  speedup is 0.03

Et dans mon dossier sauvegarde1, j’ai bien :

ls -hl sauvegarde1/
total 12K
-rw-r--r-- 1 root root 4 Apr 14 10:25 fichier1.txt
-rw-r--r-- 1 root root 4 Apr 14 10:25 fichier2.txt
-rw-r--r-- 1 root root 5 Apr 14 10:25 fichier3.txt

Maintenant dans www, je vais modifier fichier1.txt, supprimer fichier3.txt et créer fichier4.txt :

ls -hl www/
total 8.0K
-rw-r--r-- 1 root root 11 Apr 14 10:29 fichier1.txt
-rw-r--r-- 1 root root  4 Apr 14 10:25 fichier2.txt
-rw-r--r-- 1 root root  7 Apr 14 10:30 fichier4.txt

Je fais une nouvelle sauvegarde dans sauvegarde2 :

rsync -arv www/ sauvegarde2/
sending incremental file list
created directory sauvegarde2
./
fichier1.txt
fichier2.txt
fichier4.txt

sent 279 bytes  received 110 bytes  778.00 bytes/sec
total size is 22  speedup is 0.06

Je retrouve le contenu attendu dans sauvegarde2 :

ls -hl sauvegarde2/
total 12K
-rw-r--r-- 1 root root 11 Apr 14 10:29 fichier1.txt
-rw-r--r-- 1 root root  4 Apr 14 10:25 fichier2.txt
-rw-r--r-- 1 root root  7 Apr 14 10:30 fichier4.txt

Le problème

Ici je me retrouve avec trois dossier : la source dans www, et deux sauvegardes dans sauvegarde1 et sauvegarde2. C’est ce qui est attendu, mais ce n’est pas optimal. Plusieurs fichiers sont présents plusieurs fois :

On voit ici qu’il y a plusieurs doublons, et qu’il y a moyen de gagner de la place. On pourrait ne pas re-sauvegarder les fichiers qui n’ont pas changé. Mais pour des facilités d’utilisation j’aimerais bien que tous les fichiers soient présents dans tous les répertoires. Ça pourrait me permettre de récupérer rapidement un dossier complet en cas de besoin.

La solution : les liens en dur

Sous Linux, il y a deux façons de faire des liens :

L’utilisation de rsync

Comme dit plus haut, rsync permets de gérer nativement plusieurs version d’un même dossier grâce aux liens en dur. Je vais reprendre mon exemple précédent.

Je repart sur les trois fichiers de base :

ls -hl www/
total 12K
-rw-r--r-- 1 root root 4 Apr 14 10:51 fichier1.txt
-rw-r--r-- 1 root root 4 Apr 14 10:51 fichier2.txt
-rw-r--r-- 1 root root 5 Apr 14 10:51 fichier3.txt

Maintenant je fais une première sauvegarde, comme la fois précédente :

rsync -arv www/ sauvegarde1/
sending incremental file list
created directory sauvegarde1
./
fichier1.txt
fichier2.txt
fichier3.txt

sent 271 bytes  received 110 bytes  762.00 bytes/sec
total size is 13  speedup is 0.03

Dans www je modifie fichier1.txt, je supprimer fichier3.txt et je créer fichier4.txt :

ls -hl www/
total 12K
-rw-r--r-- 1 root root 11 Apr 14 10:53 fichier1.txt
-rw-r--r-- 1 root root  4 Apr 14 10:51 fichier2.txt
-rw-r--r-- 1 root root  7 Apr 14 10:54 fichier4.txt

Maintenant, pour la deuxième sauvegarde, je vais utiliser l’option --link-dest de rsync :

rsync -arv --link-dest /root/test_rsync/sauvegarde1/ www/ sauvegarde2/
sending incremental file list
created directory sauvegarde2
./
fichier1.txt
fichier4.txt

sent 236 bytes  received 92 bytes  656.00 bytes/sec
total size is 22  speedup is 0.07

Notez que l’option --link-dest demande absolument un chemin absolu (depuis la racine).

D’après ce qui est affiché, seuls fichier1.txt et fichier4.txt ont été sauvegardés. Mais si je regarde le contenu du répertoire, fichier2.txt est bien présent :

ls -hl sauvegarde2
total 12K
-rw-r--r-- 1 root root 11 Apr 14 10:53 fichier1.txt
-rw-r--r-- 2 root root  4 Apr 14 10:51 fichier2.txt
-rw-r--r-- 1 root root  7 Apr 14 10:54 fichier4.txt

Notez le 2 juste après le -rw-r--r--. Ça veut dire que ce fichier possède deux entrées dans la table des inodes. rsync a détecté que le fichier était identique entre sauvegarde1 et sauvegarde2 et ne l’a pas re-créé, il s’est contenté de faire un lien en dur. Par contre il a détecté que fichier1.txt avait été modifié, et l’a créé dans sauvegarde2. Et pareil pour fichier4.txt qui était nouveau.

Sur cet exemple ça ne casse pas des briques, mais sur une grosse sauvegarde ça permets d’économiser beaucoup de place (et beaucoup de bande passante si on passe par le réseau). Et ça marche avec plus de deux sauvegardes.

Il faut quand même faire attention à plusieurs points :

Comment je gère mes sauvegardes

Je dispose d’un NAS sur lequel je fais toutes mes sauvegardes. Il y a dessus un répertoire par équipement à sauvegarde, à l’intérieur duquel il y a un répertoire par date. Par exemple, pour les sauvegardes du serveur qui héberge ce site, ça donne ça :

palc@nas:/data/Sauvegardes$ ls -hl palc.fr/
total 0
drwxr-xr-x 1 palc palc 1,5K 31 août   2022 2022-08-31
drwxr-xr-x 1 palc palc 1,5K 15 déc.  08:12 2022-12-14
drwxr-xr-x 1 palc palc 1,4K 16 févr. 22:31 2023-02-16
drwxr-xr-x 1 palc palc 1,4K  9 mars  12:02 2023-03-09

Et voici l’espace occupé par les différentes sauvegardes :

pacl@nas:/data/Sauvegardes$ du -csh palc.fr/*
94G palc.fr/2022-08-31
19G palc.fr/2022-12-14
7,6G    palc.fr/2023-02-16
13G palc.fr/2023-03-09
132G    total

On voit bien que les les trois dernières sauvegardes sont beaucoup plus petites que la première, grâce à l’utilisation des liens en dur.

Maintenant voyons comment je procède pour mes sauvegardes.

La première sauvegarde se fait de manière traditionnelle :

rsync -avzz --delete --exclude="/bin/" --exclude="/boot/" --exclude="/dev/" --exclude="/lib/" --exclude="/mnt/" --exclude="/proc/" --exclude="/run/" --exclude="/sbin/" --exclude="/sys/" --exclude="/tmp/" --exclude="/usr/" --exclude="/var/cache/" --exclude="/var/lib/" --exclude="/var/log/" --exclude="/var/tmp/" palc.fr:/ /data/Sauvegardes/palc.fr/2022-08-31/

On notera que je préfère faire des exclude des répertoire inutiles plutôt qu’un include des répertoires utiles. C’est pour éviter d’oublier un répertoire important.

Les sauvegardes suivantes se font avec cette commande :

rsync -avzz --exclude="/bin/" --exclude="/boot/" --exclude="/dev/" --exclude="/lib/" --exclude="/mnt/" --exclude="/proc/" --exclude="/run/" --exclude="/sbin/" --exclude="/sys/" --exclude="/tmp/" --exclude="/usr/" --exclude="/var/cache/" --exclude="/var/lib/" --exclude="/var/log/" --exclude="/var/tmp/" --link-dest /data/Sauvegardes/palc.fr/`ls -1 /data/Sauvegardes/palc.fr/ | grep -v $(date +%Y-%m-%d) | tail -n 1`/ palc.fr:/ /data/Sauvegardes/palc.fr/`date +%Y-%m-%d`/

Vous noterez l’utilisation de date +%Y-%m-%d. Ça me permets de définir automatiquement la destination de la sauvegarde dans le dossier du jour.

Le ls -1 /data/Sauvegardes/palc.fr/ | grep -v $(date +%Y-%m-%d) | tail -n 1, utilisé pour l’option --link-dest, permets de trouver le dernier dossier de sauvegarde, en excluant celui du jour. De cette façon, si une sauvegarde a été interrompue en plein milieu, il est possible de relancer la commande, telle quelle.

Il y a deux points sur lesquels il faut faire attention :