🗃️ Sauvegarde au branchement avec Systemd et BTRFS
Introduction
Dans l’article sur la sauvegarde automatique dès le branchement pour mes photos, j’indiquais comment j’avais réussi à gérer des sauvegardes automatiques au branchement pour mes photos. Cela est très bien, mais pourquoi pas ne pas étendre la mécanique pour la sauvegarde de fichiers ? La mécanique de base s’avère très proche, mais complété pas les fonctionnalités du système de fichier btrfs, cela donne un système de sauvegarde très flexible.
Contrairement aux sauvegardes de photos qui allait de la carte SD au NAS, l’idée là est inverse. On veut pouvoir connecter un disque et le système va écrire de lui-même sur ce disque la nouvelle sauvegarde.
Principe
Enregistrement automatique de sauvegarde d’un disque dur
Le principe est assez simple :
- Je branche un disque dur à mon NAS, le disque dur contient une partition avec le label “backup”
- Le disque dur est détecté et la partition “backup” est monté par le système.
- Le système démarre la sauvegarde, ici avec l’aide de btrbk et de systemd.
- Le système démonte la partition du disque automatiquement lorsque la sauvegarde est terminé.
Ajoutons un peu de BTRFS par-dessus
Le système de fichiers btrfs par ses fonctionnalités : gestion de sous-volume et instantané (snapshot), nous permet de figer l’image d’un sous-volume à un instant T directement sans copie. Cette image peut être en lecture seule. C’est un instantané.
L’intérêt ? Il est possible de stocker nos données à sauvegarder dans un sous-volume btrfs, qui sera alors sauvegardé sous la forme d’un instantané en lecture seule qu’on pourra ainsi transmettre sur un autre disque sans risque de corruption de données lié à des modifications durant la sauvegarde.
Les instantanés permettent aussi localement un mécanisme de sauvegarde simple permettant de “revenir dans le temps”. L’autre avantage des instantanés btrfs est qu’ils permettent d’utiliser le mécanisme send-receive qui permet de copier efficacement des sous-volumes (et donc des instantanés) d’un volume btrfs à un autre, potentiellement d’une façon incrémentale en utilisant les sous-volumes déjà transmis.
Donc le principe est le suivant :
- On met en route une sauvegarde.
- Le système crée un instantané sur le volume source.
- Le système copie cet instantané sur le disque de sauvegarde grâce au mécanisme
send-receive. il tire parti des instantanés déjà transmis pour ne pas tout retransmettre.
Les limitations de la mécanique sont les suivantes :
send-receivene gère pas les coupures de réseau, donc pas très efficace par le réseau, car il devra renvoyer tout l’instantané en cas d’interruption.- il ne faut pas abuser des instantanés, l’impact de beaucoup d’instantanés sur l’espace disque et les performances peuvent être important, il faut donc faire le ménage.
La pratique
Prérequis : utiliser btrfs et les sous-volumes
Pour pouvoir utiliser la fonctionnalité, il faut que le disque à sauvegarder utilise btrfs, et il est conseillé de créer des sous-volumes pour définir les différents traitements.
Considérons que l’on veut sauvegarder la partition /home seulement, il faut donc un sous-volume sur home :
Imaginons qu’on a cette arborescence :
/
├── srv : point de montage direct du volume btrfs (voir fstab)
│ └── @home : emplacement du sous-volume `@home` dans le sous-volume par défault de btrfs.
└─ home : On va monter @home ici (voir fstab)
On aurait ainsi des paramètres de montage dans le fichier /etc/fstab similaire à ça :
UUID=6875f33c-4fed-4f43-bf34-4af0b2412ca4 /srv auto defaults 0 0
UUID=6875f33c-4fed-4f43-bf34-4af0b2412ca4 /home auto subvol=@home 0 0
Vous noterez que l’UUID est le même, car on pointe littéralement sur la même partition.
Quant au sous-volume @home, il a pu être créé tout simplement avec cette commande (volume /srv monté)
sudo btrfs subvolume create /srv/@home
Pour ce qui est des instantanés, nous les placerons dans le dossier snapshots à la raçine de /srv et les sauvegardes
seront dans le dossier snapshots à la racine de /mnt/backup.
Je ne rentrerai pas plus dans le détail de la mécanique de btrfs, il existe une littérature importante à ce sujet.
Créer une règle Udev
Comme précédemment, il nous faut passer par udev pour gérer l’aspect montage automatique.
99-automount_backup_drive.rules
# Automount usb backup drive on insertion
ACTION!="remove", SUBSYSTEM=="block", ENV{ID_PART_ENTRY_NAME}=="backup", ENV{SYSTEMD_WANTS}+="backup.service"
Ce qui signifie :
- Si une action provenant d’un périphérique de système block d’un périphérique donc le label de partition est
backupactive un évènement qui n’est pas “remove”, alors, on demande à activer l’unité systemdbackup.service. On reviendra plus en détail ensuite sur pourquoibackup.serviceet pasmnt-backupt.mount
📝 Note : l’action est sur les périphériques de type block, ce qui signifie que c’est agnostique du moyen de communication utilisé : USB, SATA, NVME, Réseau.
Du moment que le système voit une partition labellisée backup, il tentera de la monter.
Point de montage
Comme précédemment, il nous faut un fichier .mount.
/etc/systemd/system/mnt-backup.mount
[Unit]
Description=Mount Backup
BindsTo=btrbk_resume.service
[Mount]
What=/dev/disk/by-partlabel/backup
Where=/mnt/backup
ReadWriteOnly=true
Type=btrfs
Avec ce point de montage, systemd sait maintenant, qu’en cas d’activation de mnt-backup.mount:
- qu’il doit monter la partition de label
backupdans/mnt/backup. - qu’il doit la monter en lecture/écriture (sinon on va avoir des soucis) !
- qu’il doit démarrer
btrbk_resume.service. Je reviens dessus après. - qu’il doit la monter en btrfs. Il s’agit là d’une vérification supplémentaire, car on va utiliser des outils spécifiques à btrfs. si l’on branche un disque de sauvegarde en ext4, il faut mieux que le processus s’arrête au plus tôt.
Btrbk
Pour gérer efficacement la sauvegarde après quelques hésitations et un script perso, j’ai finalement décidé d’utiliser btrbk.
btrbk est un outil en perl pour gérer les sauvegarde via btrfs, notamment avec la méthode “création d’instantanés puis envois sur disque via btrfs send/receive”.
Il a beaucoup d’avantages :
- paquet debian.
- gère correctement l’incrémental.
- gére toute la mécanique de rotation des sauvegardes.
En bref, il nous simplifie la vie pour utiliser les fonctionnalités de btrfs dans notre cas.
Il faut néanmoins un peu de temps pour l’apprivoiser les règles de conservations sont ainsi assez compliqué à comprendre. Je vous laisse chercher dans la documentation.
Dans notre cas simple, on aura donc une configuration de ce style :
transaction_log /var/log/btrbk.log
# les snapshot du volume src et targets seront placé dans le dossier snapshot à la racine
snapshot_dir snapshots
# règle de conservation des snapshots
snapshot_preserve_min 2d
snapshot_preserve 14d
# règle de conservation des sauvegardes sur le disque de sauvegarde
target_preserve_min no
target_preserve 20d 10w *m
# on définis ici qu'on fait des snapshot pour le sous volume @home de /srv
# et qu'on les copie en mode send-receive vers /mnt/backup
volume /srv
target send-receive /mnt/backup
subvolume @home
On pourrait s’arrêter là et modifier le système pour que backup.service lance le btrbk.service du paquet debian et ça fonctionnerait
pour la sauvegarde à chaud. Mais mon souhait et d’avoir un snapshot journalier dans tous les cas et éventuellement une sauvegarde si le disque
de sauvegarde est branché.
Snapshot régulier et sauvegarde si possible
Pour réaliser cette mécanique d’instantanés réguliers en plus de la sauvegarde à chaud, il faut jouer un peu avec systemd.
Btrbk, permet de séparer la transmission de sauvegarde de la réalisation de snapshot.
Pour ce faire, au lieu de faire btrbk run on peut utiliser btrbk snapshot -pet btrbk resume.
Avec quelques override systemd, on peut arriver au bon résultat :
- On surcharge
btrbk.servicepour ne faire que des instantanés et ne rien supprimer(-p):
# /lib/systemd/system/btrbk.service
[Unit]
Description=btrbk backup
Documentation=man:btrbk(1)
[Service]
Type=oneshot
ExecStart=/usr/sbin/btrbk run
# /etc/systemd/system/btrbk.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/sbin/btrbk snapshot -p
- On ajoute
btrbk_resume.servicepour faire la transmission des sauvegardes:
# /etc/systemd/system/btrbk_resume.service
[Unit]
After=btrbk.service
# 📝 note: on demande implicitement `mnt-backup.mount`.
RequiresMountsFor=/mnt/backup
RefuseManualStart=true
[Service]
Type=oneshot
# 📝 note: On n'a pas mis de `-p` ici,
# la rotation des instanées et sauvegardes se fera à la fin de la transmission des sauvegardes.
ExecStart=/usr/sbin/btrbk resume
- On crée
backup.servicenotre point d’entrée pour tous processus de sauvegarde qui fera :- la création des instantanés
- tentera d’exécuter la sauvegardes
# /etc/systemd/system/backup.service
[Unit]
After=btrbk.service btrbk_resume.service
Requires=btrbk.service btrbk_resume.service
[Service]
# 📝 Note: Ce service est un peu subtil, car on tire profit des spécificités des services oneshot.
# Ce service lancera les 2 services btrbk et restera en activating, jusqu'à qu'ils s'arrêtent et donc sera enfin `inactivate`.
# il n'est à ma connaissance pas possible de faire la même chose avec des `.target` qui seront en `activate` à la fin du processus.
Type=oneshot
# Fausse commande car c'est obligatoire pour les service
ExecStart=/bin/true
- On surcharge aussi
btrbk.timerdes paquets pour démarrer notre point d’entrée :
# /lib/systemd/system/btrbk.timer
[Unit]
Description=btrbk daily backup
[Timer]
OnCalendar=daily
AccuracySec=10min
Persistent=true
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/btrbk.timer.d/override.conf
[Timer]
Unit=backup.service
📝 Note : Il ne faut pas oublier d’activer ce service sinon vous n’aurez pas les sauvegardes régulières
Récapitulatif Scénario
On à donc les 3 scénarios suivants :
1. Branchement à chaud
- On branche le disque de sauvegarde.
- udev demande à démarrer
backup.service backup.servicedémarre d’abordbtbrk.servicecarbtrbk_resume.servicedemande à être exécuté après.- les snapshots sont créés,
btrbk_resume.serviceest démarré. btrbk_resume.servicemontemnt-backup.service. Il fait la sauvegarde.- À la fin de
btrbk_resume,mnt-backup.serviceest démonté
2. Sauvegarde régulière
btrbk.timerdémarrebackup.servicebackup.servicedémarre d’abordbtbrk.servicecarbtrbk_resume.servicedemande à être exécuté après.- les snapshots sont créés,
btrbk_resume.serviceest démarré. btrbk_resume.servicemontemnt-backup.service. Il fait la sauvegarde.- À la fin de
btrbk_resume,mnt-backup.serviceest démonté.
3. Sauvegarde régulière sans disque branché
btrbk.timerdémarrebackup.servicebackup.servicedémarre d’abordbtbrk.servicecarbtrbk_resume.servicedemande à être exécuté après.- les snapshots sont créés,
btrbk_resume.serviceest démarré. btrbk_resume.servicemontemnt-backup.servicemaismnt-backup.servicene peut être démarré. arrêt du service.backup.servicese stoppe, mais les instantanés ont bien été créés.
Conclusion
Btrfs n’est pas si facile à prendre en main, mais les concepts bien compris et à l’aide de systemd, on peut créer des mécanismes de sauvegardes puissants.