URL: https://linuxfr.org/users/ar7/journaux/btrfs-et-opensuse-episode-2-snapper-et-grub2 Title: [Btrfs et openSUSE] Épisode 2 : snapper et GRUB2 Authors: AR7 Date: 2017年08月21日T20:18:13+02:00 License: CC By-SA Tags: btrfs, opensuse et grub2 Score: 35 ![Btrfs is hard](https://openclipart.org/image/300px/svg_to_png/170369/Hard-Disk.png) **« Btrfs et openSUSE »** est une série de journaux sur le système de fichiers Btrfs, basée sur ma propre expérience d'utilisateur d'openSUSE. Au menu : * des généralités sur Btrfs [☑](https://linuxfr.org/users/ar7/journaux/btrfs-et-opensuse-episode-0-l-ex-fs-du-futur) * des noms qui pètent : sous-volumes, _snapshots_, _rollbacks_ [☑](https://linuxfr.org/users/ar7/journaux/btrfs-et-opensuse-episode-1-sous-volumes-snapshots-et-rollbacks) * du snapper * du grub * de la mise à jour incrémentale * des quotas * de la maintenance * des trucs spécifiques à openSUSE * des tentatives désespérées pour rester applicable à d'autres distributions * des erreurs (pas taper) * des bugs * des cris * des larmes * et bien plus ! ou bien moins **Aujourd'hui, l'épisode 2 : snapper et GRUB2.** # snapper : un gestionnaire d'instantanés ## Aperçu [snapper](http://snapper.io/) est un outil, sous licence GPL-2.0, pour gérer des _snapshots_. Il a été créé par [Arvin Schnell](https://github.com/aschnell) de chez SUSE. Ses objectifs : - Prendre des instantanés automatiquement : * de façon périodique * avant et après chaque interaction avec des outils pouvant modifier le système. Des gestionnaires de paquets par exemple : il y a des greffons pour [zypper](http://snapper.io/manpages/snapper-zypp-plugin.html), [dnf](http://dnf-plugins-extras.readthedocs.io/en/latest/snapper.html), [pacman](https://wiki.archlinux.org/index.php/Snapper#Wrapping_pacman_transactions_in_snapshots), [apt](https://github.com/credativ/apt-snapper)... - Nettoyer les instantanés anciens automatiquement - Fournir des commandes simples pour : * prendre des instantanés manuellement * comparer des instantanés * faire des restaurations Il se veut indépendant du système de fichiers : par exemple, snapper serait capable de prendre des instantanés avec [LVM](https://lizards.opensuse.org/2012/07/25/snapper-lvm/) et ext4 – heu en fait non, [pas ext4](https://github.com/openSUSE/snapper/issues/331), [c'est une blague](https://en.opensuse.org/openSUSE:Next4)... Si aujourd'hui snapper est disponible sur la plupart des distributions GNU/Linux, sur openSUSE il fait partie intégrante du système. Il y a même [un module YaST](http://yast.opensuse.org/modules) permettant de réaliser certaines opérations avec une interface graphique. ![image](http://pix.toile-libre.org/upload/original/1503051188.png) (avec un style début année 2000 du meilleur goût ;-)) ## En pratique ### Lister les instantanés Pour lister les instantanés pris par snapper : ``` ~# snapper ls Type | # | Pre # | Date | Utilisateur | Nettoyer | Description | Données utilisateur -------+----+-------+---------------------------------+-------------+----------+--------------+-------------------- single | 0 | | | root | | current | single | 68 | | sam. 12 août 2017 20:56:53 CEST | root | number | | single | 69 | | mer. 16 août 2017 20:07:58 CEST | root | number | | single | 70 | | jeu. 17 août 2017 21:17:25 CEST | root | number | | pre | 71 | | ven. 18 août 2017 22:19:46 CEST | root | number | zypp(zypper) | important=no post | 72 | 71 | ven. 18 août 2017 22:20:32 CEST | root | number | | important=no ~# ``` snapper repère les instantanés en fouillant dans `/.snapshots/*/` à la recherche d'un fichier `info.xml`. Voici par exemple le contenu de `/.snapshots/70/info.xml` : ```xml single 70 2017年08月17日 19:17:25 number ``` Vous noterez la présence d'une colonne « type ». snapper définit en effet trois sortes d'instantanés : * _single_ : un instantané classique ; * _pre_ : un instantané pris avant l'utilisation d'un outil pouvant modifier le système (pris au démarrage du gestionnaire de paquets par exemple). * _post_ : un instantané pris après l'utilisation de cet outil. C'est une distinction propre à snapper, ça reste des snapshots Btrfs tout à fait similaires ! ### Créer et supprimer des instantanés Voici la syntaxe, très simple, pour la création d'un instantané : `snapper create [-c empty-pre-post|number|timeline]` L'option `-c` permet de dire à snapper que l'instantané doit être nettoyé. Sans cette option, le snapshot ne sera jamais supprimé automatiquement. Voir plus bas la partie [nettoyage](#nettoyage-automatique). La suppression est aussi très simple : `snapper delete [-s,--sync] ...|-` On peut spécifier un ou plusieurs instantanés à supprimer : `snapper delete 68`, `snapper delete 68 69 70`. On peut spécifier un intervalle : `snapper delete 68-70`. L'option `-s` permet d'attendre la fin de la suppression des données avant de rendre la main ; autrement, la suppression des données se fait de manière asynchrone. ### Comparer des instantanés Trois commandes disponibles : * `snapper status a..b` : affiche la liste des fichiers et dossiers qui ont été créés, modifiés ou supprimés entre les instantanés `a` et `b`. * `snapper diff a..b` : affiche le contenu des fichiers et dossiers qui ont été créés, modifiés ou supprimés entre les instantanés `a` et `b`. * `snapper xadiff a..b` : affiche les [attributs étendus](https://fr.wikipedia.org/wiki/Attributs_%C3%A9tendus) des fichiers et dossiers qui ont été... vous savez la suite. Attention, ça peut être très très lent. ### Restaurer des instantanés Pour le système (`/`) : ``` ~# snapper rollback 2 Création d'un instantané en lecture-seule du système actuel. (Instantané 4.) Création d'un instantané en lecture-écriture de l'instantané 2. (Instantané 5.) Définition du sous-volume par défaut sur l'instantané 5. # ``` (C'est l'équivalent [des commandes Btrfs qu'on a vues dans l'épisode précédent](https://linuxfr.org/users/ar7/journaux/btrfs-et-opensuse-episode-1-sous-volumes-snapshots-et-rollbacks#restauration-du-syst%C3%A8me).) Il est également possible de faire des « restaurations à chaud ». Les sous-volumes ne sont pas échangés, seulement les fichiers sont remplacés : `snapper undochange [fichiers]` ## Nettoyage automatique ![Autocleanup](http://comicsmedia.ign.com/comics/image/article/612/612190/toybox2005512-02_1115870266.jpg) snapper introduit trois stratégies de nettoyage : * _empty-pre-post_ : comparaison de chaque paire d'instantanés _pre_/_post_ ; si aucune différence, suppression de la paire. * _number_ : au delà d'un certains nombres de snapshots taggés _number_, les plus anciens commencent à être supprimés ; il n'y a que cette stratégie qui fasse vraiment sens pour des snapshots pris manuellement. * _timeline_ : c'est la même chose que _number_, mais réservé aux instantanés taggés _timeline_, c'est-à-dire pris périodiquement par snapper. Le nettoyage automatique des instantanés se fait grâce à une tâche cron (`/etc/cron.daily/suse.de-snapper`) qui appelle les commandes : ``` snapper cleanup empty-pre-post snapper cleanup number snapper cleanup timeline ``` ## Gare à la configuration ! Sur openSUSE, une configuration snapper est créée et activée à l'installation. Sinon, pour créer une configuration, la syntaxe est : `snapper [-c nom_config] ` Par exemple : ```sh ~# snapper create-config / # configuration par défaut "root" Échec de la création de la configuration (subvolume already covered). ~# snapper --config opt create-config /opt # configuration pour un autre sous-volume ~# snapper list-configs Configuration | Sous-volume --------------+------------ opt | /opt root | / ~# ``` Sur openSUSE, la configuration de snapper a causé bien des déboires aux utilisateurs. ![Danger](https://media.tenor.com/images/77871ab7677f2d459fde3b07e5f28914/tenor.gif) ```sh ~# snapper --config opt get-config Clé | Valeur -----------------------+------- ALLOW_GROUPS | ALLOW_USERS | BACKGROUND_COMPARISON | yes # "yes" cause des crashs du démon snapper en cas de gros diffs # entre 2 snapshots (typiquement après une grosse màj) EMPTY_PRE_POST_CLEANUP | yes EMPTY_PRE_POST_MIN_AGE | 1800 FSTYPE | btrfs NUMBER_CLEANUP | yes NUMBER_LIMIT | 50 # 50 snapshots associés à l'algo de nettoyage "number" NUMBER_LIMIT_IMPORTANT | 10 # +10 snapshots avec l'attribut "important" à conserver NUMBER_MIN_AGE | 1800 QGROUP | SPACE_LIMIT | 0.5 SUBVOLUME | /opt SYNC_ACL | no TIMELINE_CLEANUP | yes TIMELINE_CREATE | yes # un nouveau snapshot toutes les heures TIMELINE_LIMIT_DAILY | 10 # garder 1 snapshot par jour sur 10 jours différents max TIMELINE_LIMIT_HOURLY | 10 # garder 1 snapshot par heure sur 10 heures différents max TIMELINE_LIMIT_MONTHLY | 10 # garder 1 snapshot par mois sur 10 mois différents max TIMELINE_LIMIT_WEEKLY | 0 TIMELINE_LIMIT_YEARLY | 10 # ... TIMELINE_MIN_AGE | 1800 ~# ``` Pour un peu qu'on soit joueur (installations/désinstallations de logiciels fréquentes), les instantanés pouvaient très vite s'accumuler avant que la tâche cron de nettoyage ne se déclenche – avec des réglages beaucoup trop laxistes. Et l'espace disque libre de se réduire... jusqu'à rendre le système inutilisable ou presque. Surtout qu'il était et qu'il est toujours assez fréquent d'avoir de « petites » partitions racines et une partition `/home` séparée beaucoup plus grande ! Ce phénomène de saturation de la partition racine par les instantanés a causé beaucoup de tort – à tort – à Btrfs et explique en grande partie, à mon avis, son rejet par bien des utilisateurs d'openSUSE. Depuis : * la taille de la partition racine proposée à l'installation est maintenant de 40 Go ; si inférieure, snapper est désactivé ; * l'introduction du sous-volume `/var/cache` a permis de diminuer sensiblement la taille des snapshots (voir [l'épisode précédent](http://linuxfr.org/users/ar7/journaux/btrfs-et-opensuse-episode-1-sous-volumes-snapshots-et-rollbacks#pourquoi-tant-de-sous-volumes)) ; * depuis la version 0.3, snapper utilise les quotas Btrfs pour limiter la taille prise par les snapshots – ce sont les variables `QGROUP` et `SPACE_LIMIT` que vous pouvez voir dans la configuration. C'est bien plus efficace que les autres stratégie de nettoyage... mais pour cela il faut activer les quotas Btrfs, ce que l'on verra dans un prochain épisode. En cas de souci, voici une configuration très _safe_ pour du _desktop_, qui n'utilise pas les quotas : ```sh ~# snapper get-config Clé | Valeur -----------------------+------- ALLOW_GROUPS | ALLOW_USERS | BACKGROUND_COMPARISON | no # évite au démon snapper de crasher à la première # grosse mise à jour EMPTY_PRE_POST_CLEANUP | yes EMPTY_PRE_POST_MIN_AGE | 1800 FSTYPE | btrfs NUMBER_CLEANUP | yes NUMBER_LIMIT | 3 # c'est largement suffisant NUMBER_LIMIT_IMPORTANT | 3 # ça aussi NUMBER_MIN_AGE | 1800 SUBVOLUME | / SYNC_ACL | no TIMELINE_CLEANUP | no TIMELINE_CREATE | no # pas de création périodique TIMELINE_LIMIT_DAILY | 0 TIMELINE_LIMIT_HOURLY | 0 TIMELINE_LIMIT_MONTHLY | 0 TIMELINE_LIMIT_WEEKLY | 0 TIMELINE_LIMIT_YEARLY | 0 TIMELINE_MIN_AGE | 1800 ~# ``` C'est celle que j'utilise en ce moment. La commande `snapper set-config CLÉ=VALEUR` permet de modifier la configuration. # GRUB2 : booter sur du Btrfs ![GRUB](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Grub_logo2_large.png/413px-Grub_logo2_large.png) ## Le chargeur d'amorçage d'openSUSE Attention, danger : on rentre dans de l'openSUSE-überspecifik. Deuxième danger : je ne connais pas bien GRUB. Toujours là ? Alors allons-y ! openSUSE a une collection de patchs pour le chargeur d'amorçage GRUB2 ainsi qu'une configuration personnalisée, liée à snapper, qui permettent : * de faire des restaurations du système sans avoir à modifier la configuration du chargeur d'amorçage * d'explorer les snapshots en lecture seule du système directement depuis le menu du chargeur d'amorçage * de booter sur ces snapshots ! Je ne sais pas si d'autres distributions intègrent des patchs similaires et/ou une configuration de GRUB2 pour Btrfs. J'ai juste aperçu [grub-btrfs](https://github.com/Antynea/grub-btrfs), une configuration de GRUB écrite par un utilisateur d'Arch Linux. Cela pourrait être intéressant d'en discuter dans les commentaires ;-) ## De jolies images Pour vous donner envie, voici quelques captures d'écran des différents menus du GRUB d'openSUSE. La résolution est mauvaise, c'est une machine virtuelle, désolé... * Menu principal : ![Menu principal](http://pix.toile-libre.org/upload/original/1502796768.png) * Liste des snapshots (dommage, on ne voit pas les lignes complètes avec cette faible résolution) : ![Liste des snapshots](http://pix.toile-libre.org/upload/original/1502796861.png) * Menu d'un snapshot bootable ![Menu d'un snapshot bootable](http://pix.toile-libre.org/upload/original/1501314405.png) ## Comment ça marche ? ### Configuration Prenons les choses à l'envers et regardons d'abord la configuration. Tout d'abord, le contenu du fichier `/etc/grub.d/80_suse_btrfs_snapshot` est ajouté à `/boot/grub2/grub.cfg` au moment de sa génération : ```sh #! /bin/sh set -e if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && [ "x${GRUB_FS}" = "xbtrfs" ] ; then cat << \EOF if [ -f "/.snapshots/grub-snapshot.cfg" ]; then source "/.snapshots/grub-snapshot.cfg" fi EOF fi ``` Ces quelques lignes ne font que sourcer le fichier `/.snapshots/grub-snapshot.cfg` qui lui-même ne fait rien d'autre que sourcer les fichiers `/.snapshots/*/grub-snapshot.cfg` : ```sh ~# cat /.snapshots/grub-snapshot.cfg if [ -z "$extra_cmdline" ]; then submenu "Start bootloader from a read-only snapshot" { if [ -f "/.snapshots/72/grub-snapshot.cfg" ]; then source "/.snapshots/72/grub-snapshot.cfg" fi if [ -f "/.snapshots/71/grub-snapshot.cfg" ]; then source "/.snapshots/71/grub-snapshot.cfg" fi if [ -f "/.snapshots/70/grub-snapshot.cfg" ]; then source "/.snapshots/70/grub-snapshot.cfg" fi if [ -f "/.snapshots/69/grub-snapshot.cfg" ]; then source "/.snapshots/69/grub-snapshot.cfg" fi if [ -f "/.snapshots/68/grub-snapshot.cfg" ]; then source "/.snapshots/68/grub-snapshot.cfg" fi if [ x$snapshot_found != xtrue ]; then submenu "Not Found" { true; } fi } fi ~# ``` C'est snapper qui se charge de modifier le fichier `/.snapshots/grub-snapshot.cfg` et de rajouter le fichier `/.snapshots//grub-snapshot.cfg` à la prise de l'instantané `` : ```sh ~# ls /.snapshots/70/ grub-snapshot.cfg info.xml snapshot ~# cat grub-snapshot.cfg if [ -f "/.snapshots/70/snapshot/boot/grub2/grub.cfg" ]; then snapshot_found=true saved_subvol=$btrfs_subvol menuentry " openSUSE Tumbleweed (4.11.8-2,2017年08月17日T19:17)" "/.snapshots/70/snapshot" "/@/.snapshots/70/snapshot" { btrfs_subvol="3ドル" extra_cmdline="rootflags=subvol=3ドル" export extra_cmdline snapshot_num=70 export snapshot_num configfile "2ドル/boot/grub2/grub.cfg" btrfs_subvol=$saved_subvol } fi ~# ``` Ainsi, pas besoin de régénérer le `grub.cfg` pour rajouter des instantanés en lecture seule dans les menus de GRUB. Concernant, le menu principal, dans `/boot/grub2/grub.cfg` justement : ```sh ### BEGIN /etc/grub.d/10_linux ### menuentry 'openSUSE Tumbleweed' --class opensuse --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-4dcb59ef-2749-4017-94f4-d046a2cd7391' { load_video set gfxpayload=keep insmod gzio insmod part_msdos insmod btrfs set root='hd0,msdos2' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos2 --hint-efi=hd0,msdos2 --hint-baremetal=ahci0,msdos2 --hint='hd0,msdos2' 4dcb59ef-2749-4017-94f4-d046a2cd7391 else search --no-floppy --fs-uuid --set=root 4dcb59ef-2749-4017-94f4-d046a2cd7391 fi echo 'Chargement de Linux 4.11.8-2-default...' linux /boot/vmlinuz-4.11.8-2-default root=UUID=4dcb59ef-2749-4017-94f4-d046a2cd7391 ${extra_cmdline} resume=/dev/sda1 quiet showopts acpi_backlight=native echo 'Chargement du disque mémoire initial...' initrd /boot/initrd-4.11.8-2-default } ``` On remarque que, pour cette entrée, les chemins vers le noyau et l'initrd ressemblent à ce que l'on peut avoir avec d'autres systèmes de fichiers : il n'y a pas de référence explicite à un sous-volume. ### Pilote Btrfs : à la croisée des chemins Au niveau de GRUB en lui-même, la série de patchs modifie le pilote Btrfs pour : - fournir les commandes : * `btrfs-get-default-subvol` : équivalent `btrfs subvolume get-default`. * `btrfs-info` : équivalent à `btrfs filesystem show`. * `btrfs-list-subvols` : une version simplifiée de `btrfs subvolume list`. * `btrfs-mount-subvol` : permet de monter des sous-volumes. - permettre de définir les variables : * `btrfs_subvol` : nom du sous-volume à monter ; vous avez vu que c'est utilisé dans les `/.snapshots/*/grub-snapshot.cfg`. * `btrfs_subvolid` : id du sous-volume à monter. * `btrfs_relative_path` : booléen indiquant à GRUB s'il doit utiliser des chemins relatifs. Intéressons-nous à ce `btrfs_relative_path` et à ce qu'il implique. ![Yellow Brick Road](https://www.berlinale.de/media/filmstills/2015_1/retrospektive_11/201520146_3_IMG_FIX_700x700.jpg) GRUB gère les sous-volumes Btrfs comme des répertoires. La version _upstream_ utilise le nom complet des sous-volumes comme un chemin absolu. Par exemple : `/@/.snapshots/1/snapshot/boot/grub2/grub.cfg`, `/@/.snapshots/1/snapshot/boot/vmlinuz-4.11.8-1-default`, `/@/.snapshots/1/boot/initrd-4.11.8-1-default`, ... Cela a l'avantage d'être clair et similaire à ce qui est fait pour les autres systèmes de fichiers qui n'ont pas de notion de sous-volume. Le problème, c'est, si j'ai bien compris, que cela nous oblige à spécifier un sous-volume précis dans la configuration de GRUB. Or, dans le cas d'une restauration, souvenez-vous : on change de sous-volume. Du coup, avec un GRUB _vanilla_, on est bien obligé de régénérer sa configuration – le `grub.cfg` et même le `core.img` qui va chercher le `grub.cfg` – pour faire une restauration qui marche ! Du moins, une restauration telle qu'on l'a faite. openSUSE a une autre façon de faire, du moins si `btrfs_relative_path` est à `y`, ce qui est le cas par défaut. L'idée est de faire en sorte que GRUB n'utilise plus un chemin complet, mais un chemin relatif par rapport au sous-volume par défaut. Avec cette façon de faire, `/@/.snapshots/1/snapshot/boot/grub2/grub.cfg` devient `/boot/grub2/grub.cfg` du sous-volume par défaut. Cela évite de devoir modifier le fichier `grub.cfg` et le `core.img` pour chaque restauration. Et cela explique pourquoi un simple `snapper rollback` – ou les commandes Btrfs équivalentes – marche sur openSUSE, mais pas sur d'autres distributions. Si j'ai bien compris. ## Bugs connus ![bugs](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Buggie.svg/langfr-182px-Buggie.svg.png) Je vous en mets deux que j'ai eu l'occasion de rencontrer : * [boo#1048088](https://bugzilla.opensuse.org/show_bug.cgi?id=1048088) : la commande `btrfs-list-subvol` ne liste que les sous-volumes descendant immédiatement de `FS_TREE`. Cela marche sur les anciennes installations qui n'utilisent pas `@`, mais pour les nouvelles... ben ça affiche juste `@`. Pas bien méchant. Bug spécifique à openSUSE, vu qu'il n'y a pas de commande `btrfs-list-subvol` dans le projet _upstream_. * [boo#1049994](https://bugzilla.opensuse.org/show_bug.cgi?id=1049994) : l'accès au sous-volume `@/.snapshots/` n'est pas très propre ; cela empêche par exemple de booter correctement sur des snapshots qui auraient été transférés avec `btrfs send` et `btrfs receive` ; on reviendra sur cela quand on parlera de sauvegarde incrémentale. Le bug est en partie _upstream_ si j'ai bien compris. ## Mais quand est-ce qu'on merge ? ![git](https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Git_icon.svg/240px-Git_icon.svg.png) Ma question favorite :-) Le fait est que l'_upstream_ reste sur sa position : pas de chemin relatif pour Btrfs, seulement un chemin absolu. Du coup status quo depuis 3 ans :-/ C'est dommage. Après on peut aussi se retourner vers openSUSE : est-ce vraiment une bonne solution que d'utiliser des chemins relatifs dans GRUB2 ? Bref : c'est pas hyper bien parti, mais espérons que les gourous de GRUB arrivent un jour à une solution qui satisfasse tout le monde ! # Liens Sur Snapper : * [La documentation openSUSE sur snapper](https://doc.opensuse.org/documentation/leap/reference/html/book.opensuse.reference/cha.snapper.html). * [Gestion des instantanés avec snapper](https://www.alionet.org/content.php?673-Gestion-des-instantan%E9s-avec-Snapper), commence à dater, sur Alionet. Sur GRUB : * [Les sources du paquet grub2 d'openSUSE Tumbleweed](https://build.opensuse.org/package/show/openSUSE%3AFactory/grub2), sur le Build Service. Les patchs relatifs à Btrfs sont préfixés par "grub2-btrfs-". * [Un fil de discussion à propos de Btrfs](http://lists.gnu.org/archive/html/grub-devel/2013-12/msg00337.html) sur la liste de diffusion grub-devel.

AltStyle によって変換されたページ (->オリジナル) /