ZFS sur root : installer pour que les retours en arrière fonctionnent vraiment

Cet article vous a aidé ?

Vous ne voulez pas des « instantanés ». Vous voulez la confiance de revenir en arrière après une mise à jour qui casse à 02:00 et de redémarrer proprement — réseau, SSH, services, tout — sans devoir explorer les journaux d’initramfs comme un spéléologue avec sa lampe frontale.

La plupart des guides ZFS-on-root vous amènent à « ça démarre une fois ». La production demande plus : une disposition de datasets prévisible, une intégration du chargeur de démarrage qui respecte les environnements de démarrage, et des pratiques opérationnelles qui n’érigent pas votre historique d’instantanés en scène de crime.

Ce que « retour en arrière » doit signifier sur un vrai système

Un retour en arrière qui « fonctionne » n’est pas juste un zfs rollback qui s’exécute sans erreur. Un retour en arrière qui fonctionne signifie :

  • Le chargeur de démarrage peut énumérer et démarrer un état racine précédent (environnement de démarrage), pas seulement monter un dataset si vous éditez manuellement les paramètres du noyau.
  • L’initramfs peut importer votre pool (et le déverrouiller s’il est chiffré) de manière cohérente et automatique.
  • /boot est cohérent avec le noyau+initramfs contenus dans l’environnement racine que vous démarrez — ou est délibérément séparé et géré comme une exception de première classe.
  • L’état et la configuration des services sont isolés de sorte que revenir en arrière sur la racine ne ressuscite pas un état disque incompatible pour des applications qui écrivent en permanence (bases de données, files d’attente, stores de métriques).
  • Le flux opérateur est évident : créer un environnement de démarrage (BE), mettre à jour, redémarrer dans le BE, vérifier, conserver ou revenir en arrière.

En clair : vous n’installez pas « ZFS », vous installez une machine à remonter le temps avec un chargeur de démarrage comme panneau de contrôle. Et le panneau de contrôle doit continuer à fonctionner après votre voyage temporel.

Prise de position : si vous ne pouvez pas lister vos environnements de démarrage depuis le menu de démarrage ou une CLI prévisible et y démarrer sans rien éditer, vos retours en arrière sont du théâtre. Le théâtre est amusant, mais pas à 02:00.

Blague courte #1 : Un plan de retour en arrière qui demande « quelques étapes manuelles » est juste un plan à sens unique avec le déni intégré.

Faits et contexte intéressants (parce que l’histoire explique les arêtes d’aujourd’hui)

  1. ZFS est né chez Sun au milieu des années 2000 avec checksum de bout en bout et copy-on-write intégrés ; il a été conçu pour rendre la corruption silencieuse moins réjouissante.
  2. « Le stockage en pool » fut un changement de paradigme : les systèmes de fichiers ne sont plus liés à un unique bloc de stockage ; on gère un pool et on découpe des datasets et volumes depuis celui‑ci.
  3. Les environnements de démarrage sont devenus une norme culturelle sur Solaris/Illumos, où mettre à jour vers un nouveau BE tout en gardant l’ancien bootable était le flux attendu, pas un tour exotique.
  4. ZFS sous Linux est hors-arbre à cause d’incompatibilités de licence ; c’est pourquoi l’intégration par distribution varie et pourquoi « ça marchait sur mon portable » n’est pas un plan.
  5. GRUB a gagné le support ZFS il y a des années, mais le support dépend des flags de fonctionnalités ; activer chaque nouvelle fonctionnalité du pool peut laisser votre chargeur de démarrage bloqué dans le passé.
  6. Les feature flags d’OpenZFS ont remplacé les numéros de version sur disque ; cela a facilité l’évolution incrémentale mais introduit une nouvelle règle opérationnelle : les composants de démarrage doivent supporter les flags que vous activez.
  7. Les comportements ACL et xattr diffèrent selon les attentes OS ; ZFS peut émuler, mais des paramètres mismatches peuvent créer des surprises subtiles de permissions après un rollback.
  8. La compression n’est plus controversée une fois les CPU modernes rapides ; aujourd’hui, lz4 est souvent un gain gratuit, mais il faut tout de même l’appliquer avec intention.
  9. Les instantanés sont peu coûteux jusqu’à ce qu’ils ne le soient plus : les métadonnées et la fragmentation peuvent s’immiscer, surtout quand les instantanés sont conservés indéfiniment et que des datasets à fort churn vivent dans le même arbre racine.

Les retours en arrière échouent quand vous traitez ZFS-on-root comme « un choix de système de fichiers » plutôt que comme « un choix d’architecture de démarrage ». Le système de fichiers est la partie facile. Le démarrage est l’endroit où votre confiance va mourir.

Objectifs de conception : règles qui rendent les retours en arrière sans histoire

Règle 1 : Séparer ce qui change ensemble

Les parties du système d’exploitation (paquets, bibliothèques, configuration) doivent être dans l’environnement de démarrage. Les données applicatives à fort churn ne doivent pas l’être. Si vous revenez en arrière sur la racine et que vos fichiers de base de données reviennent aussi, vous avez construit une machine à remonter le temps qui viole la causalité.

À faire : séparez /var/lib en datasets par application, et placez les bases de données sur des datasets dédiés (ou même sur des pools séparés) avec leurs propres politiques d’instantanés.

À ne pas faire : mettre tout sous un unique dataset monolithique rpool/ROOT et appeler ça « simple ». C’est simple comme une grande zone d’impact unique.

Règle 2 : Le démarrage voit ce dont il a besoin, et pas plus

L’initramfs et le chargeur de démarrage ont besoin d’une manière stable de trouver et monter la bonne racine. Cela signifie typiquement : un bootfs prévisible, une nomenclature claire des BE, et éviter des fonctionnalités que le chargeur de démarrage ne peut pas lire.

Règle 3 : Votre frontière de rollback est un environnement de démarrage, pas un instantané

Les instantanés sont des matériaux bruts. Un environnement de démarrage est un instantané+clone (ou clone de dataset) organisé avec une / cohérente et généralement une histoire /boot cohérente. Utilisez le bon outil. Arrêtez d’essayer de démarrer des instantanés au hasard comme un numéro de cirque.

Règle 4 : Les propriétés par défaut doivent être des valeurs par défaut pour une raison

Définissez les propriétés au bon niveau de dataset. Évitez de tout mettre sur la racine du pool sauf si vous aimez déboguer l’héritage pour le reste de votre vie.

Disposition des datasets qui fait se comporter les environnements de démarrage

La disposition des datasets est l’endroit où la plupart des installations « ZFS on root » échouent discrètement. Elles démarrent, certes. Mais plus tard, vous ne pouvez pas revenir en arrière proprement parce que la moitié de l’état est partagé, ou parce que des points de montage se battent entre eux, ou parce que /boot est sur une île qui ne correspond pas à votre BE.

Une disposition pragmatique (pool unique, GRUB, Linux typique)

Supposons un pool nommé rpool. On crée :

  • rpool/ROOT (container, mountpoint=none)
  • rpool/ROOT/default (BE racine réel, mountpoint=/)
  • rpool/ROOT/<be-name> (environnements de démarrage additionnels)
  • rpool/BOOT ou une partition /boot non-ZFS séparée, selon votre distribution et vos goûts
  • rpool/home (mountpoint=/home)
  • rpool/var (mountpoint=/var, en général)
  • rpool/var/log (churn des logs ; politique d’instantanés différente)
  • rpool/var/lib (container, souvent)
  • rpool/var/lib/docker ou .../containers (si vous y tenez, mais envisagez un pool séparé)
  • rpool/var/lib/postgresql, rpool/var/lib/mysql etc. (séparés volontairement)
  • rpool/tmp (mountpoint=/tmp, avec sync=disabled seulement si vous aimez vivre dangereusement ; sinon laissez tel quel)

Et /boot ?

Deux modèles viables :

  • /boot sur EFI+ext4 (ou vfat pour l’ESP, ext4 pour /boot) : ennuyeux, compatible, facile. Les retours en arrière doivent gérer soigneusement les versions noyau/initramfs, car /boot est partagé entre les BE. C’est encore correct si vous utilisez un outil BE qui s’en occupe.
  • /boot sur ZFS : possible avec GRUB lisant ZFS, mais vous devez garder les features du pool compatibles avec GRUB et être très délibéré lors des mises à jour. Cela peut être propre si l’outillage de votre distro le supporte ; sinon c’est une nuisance attirante.

Mon biais : si vous faites cela sur des machines de flotte, gardez /boot sur une petite partition ext4 et séparée de l’ESP. Retirez un élément mobile du démarrage précoce. Vous pouvez toujours avoir une forte sémantique de rollback avec une bonne gestion des BE ; vous traitez simplement les artefacts noyau avec soin.

Plan d’installation : une construction ZFS-on-root propre et favorable aux retours

Ceci est un plan générique qui se mappe bien aux systèmes de type Debian/Ubuntu, et conceptuellement à d’autres. Adaptez le gestionnaire de paquets et l’outillage initramfs en conséquence. L’essentiel n’est pas les commandes exactes ; c’est l’architecture et les étapes de vérification.

Partitionnement : choisissez la prévisibilité

Utilisez GPT. Créez une ESP. Faites une partition dédiée pour /boot à moins que vous ne sachiez que vous avez besoin de /boot sur ZFS et que vous ayez testé la compatibilité GRUB avec les features du pool. Mettez le reste dans ZFS.

Créer le pool en gardant le démarrage à l’esprit

Choisissez ashift correctement (presque toujours 12). Choisissez des valeurs par défaut sensées : compression=lz4, atime=off, xattr=sa, et un acltype cohérent pour Linux (posixacl).

Chiffrement : si vous chiffrez la racine, décidez comment elle se déverrouille (mot de passe au console, fichier-clé dans initramfs, ou déverrouillage réseau). Ne « réglez ça plus tard ». Plus tard, c’est quand votre système ne démarrera pas.

Construire les datasets et les monter correctement

Définissez mountpoint=none sur les containers. Mettez le dataset racine réel sous rpool/ROOT/<be-name> avec mountpoint=/. Montez ensuite une racine temporaire sur /mnt et installez le système dans celle-ci.

Installer le chargeur de démarrage + initramfs avec le support ZFS

Vous avez besoin des modules ZFS dans l’initramfs et que la logique d’import du pool soit correcte. Cela inclut :

  • Une ligne de commande noyau correcte root=ZFS=rpool/ROOT/<be-name> (ou l’équivalent de votre distro).
  • Cache du pool si votre initramfs l’utilise (/etc/zfs/zpool.cache), plus un hostid correct.
  • GRUB configuré pour trouver ZFS et lister les BE (varie selon la distro/outillage).

Outillage des environnements de démarrage : choisissez-en un et utilisez-le

Si vous voulez des retours en arrière qui « fonctionnent vraiment », utilisez un outil BE qui s’intègre au flux de mise à jour du chargeur de démarrage de votre distro. Sous Linux, cela peut signifier des outils comme zsys (ère Ubuntu), zfsbootmenu (une approche différente), ou des hooks spécifiques à la distro. Je ne prescris pas un outil précis, mais j’exige que vous cessiez de bricoler des BE sans une histoire d’intégration de démarrage répétable.

Citation fiabilité, parce que c’est toujours vrai : « L’espoir n’est pas une stratégie. » — General Gordon R. Sullivan

Tâches pratiques : commandes, sorties et décisions (12+)

Voici les vérifications à exécuter lors de l’installation, de la validation ou du débogage. Chaque tâche inclut : la commande, ce que la sortie signifie, et la décision suivante.

Task 1: Confirm sector size assumptions (so ashift won’t bite you)

cr0x@server:~$ lsblk -o NAME,MODEL,SIZE,PHY-SEC,LOG-SEC,ROTA
NAME MODEL             SIZE PHY-SEC LOG-SEC ROTA
nvme0n1 Samsung SSD    1.8T    4096    512    0
sda    ST4000NM0035    3.6T    4096   4096    1

Signification : PHY-SEC est la taille de secteur physique. La plupart des disques modernes ont 4K physiques. Si vous choisissez ashift=12 (2^12=4096), vous êtes bien aligné.

Décision : Utilisez ashift=12 sauf si vous avez un dispositif inhabituel nécessitant réellement 8K (rare) ou si vous êtes sur du 512B pur (également rare de nos jours).

Task 2: Create the pool with sane defaults (and show you what happened)

cr0x@server:~$ sudo zpool create -f -o ashift=12 \
  -O compression=lz4 -O atime=off -O xattr=sa -O acltype=posixacl \
  -O mountpoint=none -R /mnt rpool /dev/nvme0n1p3
cr0x@server:~$ zpool status -v rpool
  pool: rpool
 state: ONLINE
config:

        NAME            STATE     READ WRITE CKSUM
        rpool           ONLINE       0     0     0
          nvme0n1p3     ONLINE       0     0     0

errors: No known data errors

Signification : Le pool existe, ONLINE, sans erreurs. L’altroot -R /mnt signifie que les montages se font sous /mnt pour l’installation.

Décision : Si vous voyez autre chose qu’ONLINE, arrêtez. Réparez le câblage, le chemin du périphérique, le partitionnement ou la santé du disque avant d’installer un OS sur une base instable.

Task 3: Verify the pool feature flags won’t surprise your bootloader

cr0x@server:~$ zpool get -H feature@* rpool | head
rpool    feature@async_destroy              enabled     local
rpool    feature@bookmarks                  enabled     local
rpool    feature@embedded_data              enabled     local
rpool    feature@extensible_dataset         enabled     local
rpool    feature@filesystem_limits          enabled     local
rpool    feature@hole_birth                 enabled     local
rpool    feature@large_blocks               enabled     local
rpool    feature@lz4_compress               enabled     local
rpool    feature@spacemap_histogram         enabled     local
rpool    feature@enabled_txg                active      local

Signification : Les feature flags sont enabled/active. Les chargeurs de démarrage (en particulier les builds de GRUB) peuvent ne pas supporter toutes les features sur chaque version de distro.

Décision : Si vous comptez sur GRUB pour lire ZFS pour /boot, validez la compatibilité des features. Si vous n’êtes pas sûr à 100 %, gardez /boot hors ZFS et laissez GRUB lire ext4.

Task 4: Create container datasets with mountpoint discipline

cr0x@server:~$ sudo zfs create -o mountpoint=none rpool/ROOT
cr0x@server:~$ sudo zfs create -o mountpoint=/ rpool/ROOT/default
cr0x@server:~$ sudo zfs create -o mountpoint=/home rpool/home
cr0x@server:~$ sudo zfs create -o mountpoint=/var rpool/var
cr0x@server:~$ sudo zfs create -o mountpoint=/var/log rpool/var/log
cr0x@server:~$ zfs list -o name,mountpoint -r rpool | head -n 10
NAME               MOUNTPOINT
rpool              none
rpool/ROOT         none
rpool/ROOT/default /mnt/
rpool/home         /mnt/home
rpool/var          /mnt/var
rpool/var/log      /mnt/var/log

Signification : Avec altroot /mnt, les mountpoints apparaissent sous /mnt. Les datasets conteneurs ne sont pas montés.

Décision : Si vous voyez des datasets conteneurs montés, corrigez cela maintenant. Les datasets conteneurs devraient généralement être mountpoint=none pour éviter des conflits de montage et des couplages étranges lors des rollbacks.

Task 5: Check what will mount at boot (and whether ZFS agrees)

cr0x@server:~$ zfs get -r canmount,mountpoint rpool/ROOT/default | head
NAME               PROPERTY   VALUE     SOURCE
rpool/ROOT/default canmount   on        default
rpool/ROOT/default mountpoint /         local

Signification : Le dataset root montera sur /.

Décision : Pour des datasets non-root que vous voulez par-BE (rare mais parfois nécessaire), définissez canmount=noauto et gérez-les explicitement. Ne faites pas à l’arrache.

Task 6: Confirm the bootfs property points at the intended BE

cr0x@server:~$ sudo zpool set bootfs=rpool/ROOT/default rpool
cr0x@server:~$ zpool get bootfs rpool
NAME   PROPERTY  VALUE              SOURCE
rpool  bootfs    rpool/ROOT/default local

Signification : Le bootfs du pool est défini. Certains flux de démarrage utilisent cela comme indice.

Décision : Lorsque vous créez un nouveau BE pour monter une mise à jour, définissez bootfs sur ce BE avant le reboot (ou laissez votre outil BE le faire). Si vous oubliez, vous démarrerez l’ancien environnement et vous vous demanderez pourquoi rien n’a changé.

Task 7: Validate hostid and zpool.cache (so initramfs imports reliably)

cr0x@server:~$ hostid
9a3f1c2b
cr0x@server:~$ sudo zgenhostid -f 9a3f1c2b
cr0x@server:~$ sudo zpool set cachefile=/etc/zfs/zpool.cache rpool
cr0x@server:~$ sudo ls -l /etc/zfs/zpool.cache
-rw-r--r-- 1 root root 2264 Jan 12 09:41 /etc/zfs/zpool.cache

Signification : Un hostid stable réduit le drame « le pool a été vu pour la dernière fois par un autre système ». Le cachefile aide le démarrage précoce à trouver les périphériques de manière fiable.

Décision : Sur des VM clonées, régénérez l’hostid par machine. Les hostids dupliqués sont une histoire classique « ça marche en dev, explose en prod ».

Task 8: Check that initramfs includes ZFS and will import pools

cr0x@server:~$ lsinitramfs /boot/initrd.img-$(uname -r) | grep -E 'zfs|zpool' | head
usr/sbin/zpool
usr/sbin/zfs
usr/lib/modules/6.8.0-31-generic/kernel/zfs/zfs.ko
usr/lib/modules/6.8.0-31-generic/kernel/zfs/zcommon.ko
scripts/zfs

Signification : Les outils et modules sont dans l’initramfs. S’ils ne le sont pas, le démarrage précoce ne pourra pas importer le pool, et vous atterrirez dans un shell initramfs avec un regard vide.

Décision : Si c’est manquant, installez le paquet d’intégration ZFS-initramfs adapté à votre distro et reconstruisez l’initramfs.

Task 9: Confirm kernel cmdline points at the right root dataset

cr0x@server:~$ cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-6.8.0-31-generic root=ZFS=rpool/ROOT/default ro quiet

Signification : Le noyau se voit demander de monter rpool/ROOT/default comme racine.

Décision : Si cela pointe sur un instantané ou un mauvais dataset, corrigez la configuration GRUB ou votre outillage BE. N’essayez pas de « juste le monter » après le démarrage ; la frontière de démarrage est incorrecte.

Task 10: Create a new boot environment (clone) and verify it’s independent

cr0x@server:~$ sudo zfs snapshot rpool/ROOT/default@pre-upgrade
cr0x@server:~$ sudo zfs clone rpool/ROOT/default@pre-upgrade rpool/ROOT/upgrade-test
cr0x@server:~$ zfs list -o name,origin,mountpoint -r rpool/ROOT | grep -E 'default|upgrade-test'
rpool/ROOT/default        -                           /
rpool/ROOT/upgrade-test   rpool/ROOT/default@pre-upgrade  /

Signification : upgrade-test est un clone du snapshot et peut servir de BE. Note : les deux montrent mountpoint / ; un seul devrait être monté à la fois.

Décision : Définissez canmount=noauto sur les BE inactifs pour qu’ils ne se montent pas accidentellement s’ils sont importés en contexte de maintenance.

Task 11: Make only one BE mountable by default

cr0x@server:~$ sudo zfs set canmount=noauto rpool/ROOT/default
cr0x@server:~$ sudo zfs set canmount=noauto rpool/ROOT/upgrade-test
cr0x@server:~$ sudo zfs set canmount=on rpool/ROOT/upgrade-test
cr0x@server:~$ zfs get canmount rpool/ROOT/default rpool/ROOT/upgrade-test
NAME                   PROPERTY  VALUE   SOURCE
rpool/ROOT/default      canmount  noauto  local
rpool/ROOT/upgrade-test canmount  on      local

Signification : Vous contrôlez ce qui se monte automatiquement. Cela réduit les incidents de « deux racines montées quelque part de façon bizarre ».

Décision : Pour votre flotte, standardisez une convention de propriétés BE. Votre futur vous remerciera discrètement, et c’est la meilleure forme de gratitude.

Task 12: Switch bootfs to the new BE and update bootloader configs

cr0x@server:~$ sudo zpool set bootfs=rpool/ROOT/upgrade-test rpool
cr0x@server:~$ zpool get bootfs rpool
NAME   PROPERTY  VALUE                   SOURCE
rpool  bootfs    rpool/ROOT/upgrade-test local
cr0x@server:~$ sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-6.8.0-31-generic
cr0x@server:~$ sudo update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.8.0-31-generic
Found initrd image: /boot/initrd.img-6.8.0-31-generic
done

Signification : Vous préparez les artefacts du démarrage précoce et le menu de démarrage. Certaines distros nécessitent des hooks additionnels pour l’énumération des BE ; ceci montre la forme générale.

Décision : Redémarrez et validez que vous pouvez bien démarrer à la fois le nouveau BE et l’ancien BE. Si vous ne pouvez pas, arrêtez de les appeler « environnements de démarrage ». Ce sont des « datasets que vous espérez pouvoir monter plus tard ».

Task 13: Confirm what you actually booted into after reboot

cr0x@server:~$ findmnt -n -o SOURCE /
rpool/ROOT/upgrade-test
cr0x@server:~$ zfs list -o name,mounted,canmount rpool/ROOT
NAME                   MOUNTED  CANMOUNT
rpool/ROOT             no       on
rpool/ROOT/default      no       noauto
rpool/ROOT/upgrade-test yes      on

Signification : La racine est le nouveau BE. L’ancien BE n’est pas monté.

Décision : Si la racine est toujours default, vous n’avez pas réellement basculé ; corrigez bootfs et/ou la configuration du chargeur, puis réessayez.

Task 14: Audit snapshots and space impact (so “rollback” doesn’t become “out of space”)

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint rpool | head -n 8
NAME                  USED  AVAIL  REFER  MOUNTPOINT
rpool                38.2G  1.67T   192K  none
rpool/ROOT           14.1G  1.67T   192K  none
rpool/ROOT/default   7.20G  1.67T  7.20G  /
rpool/ROOT/upgrade-test 7.14G 1.67T 7.14G  /
rpool/var            11.5G  1.67T  7.31G  /var
rpool/var/log        1.84G  1.67T  1.84G  /var/log
cr0x@server:~$ zfs list -t snapshot -o name,used,refer -r rpool/ROOT | tail -n 5
rpool/ROOT/default@pre-upgrade  132M  7.20G

Signification : used des snapshots montre l’espace retenu par les changements depuis le snapshot. Un used élevé indique souvent du churn dans des datasets que vous n’avez pas isolés.

Décision : Si les instantanés explosent parce que des logs ou des couches de conteneurs sont à l’intérieur du BE, séparez-les maintenant. La pression d’espace est le tueur n°1 des rollbacks parce que ZFS a besoin d’air pour respirer.

Task 15: Check pool health and error counters before blaming “ZFS weirdness”

cr0x@server:~$ zpool status -xv
all pools are healthy

Signification : Pas d’erreurs connues, pas de vdev dégradé. Bien.

Décision : Si vous voyez des erreurs de checksum, traitez cela comme un incident matériel/transport jusqu’à preuve du contraire. « Mon rollback n’a pas marché » se traduit parfois par « mon SSD ment ».

Task 16: Identify a boot-import delay (the silent rollback saboteur)

cr0x@server:~$ systemd-analyze blame | head -n 10
14.203s zfs-import-cache.service
 6.412s dev-nvme0n1p3.device
 3.901s systemd-udev-settle.service
 2.311s network-online.target
 1.988s zfs-mount.service

Signification : Le temps de démarrage est dominé par l’import du pool en attente de périphériques ou par un mismatch de cache.

Décision : Si zfs-import-cache est lent, corrigez la stabilité des noms de périphériques, reconstruisez zpool.cache, ou passez à l’import par ID. Si udev-settle est lent, auditez ce qui le déclenche.

Trois mini-récits d’entreprise depuis le pays des conséquences

1) Incident causé par une mauvaise hypothèse : « Les instantanés équivalent aux environnements de démarrage »

Une entreprise SaaS de taille moyenne a déployé ZFS-on-root sur un ensemble de nœuds API. L’ingénieur qui l’a porté était compétent, mais pressé. Ils ont créé un seul dataset pour /, activé les instantanés, et déclaré victoire. Le reste de l’organisation a entendu « on peut revenir en arrière maintenant ». Cette phrase a des conséquences.

Lors d’un cycle de patchs de sécurité routinier, un nœud est revenu avec le réseau cassé après reboot. L’astreinte a fait ce qu’on lui avait dit : « rollback ». Ils ont fait un rollback du dataset racine sur le dernier instantané, redémarré, et… obtinrent le même réseau cassé. Puis ils ont essayé des instantanés plus anciens. Même résultat. Maintenant ils en veulent à ZFS, ce qui est injuste mais fréquent.

La cause racine était que leur /boot vivait sur une partition ext4 partagée entre tous les « états », et l’initramfs avait été régénéré avec un mismatch de modules. Le rollback a restauré le contenu du root, mais n’a pas rétabli le couple noyau+initramfs dans /boot. Ils démarraient en fait un nouvel initramfs dans une ancienne racine. Linux est beaucoup de choses, mais « indulgent sur les incompatibilités ABI au démarrage » n’en fait pas partie.

La correction n’a pas été magique. Ils ont introduit de vrais environnements de démarrage : des clones de rpool/ROOT/<be> et un workflow où les mises à jour se font dans un nouveau BE, et /boot est mis à jour de façon coordonnée. Ils ont aussi changé leur langage interne : ils ont cessé d’appeler les instantanés « rollbacks » et ont commencé à dire « basculement d’environnement de démarrage ». La précision a réduit le nombre de surprises nocturnes.

2) Optimisation qui s’est retournée contre eux : « On va activer dedup sur la racine, c’est quasi identique »

Une firme financière adorait l’idée des BE mais détestait l’empreinte disque. Quelqu’un a remarqué que plusieurs BE se ressemblent : mêmes bibliothèques, mêmes binaires, juste quelques mises à jour. La déduplication semblait une astuce propre : stocker les blocs identiques une seule fois, gagner de l’espace, obtenir des BE « infinis ».

Ils ont activé dedup sur l’arbre racine et ont fonctionné heureux pendant un temps. Puis une vague de mises à jour est arrivée : noyaux, runtimes, outils de conteneurs. La table de déduplication a grossi, la pression mémoire a augmenté, et les machines ont commencé à thrash. Pas catastrophique au début — juste un peu de latence, juste un peu d’étrangeté. Puis un redémarrage est survenu, et le temps de boot a explosé. L’import était lent, les services ont expiré, et un nœud est sorti du load balancer. Répété à travers le cluster, un par un, au fil des maintenances.

La déduplication n’était pas « cassée ». Elle a fait exactement ce qu’elle fait : échanger mémoire et travail métadonnées contre de l’espace. Sur des filesystems racine avec churn et beaucoup d’instantanés/BE, ce compromis peut être brutal. Le pire est que le mode de défaillance n’est pas une erreur nette ; c’est une mort par mille opérations lentes.

La récupération fut ennuyeuse et coûteuse : migration hors des datasets avec dedup vers de nouveaux sans dedup, redémarrage dans des BE propres, et acceptation que clones + compression suffisent. Ils ont cessé d’optimiser pour le tableau de bord de l’équipe stockage et ont commencé à optimiser pour la fiabilité des redémarrages. C’est la bonne direction de l’embarras.

3) Pratique ennuyeuse mais correcte qui a sauvé la situation : « Toujours garder un BE connu bon et tester la bootabilité »

Une société d’analytique santé avait une pratique qui semblait fastidieuse : chaque mois, ils créaient un environnement de démarrage « connu bon », l’étiquetaient avec le ticket de changement, et le conservaient au moins un cycle de release complet. Ils imposaient aussi : après toute mise à jour, l’opérateur doit prouver qu’il peut toujours démarrer l’ancien BE en le sélectionnant dans le menu et en atteignant le mode multi-utilisateur. Puis ils redémarraient dans le nouveau BE.

Ça leur coûtait peut-être dix minutes par fenêtre de mise à jour. Les gens se plaignaient. Les gens se plaignent toujours des rituels qui empêchent des problèmes qu’ils n’ont pas encore subis.

Un trimestre, une mise à jour de pilote fournisseur a cassé l’ordre d’énumération des disques sur un lot de serveurs. Rien de dramatique, juste assez pour que la logique d’import de l’initramfs ralentisse et choisisse parfois le mauvais chemin en premier. Quelques machines n’ont pas réussi à démarrer dans l’environnement mis à jour. Mais parce que l’équipe avait un BE connu bon testé et une procédure établie, l’astreinte a sélectionné l’ancien BE, le système est monté, et ils ont eu le temps de corriger le nouveau BE sans outage complet.

C’est à cela que ressemble le « correct » en production : des étapes pas glamours qui échangent cinq minutes maintenant contre cinq heures non passées plus tard. Aussi, ça raccourcit les postmortems, ce qui est une forme de gentillesse.

Mode opératoire de diagnostic rapide : trouver le goulot vite

Quand un rollback/environnement de démarrage ZFS-on-root échoue, vous pouvez perdre des heures dans le mauvais niveau. Ne le faites pas. Vérifiez dans cet ordre.

Premièrement : Démarrez-vous dans le BE que vous croyez démarrer ?

  • Depuis un système en fonctionnement : findmnt -n -o SOURCE /
  • Confirmez la cmdline : cat /proc/cmdline
  • Confirmez zpool get bootfs rpool

Interprétation : Si votre basculement BE n’a pas pris, tout le reste est du bruit. Corrigez la sélection de démarrage avant de déboguer l’espace utilisateur.

Deuxièmement : Le démarrage précoce peut-il importer le pool rapidement et de manière cohérente ?

  • systemd-analyze blame | head pour les délais zfs-import-*
  • journalctl -b -u zfs-import-cache.service pour timeouts et périphériques manquants
  • Vérifiez que /etc/zfs/zpool.cache existe et correspond à la réalité
  • Vérifiez la cohérence du hostid : hostid

Interprétation : Les problèmes d’import se déguisent en « échecs aléatoires au démarrage ». Ils sont généralement déterministes une fois que vous regardez la découverte des périphériques et le cache.

Troisièmement : /boot est-il cohérent avec le BE racine ?

  • Listez noyaux et initramfs : ls -lh /boot
  • Vérifiez que l’initramfs contient les modules ZFS : lsinitramfs ... | grep zfs
  • Vérifiez la génération du menu GRUB et ses entrées

Interprétation : Si vous partagez /boot entre les BE, vous avez besoin d’une politique. Sans politique, vous avez « ce que la dernière mise à jour a fait », ce qui n’est pas une stratégie d’ingénierie.

Quatrièmement : Ça démarre mais le rollback ne « restaure pas la sanity », vérifiez les frontières des datasets

  • Les logs sont-ils dans le BE ? zfs list -o name,mountpoint | grep /var/log
  • L’état applicatif est-il partagé entre les BE de façon inattendue ? Passez en revue les datasets de /var/lib
  • Pression d’espace des instantanés : zfs list -t snapshot, zpool list

Interprétation : La plupart des déceptions de rollback sont auto-infligées via une mauvaise séparation des datasets.

Erreurs courantes : symptômes → cause racine → correction

1) Symptom: “Rollback succeeded, but system still broken after reboot”

Cause racine : /boot est partagé et n’a pas été restauré ; mismatch noyau/initramfs avec la racine restaurée. Ou vous avez rollbacké un dataset sans changer l’environnement que le chargeur utilise.

Correction : Utilisez des BE (basés sur clones) et coordonnez les mises à jour noyau/initramfs. Confirmez que root=ZFS=... pointe vers le BE voulu. Envisagez de conserver les artefacts noyau par BE si votre outillage le supporte, ou implémentez des règles strictes de rétention des noyaux.

2) Symptom: Boot drops to initramfs shell: “cannot import ‘rpool’”

Cause racine : Modules ZFS manquants dans l’initramfs, zpool.cache incorrect, chemins de périphériques instables, ou hostid mismatched (commun après clonage).

Correction : Reconstruisez l’initramfs avec ZFS inclus ; régénérez /etc/zfs/zpool.cache ; assurez‑vous que l’hostid est unique ; préférez des identifiants de périphérique stables. Validez en inspectant le contenu de l’initramfs.

3) Symptom: GRUB can’t see the pool or can’t read /boot on ZFS

Cause racine : Features du pool activées que la build GRUB ne peut pas lire, ou module GRUB ZFS manquant/ancien.

Correction : Gardez /boot hors ZFS, ou figez les features du pool à ce que GRUB supporte, ou mettez à jour la pile du chargeur de démarrage de manière contrôlée. Ne faites pas un « zpool upgrade » sur des pools critiques pour le démarrage sans réfléchir.

4) Symptom: Boot environment list exists, but selecting an older one panics or hangs

Cause racine : L’ancien BE a un initramfs qui ne peut pas importer le pool avec la nomenclature actuelle, la config de chiffrement, ou l’ensemble de modules ; ou l’espace utilisateur attend un noyau plus récent.

Correction : Testez périodiquement le démarrage des anciens BE. Gardez au moins un BE connu bon mis à jour suffisamment pour démarrer sur le matériel/firmware actuel. Si vous gardez des BE anciens, traitez-les comme des archives, pas comme des cibles de démarrage.

5) Symptom: “zfs rollback” fails with dataset busy, or rollback breaks mounts

Cause racine : Tentative de rollback du dataset root monté en live, ou conflits de points de montage dus à des containers montés, ou canmount mal géré.

Correction : Faites les rollback en basculant d’abord de BE, pas en rollbackant la racine montée. Assurez-vous que les datasets de containers ont mountpoint=none. Mettez les BE inactifs en canmount=noauto.

6) Symptom: Space keeps shrinking, snapshots “don’t delete”

Cause racine : Les snapshots retiennent des blocs ; les clones dépendent des snapshots ; supprimer des snapshots qui sont origine de clones ne libérera pas l’espace comme vous l’attendez.

Correction : Comprenez les dépendances clone/snapshot. Promouvez les clones lorsque nécessaire. Séparez les datasets à fort churn. Implémentez des politiques de rétention par dataset.

7) Symptom: Boot becomes slower over time

Cause racine : Délais d’import dus à la découverte des périphériques, métadonnées d’instantanés gonflées, ou une « optimisation » comme dedup qui a augmenté le travail métadonnées.

Correction : Mesurez avec systemd-analyze. Corrigez la méthode d’import et le cache. Réduisez le churn d’instantanés sur la racine. Évitez la dédup sur la racine sauf si vous avez une bonne raison et beaucoup de RAM.

Blague courte #2 : La dédup sur la racine, c’est comme une moto sportive en hiver : impressionnant jusqu’au moment où la physique vous rend visite personnellement.

Listes de contrôle / plan étape par étape

Checklist A: Before you install

  • Décidez : /boot sur ext4 (recommandé pour la flotte) ou /boot sur ZFS (seulement avec compatibilité GRUB vérifiée).
  • Choisissez un nom de pool (rpool est courant) et un schéma de nommage pour les BE (default, 2025q1-upgrade, etc.).
  • Confirmez les tailles de secteurs (lsblk) et fixez ashift=12.
  • Décidez de la méthode de chiffrement et de l’histoire opérationnelle d’unlock.
  • Notez les frontières des datasets : au minimum séparez /var/log et les chemins de bases de données.

Checklist B: Create pool + datasets (rollback-friendly)

  • Créez le pool avec mountpoint=none, compression=lz4, atime=off, xattr=sa, acltype=posixacl.
  • Créez rpool/ROOT container et rpool/ROOT/default monté sur /.
  • Créez des datasets séparés pour /home, /var, /var/log, et les données applicatives.
  • Définissez bootfs sur le BE que vous comptez démarrer.
  • Définissez cachefile et générez un hostid stable.

Checklist C: Install OS + make it bootable

  • Installez l’espace utilisateur ZFS et les modules noyau.
  • Assurez-vous que l’initramfs inclut les scripts et modules d’import ZFS.
  • Installez le chargeur de démarrage et assurez-vous que la cmdline noyau inclut root=ZFS=rpool/ROOT/<be>.
  • Si /boot est partagé : définissez la rétention des noyaux et le processus de mise à jour ; ne comptez pas sur les bonnes ondes.

Checklist D: Validate rollback behavior (the part everyone skips)

  • Créez un nouveau BE (snapshot + clone) avant votre première vraie mise à jour.
  • Basculez bootfs vers le nouveau BE et régénérez les configs de démarrage.
  • Redémarrez dans le nouveau BE ; confirmez avec findmnt.
  • Redémarrez dans l’ancien BE depuis le menu ; confirmez qu’il démarre encore.
  • Ce n’est qu’ensuite que vous procédez à la mise à jour réelle et répétez la danse.

FAQ

1) Are ZFS snapshots enough for OS rollbacks?

Non. Les instantanés sont des briques de construction. Pour un rollback d’OS vous voulez un environnement de démarrage que le chargeur peut sélectionner et démarrer proprement.

2) Should I put /boot on ZFS?

Si vous exploitez une flotte et voulez moins de surprises au démarrage précoce, gardez /boot sur ext4. Mettez-le sur ZFS uniquement si vous avez testé la compatibilité GRUB avec les features du pool et votre flux de mise à jour.

3) What’s the single most important dataset decision for rollbacks?

Séparez l’état à fort churn du BE : au minimum /var/log et les bases de données. Sinon les instantanés et clones deviennent des ancres d’espace et la sémantique des rollbacks devient étrange.

4) How many boot environments should I keep?

Gardez au moins deux bootables : l’actuel et un connu bon. Plus est acceptable si vous avez une politique de rétention et comprenez les dépendances snapshot/clone.

5) Why does my pool import slowly at boot?

Généralement l’instabilité de découverte des périphériques ou un zpool.cache obsolète. Parfois duplication d’hostid sur systèmes clonés. Mesurez avec systemd-analyze blame et inspectez les logs zfs-import-*.

6) Can I just run zfs rollback on rpool/ROOT/default while the system is running?

Ne le faites pas. Rollback d’une racine montée est une invitation à de nouveaux et intéressants modes de défaillance. Basculez d’environnement de démarrage, puis redémarrez.

7) Is encryption on ZFS root safe operationally?

Oui, si vous concevez le workflow d’unlock. Décidez d’avance si vous déverrouillez via mot de passe console, fichier-clé dans initramfs, ou mécanisme distant. « On verra plus tard » devient souvent « on ne peut pas démarrer ».

8) Should I enable dedup to save space across BEs?

Généralement non pour la racine. La compression est habituellement le bon choix par défaut. La dédup peut être valide dans des cas étroits avec beaucoup de RAM et des mesures, mais ce n’est pas un interrupteur à presser sans réfléchir.

9) What property settings are the usual safe defaults for root datasets?

Communément : compression=lz4, atime=off, xattr=sa, et acltype=posixacl. Validez les attentes des applications sur les ACL et xattrs, surtout avec des runtimes de conteneurs.

10) How do I know which BE I’m currently on?

findmnt -n -o SOURCE / doit afficher quelque chose comme rpool/ROOT/<be>. Vérifiez aussi cat /proc/cmdline pour root=ZFS=....

Conclusion : prochaines étapes réalisables cette semaine

Si vous avez déjà ZFS sur root et que vous n’êtes pas sûr que les retours en arrière fonctionnent vraiment, n’attendez pas la prochaine mauvaise mise à jour pour le découvrir. Faites trois choses :

  1. Prouvez votre identité de démarrage actuelle : vérifiez /proc/cmdline, findmnt /, et zpool get bootfs. Assurez-vous que l’histoire correspond.
  2. Créez un vrai environnement de démarrage et démarrez-le : snapshot, clone, changez bootfs, régénérez les artefacts de démarrage, redémarrez. Puis démarrez aussi l’ancien. Si l’un échoue, corrigez-le maintenant pendant que vous êtes calme.
  3. Redessinez les frontières des datasets là où le churn existe : séparez /var/log et les données applicatives hors du BE. Ajoutez des politiques de rétention. Ne laissez plus les logs tenir vos rollbacks en otage.

ZFS-on-root vaut le coup lorsqu’il est installé comme vous prévoyez de l’utiliser : comme un système de rollback contrôlé avec un chargeur de démarrage qui comprend votre intention. L’objectif n’est pas d’être malin. L’objectif est une récupération ennuyeuse.

← Précédent
HBM sur les CPU : quand la mémoire entre dans le package
Suivant →
Docker « Trop de requêtes » lors du pull d’images : corriger le throttling du registre sérieusement

Laisser un commentaire