🗃️ Sauvegarde au branchement avec Systemd et BTRFS

| ~ 8 mins | 1631 mots

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 :

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 :

Les limitations de la mécanique sont les suivantes :

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 :

📝 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:

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 :

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 :

# /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
# /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
# /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
# /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

2. Sauvegarde régulière

3. Sauvegarde régulière sans disque branché

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.