ZFS canmount : le réglage qui évite les surprises au démarrage

Cet article vous a aidé ?

Les pannes de stockage au démarrage n’annoncent généralement pas leur arrivée avec des feux d’artifice. Elles se manifestent de façon plus discrète : un système qui « démarre » mais ne retrouve pas /var, une connexion qui fonctionne mais pas les services, un hôte qui monte l’ensemble de données d’hier parce qu’il a paru plus pressé que celui d’aujourd’hui. Si vous utilisez ZFS suffisamment longtemps, vous finirez par rencontrer la propriété qui décide si vos datasets attendent poliment leur tour — ou se ruent dans votre table de montage au pire moment.

Cette propriété, c’est canmount. Elle est petite, facile à ignorer, et elle a sauvé plus de systèmes de production que n’importe laquelle des fonctionnalités ZFS plus tape-à-l’œil. Si un dataset s’est déjà « mystérieusement » monté par-dessus un répertoire existant, ou si un environnement de démarrage est arrivé avec une racine incorrecte, c’est souvent là que l’histoire s’arrête. Ou commence, selon la quantité de café restante.

Ce que canmount contrôle réellement

canmount répond à une seule question : « Ce dataset est-il éligible pour être monté ? » Il ne choisit pas le point de montage ; c’est mountpoint. Il ne décide pas non plus si le système tentera de le monter au démarrage ; c’est un mélange de canmount, mountpoint et de la logique du service de montage ZFS de votre OS. Mais quand les choses tournent mal, la présence ou l’absence de canmount fait souvent la différence entre un démarrage propre et une chasse au trésor dans /mnt.

Voici pourquoi cette propriété a une importance opérationnelle :

  • Les datasets ZFS sont des systèmes de fichiers montables par défaut. C’est une fonctionnalité… jusqu’à ce que cela devienne une source d’erreurs.
  • ZFS monte volontiers les datasets dans l’ordre des dépendances, mais il peut quand même faire quelque chose que vous n’aviez pas prévu — surtout si vous avez cloné, rollbacké, renommé ou importé des pools dans un nouveau contexte.
  • Les agencements de type « environnement de démarrage » (communs sur illumos, FreeBSD et certaines configurations Linux) s’appuient sur canmount=noauto à des endroits précis pour empêcher la mauvaise racine de s’auto-monter.

Une vérité opérationnelle : ZFS ne connaît pas votre fenêtre de maintenance. Si vous laissez un dataset éligible au montage avec un mountpoint qui pointe vers un chemin critique, ZFS considérera cela comme une requête parfaitement raisonnable. C’est comme donner un steak à votre chien et espérer qu’il attende l’heure du dîner.

Faits intéressants et contexte historique

Quelques points de contexte qui font de canmount moins un bouton aléatoire et plus une cicatrice utile :

  1. ZFS a été conçu avec l’idée que « les systèmes de fichiers sont peu coûteux ». C’est pourquoi les datasets se multiplient — par service, par locataire, par application — et pourquoi le comportement de montage a besoin de garde-fous.
  2. Les premières déploiements ZFS ont appris à la dure que le montage automatique est merveilleux… jusqu’à ce que vous introduisiez des clones, des snapshots et des racines alternatives (altroot) lors d’une récupération.
  3. La notion d’« environnements de démarrage » (plusieurs racines sur le même pool) a popularisé le fait de régler les racines sur canmount=noauto afin que le système puisse choisir laquelle devient /.
  4. L’héritage des mountpoints est une fonctionnalité pratique de ZFS, mais elle devient dangereuse quand un dataset enfant hérite d’un mountpoint que vous ne vouliez pas actif sur cet hôte.
  5. La logique de montage ZFS a historiquement varié selon les plateformes (SMF de Solaris/illumos, rc de FreeBSD, unités systemd sur Linux), mais canmount reste l’interrupteur portable « ne montez pas ceci sauf si je l’ai demandé ».
  6. Parce que ZFS stocke les propriétés sur disque, un pool importé sur une machine différente apporte avec lui ses intentions de montage. C’est un cadeau pour la cohérence et une malédiction pour les montages surprises.
  7. Les clones et les datasets promus peuvent conserver des propriétés de point de montage qui avaient du sens dans leur environnement d’origine, pas dans celui où vous venez de les déplacer.
  8. Les « points de montage masqués » (un dataset montant par-dessus un répertoire qui a déjà du contenu) ont été une source récurrente d’incidents silencieux parce que le système semble sain alors qu’il lit les mauvais fichiers.

Un modèle mental pratique : éligibilité au montage vs emplacement du montage

Si vous ne retenez rien d’autre de cet article, retenez ceci : le montage ZFS est une décision en deux parties.

1) Éligibilité : « Puis-je monter ce dataset ? »

C’est canmount. Si canmount=off, la réponse est « non, jamais ». Si canmount=noauto, la réponse est « pas automatiquement ». Si canmount=on, la réponse est « oui ».

2) Emplacement : « Où cela serait-il monté ? »

C’est mountpoint. Ce peut être un chemin réel comme /var, cela peut être hérité, cela peut être legacy (ce qui signifie « le montage est géré par /etc/fstab ou équivalent »), ou cela peut être none.

Dans la vraie vie, les catastrophes surviennent quand l’éligibilité et l’emplacement s’alignent accidentellement sur quelque chose d’important. Par exemple :

  • Un clone d’un dataset racine est éligible (canmount=on) et a un mountpoint de /. Ce n’est pas un système de fichiers ; c’est une tentative de coup d’État.
  • Un dataset destiné au travail de récupération hérite de mountpoint=/var et est éligible au montage. Soudain, votre dataset de « récupération » devient votre /var de production.

Blague #1 : ZFS ne « vole » pas vos points de montage. Il les emprunte juste pour une longue promenade et revient habillé avec vos vêtements.

Les trois valeurs : on, off, noauto

canmount prend généralement ces valeurs :

canmount=on

Le dataset peut être monté, et sera généralement monté automatiquement s’il a un mountpoint valide et si le service de montage ZFS de votre OS est en marche. C’est ce que vous voulez pour des datasets normaux comme pool/home ou pool/var.

canmount=off

Le dataset ne peut pas être monté du tout. Vous pourrez toujours faire des snapshots, le répliquer, et y accéder par d’autres moyens (comme monter des snapshots ailleurs), mais ZFS ne le montera pas comme système de fichiers. C’est idéal pour des datasets « conteneurs » qui existent seulement pour contenir des enfants ou pour porter des propriétés héritées.

Usage courant : un dataset de niveau supérieur qui existe uniquement pour contenir des enfants et définir des propriétés communes :

  • pool/ROOT avec canmount=off, les enfants sont des environnements de démarrage
  • pool/containers avec canmount=off, les enfants sont des datasets par conteneur

canmount=noauto

Le dataset peut être monté, mais il ne sera pas monté automatiquement. C’est le réglage « ne te monte pas tout seul » pour les environnements de démarrage. Ce n’est pas « off », c’est « garde tes mains à toi sauf si je te le demande ».

Il est particulièrement utile pour les datasets qui ne doivent être montés que dans un contexte spécifique :

  • Un environnement de démarrage sélectionné par le chargeur d’amorçage comme racine
  • Un dataset monté temporairement pour une analyse forensique
  • Un dataset utilisé pour le chroot de maintenance, où l’auto-montage pourrait entrer en collision avec la racine active

Blague #2 : canmount=noauto est le réglage « je ne suis pas antisocial, je ne viens juste pas à ta réunion ».

D’où viennent les surprises au démarrage

La plupart des surprises ZFS au démarrage ne viennent pas d’un « ZFS cassé ». Elles viennent d’un « ZFS a fait ce qu’on lui a dit, et on a oublié ce qu’on lui a dit le trimestre dernier. » Causes récurrentes :

Surprise #1 : Un dataset monte par-dessus un répertoire qui contient déjà des fichiers critiques

C’est l’ombre de point de montage (mountpoint shadowing). Le répertoire sous-jacent existe toujours, mais vous ne pouvez pas le voir parce qu’un système de fichiers est monté au-dessus. Si le répertoire contenait des fichiers de configuration (par exemple sous /etc dans un scénario chroot, ou /var/lib pour une base de données), vous pouvez démarrer et fonctionner — mais avec un ensemble de fichiers différent de celui attendu.

Surprise #2 : Les pools importés apportent leurs mountpoints

Les propriétés ZFS vivent avec le pool. Si vous importez un pool depuis un autre hôte, il peut arriver avec des mountpoints comme /data, /var, ou quelque chose d’effrayant comme /. Sur un système avec montage automatique, cela peut transformer « je branche juste un disque » en « pourquoi SSH a-t-il arrêté ? »

Surprise #3 : Les environnements de démarrage se multiplient et l’un se monte alors qu’il ne devrait pas

Un design propre d’environnement de démarrage s’attend à ce que seul l’EB sélectionné soit monté comme racine. Si d’anciens EB sont éligibles et ont des mountpoints significatifs, ils peuvent aussi se monter, parfois sous le même arbre de répertoires, causant des conflits et des vues partielles étranges du système de fichiers.

Surprise #4 : Vous avez « optimisé » les montages et changé l’ordre par accident

L’ordre de montage compte quand des services s’attendent à ce que des répertoires existent avant leur démarrage. Un dataset montant tard peut amener des services à créer des répertoires sur le système de fichiers sous-jacent, puis plus tard ZFS monte dessus, masquant ces répertoires nouvellement créés. Le service continue tranquillement, écrivant maintenant dans un emplacement différent de celui créé au démarrage. C’est ainsi que l’on obtient « mes logs ont disparu » alors qu’en réalité « mes logs sont sous le mountpoint, pas dedans ».

Trois mini-récits du monde de l’entreprise

Mini-récit 1 : Un incident causé par une mauvaise hypothèse

Dans une entreprise de taille moyenne avec une configuration ZFS-on-Linux assez standard, l’équipe stockage maintenait un « pool utilitaire » utilisé pour les backups et les restaurations ponctuelles. Le pool était normalement importé uniquement sur un hôte de récupération dédié. Un vendredi, quelqu’un a connecté les étagères de sauvegarde à un serveur applicatif de production pour des restaurations plus rapides — temporairement, ont-ils dit, avec cette confiance particulière qui précède toujours un ticket qui devient incident.

L’hypothèse était simple : « Importer le pool ne changera rien à moins que l’on monte quelque chose. » Mais les datasets du pool avaient des mountpoints comme /var/lib/postgresql et /srv d’une vie antérieure. Ces datasets étaient toujours en canmount=on, parce que pourquoi ne le seraient-ils pas ? L’hôte de production a importé le pool, les unités systemd de montage ZFS ont fait leur travail, et soudain le serveur applicatif avait un nouveau /srv et un nouveau /var/lib/postgresql — juste au-dessus des anciens.

L’application n’a pas planté immédiatement. C’était la partie cruelle. Elle a continué à tourner, lisant certains fichiers depuis les datasets nouvellement montés tout en gardant des descripteurs de fichiers ouverts vers les anciens. Puis la base de données a redémarré pendant une rotation des logs (comme les bases le font quand on les titille indirectement), et elle est revenue en regardant le mauvais répertoire de données. De l’extérieur, cela ressemblait à une perte de données spontanée. En interne, c’étaient deux datasets se disputant le même espace de noms.

La correction fut ennuyeuse : exporter le pool, définir les datasets de haut niveau sur canmount=off ou mountpoint=none, et ré-importer avec un altroot lors des opérations de restauration. La leçon est restée : importer n’est pas un acte passif quand l’auto-mount est activé ; c’est un événement de déploiement.

Mini-récit 2 : Une optimisation qui s’est retournée contre l’équipe

Une grande entreprise a standardisé des datasets par service : pool/var, pool/var/log, pool/var/lib, et plus profond. Cela rendait quotas et snapshots propres. Cela rendait aussi le démarrage dépendant d’une longue chaîne de montages. Lors d’un effort de performance, quelqu’un a essayé de « simplifier » en effondrant des mountpoints et en comptant sur l’héritage pour réduire la prolifération des propriétés.

Le changement semblait propre en revue : mettre mountpoint=/var sur un dataset parent, faire hériter quelques enfants. Mais un enfant avait été volontairement réglé en canmount=noauto car il servait à une migration progressive : il contenait des données en attente et n’était monté que pendant la bascule. Dans le nouveau schéma d’héritage, cet enfant a commencé à hériter des changements de mountpoint tout en conservant sa valeur canmount — et quelqu’un l’a ensuite basculé en canmount=on pour « tester quelque chose ».

Des semaines plus tard, pendant un reboot, le dataset préparé s’est monté automatiquement et a masqué un répertoire sous /var/lib qu’un service utilisait pour son état. Le service a démarré proprement, créé de l’état sur le système de fichiers sous-jacent avant que le montage ne soit effectif (le timing compte), puis ZFS est monté par-dessus. L’état a « disparu ». Le service est entré dans une voie d’initialisation et a commencé à re-seeder des données depuis des systèmes en amont, ce qui a provoqué des pics de charge et une cascade de throttling. Ce n’était pas une seule défaillance ; c’était un test de système distribué auto-infligé.

Le postmortem fut sans concession : l’optimisation avait économisé presque aucun effort opérationnel mais avait retiré une sécurité critique. La remédiation fut de marquer par politique les datasets « conteneurs » en canmount=off, et de traiter les changements de canmount comme des modifications à haut risque nécessitant un test de reboot en staging. Le retour de bâton n’était pas la complexité de ZFS — c’était d’oublier que le démarrage est le workflow le plus sensible au timing.

Mini-récit 3 : Une pratique ennuyeuse mais correcte qui a sauvé la mise

Une autre organisation faisait tourner des pools ZFS sur une flotte de serveurs de build. Ces hôtes étaient constamment ré-imagés, mais les pools bougeaient parfois entre machines quand le matériel était réaffecté. L’équipe SRE avait une pratique que personne ne célébrait : chaque import de pool pour des pools non-root se faisait avec altroot, et chaque dataset de haut niveau utilisé pour organiser des sous-datasets avait canmount=off et mountpoint=none.

Un jour, un hôte a rendu l’âme et son pool a été attaché à un autre serveur pour récupérer des artefacts de build. Le pool contenait des datasets avec des mountpoints qui, sur leur machine d’origine, pointaient sous /srv/build. Sur l’hôte de récupération, /srv existait déjà et servait à autre chose. Sans garde-fous, cela aurait été le classique « pourquoi mon service s’est reconfiguré ».

Au lieu de cela, l’import ressemblait à ceci : zpool import -o altroot=/mnt/recovery poolname. Tous les mountpoints furent relocalisés sous /mnt/recovery. Les datasets qui ne devaient pas se monter restèrent inéligibles. L’équipe monta seulement ce dont elle avait besoin, copia les artefacts, et exporta le pool. Aucun service n’a cligné.

Cette pratique n’a jamais fait l’objet d’une présentation. Elle a cependant empêché une action de récupération de devenir un incident. En opérations de production, « ennuyeux » n’est pas une insulte ; c’est un KPI.

Tâches pratiques (commandes + interprétation)

Ce sont des tâches que vous pouvez exécuter aujourd’hui sur un système ZFS. Les commandes sont écrites pour le comportement CLI typique d’OpenZFS. Ajustez les noms de pool/dataset pour votre environnement.

Task 1: List datasets with mount behavior in one view

cr0x@server:~$ zfs list -o name,canmount,mountpoint,mounted -r tank
NAME                 CANMOUNT  MOUNTPOINT         MOUNTED
tank                 on        /tank             yes
tank/ROOT            off       none              no
tank/ROOT/default    noauto    /                 yes
tank/var             on        /var              yes
tank/var/log         on        /var/log          yes

Interprétation : Cherchez les datasets qui sont canmount=on (ou accidentellement noauto) avec des mountpoints qui entrent en collision avec des chemins critiques. tank/ROOT en off et none est un bon exemple de pattern « dataset conteneur » sain.

Task 2: Find “eligible to mount” datasets that are currently not mounted

cr0x@server:~$ zfs list -H -o name,canmount,mountpoint,mounted -r tank | awk '$2=="on" && $4=="no" {print}'
tank/tmp	on	/tmp	no

Interprétation : Un dataset éligible mais non monté peut être acceptable (peut-être nouveau ou volontairement démonté), mais c’est aussi une surprise au démarrage en attente si son mountpoint est sensible et si le service de montage change de comportement.

Task 3: Identify datasets with mountpoint=legacy

cr0x@server:~$ zfs list -H -o name,mountpoint -r tank | awk '$2=="legacy" {print}'
tank/oldroot	legacy

Interprétation : legacy signifie que votre OS s’appuiera sur /etc/fstab (ou équivalent) pour le montage. Mélanger les montages legacy avec l’auto-montage ZFS est parfois nécessaire, mais cela augmente le nombre de chemins de démarrage que vous devez raisonner.

Task 4: Check property inheritance (the “why did this happen” tool)

cr0x@server:~$ zfs get -r -o name,property,value,source canmount,mountpoint tank/var
NAME         PROPERTY   VALUE       SOURCE
tank/var     canmount   on          local
tank/var     mountpoint /var        local
tank/var/log canmount   on          inherited from tank/var
tank/var/log mountpoint /var/log    local

Interprétation : Le champ source vous indique si vous avez affaire à un override local ou à de l’héritage. La plupart des mystères « ça s’est monté quelque part de bizarre » se résolvent en remarquant qu’un enfant a hérité d’une propriété que vous supposiez locale ailleurs.

Task 5: Make a container dataset non-mountable (safe default)

cr0x@server:~$ sudo zfs set canmount=off tank/containers
cr0x@server:~$ sudo zfs set mountpoint=none tank/containers
cr0x@server:~$ zfs get -o name,property,value tank/containers canmount,mountpoint
NAME            PROPERTY   VALUE  SOURCE
tank/containers canmount   off    local
tank/containers mountpoint none   local

Interprétation : Cela empêche le montage accidentel du parent tout en permettant aux enfants comme tank/containers/app1 de se monter là où ils doivent l’être.

Task 6: Mark a boot environment dataset as “mount only when selected”

cr0x@server:~$ sudo zfs set canmount=noauto tank/ROOT/be-2025q4
cr0x@server:~$ zfs get -o name,property,value tank/ROOT/be-2025q4 canmount
NAME               PROPERTY  VALUE   SOURCE
tank/ROOT/be-2025q4 canmount  noauto  local

Interprétation : Un environnement de démarrage ne devrait généralement pas s’auto-monter comme système de fichiers régulier après import ; il doit être monté comme racine seulement lorsqu’il est choisi par le processus de boot. noauto est le garde-fou.

Task 7: Temporarily mount a noauto dataset for inspection

cr0x@server:~$ sudo zfs mount tank/ROOT/be-2025q4
cr0x@server:~$ zfs list -o name,mountpoint,mounted tank/ROOT/be-2025q4
NAME               MOUNTPOINT  MOUNTED
tank/ROOT/be-2025q4 /          yes

Interprétation : Si le dataset a mountpoint=/, le monter sur un système en production est dangereux sauf si vous utilisez aussi un root alternatif (voir la tâche suivante). En pratique, pour les BE, vous montez typiquement sous un chemin d’altroot sûr, pas sur /.

Task 8: Import a pool safely using altroot (recovery best practice)

cr0x@server:~$ sudo zpool export backup
cr0x@server:~$ sudo zpool import -o altroot=/mnt/recovery backup
cr0x@server:~$ zfs mount | head
backup/ROOT/be-old  /mnt/recovery/     

Interprétation : altroot préfixe les mountpoints avec un préfixe sûr, donc un dataset dont le mountpoint est /var devient /mnt/recovery/var. C’est ainsi que vous empêchez le travail de récupération de marcher sur des chemins de production.

Task 9: Unmount a dataset that mounted somewhere it shouldn’t

cr0x@server:~$ zfs list -o name,mountpoint,mounted | grep '/var/lib/postgresql'
backup/pgdata  /var/lib/postgresql  yes
cr0x@server:~$ sudo zfs unmount backup/pgdata
cr0x@server:~$ zfs list -o name,mountpoint,mounted backup/pgdata
NAME         MOUNTPOINT            MOUNTED
backup/pgdata /var/lib/postgresql  no

Interprétation : Le démontage stoppe l’hémorragie immédiate, mais n’empêche pas un remontage au prochain reboot. Pour cela, il faut corriger canmount et/ou mountpoint.

Task 10: Prevent remounting by setting canmount=off

cr0x@server:~$ sudo zfs set canmount=off backup/pgdata
cr0x@server:~$ zfs get -o name,property,value backup/pgdata canmount
NAME         PROPERTY  VALUE  SOURCE
backup/pgdata canmount  off    local

Interprétation : Cela rend le dataset inéligible au montage. Si vous avez toujours besoin d’accéder à son contenu, vous pouvez le remettre temporairement ou utiliser une autre technique de récupération, mais vous avez rendu le défaut sûr.

Task 11: Fix a dangerous mountpoint without changing eligibility

cr0x@server:~$ sudo zfs set mountpoint=/mnt/pgdata-staging backup/pgdata
cr0x@server:~$ sudo zfs set canmount=on backup/pgdata
cr0x@server:~$ sudo zfs mount backup/pgdata
cr0x@server:~$ zfs list -o name,mountpoint,mounted backup/pgdata
NAME         MOUNTPOINT            MOUNTED
backup/pgdata /mnt/pgdata-staging  yes

Interprétation : Parfois vous voulez qu’il soit montable, juste pas au-dessus d’un répertoire de production. Déplacer le mountpoint est généralement plus sûr que de compter sur « personne n’importera ce pool ici ».

Task 12: Detect shadowing risk (datasets mounted under critical trees)

cr0x@server:~$ zfs list -H -o name,mountpoint,canmount -r tank | awk '$2 ~ "^/(etc|var|usr|srv|home)($|/)" {print}'
tank/var	/var	on
tank/var/log	/var/log	on
tank/home	/home	on

Interprétation : Ce n’est pas forcément « mauvais ». C’est un inventaire des datasets qui vivent sous des arbres critiques. Toute entrée inattendue ici mérite d’être investiguée avant le prochain reboot.

Task 13: Verify what ZFS thinks is currently mounted

cr0x@server:~$ zfs mount | sed -n '1,8p'
tank                           /tank
tank/var                       /var
tank/var/log                   /var/log
tank/home                      /home

Interprétation : C’est la vue de ZFS, pas nécessairement toute la table de montage du système. Pourtant, c’est la liste faisant foi des systèmes de fichiers montés par ZFS.

Task 14: Cross-check against the kernel mount table

cr0x@server:~$ mount | grep ' type zfs ' | head
tank/var on /var type zfs (rw,xattr,noacl)
tank/var/log on /var/log type zfs (rw,xattr,noacl)

Interprétation : Si zfs mount et mount divergent, vous êtes dans un territoire d’edge-case : peut-être un montage échoué, peut-être un montage legacy, peut-être un problème de namespace dans des conteneurs.

Task 15: Find datasets that will try to mount but have mountpoint=none

cr0x@server:~$ zfs list -H -o name,canmount,mountpoint -r tank | awk '$2=="on" && $3=="none" {print}'
tank/ROOT	on	none

Interprétation : C’est généralement une mauvaise odeur de configuration. Si un dataset peut se monter mais n’a pas d’endroit où aller, c’est probablement un dataset conteneur qui devrait être canmount=off.

Task 16: Audit recent changes by comparing local vs inherited properties

cr0x@server:~$ zfs get -r -o name,property,value,source canmount,mountpoint tank | awk '$4=="local" {print}' | head
tank            mountpoint  /tank   local
tank/ROOT       canmount    off     local
tank/ROOT       mountpoint  none    local
tank/var        mountpoint  /var    local

Interprétation : Les propriétés « local » sont les endroits où les surprises ont tendance à être introduites. Cela vous donne une courte liste d’endroits où des humains ont touché aux choses.

Playbook de diagnostic rapide

Voici le workflow « il est 03:12 et l’hôte n’est pas revenu correctement ». Il suppose que vous suspectez des problèmes de montage ZFS. L’objectif est d’identifier rapidement le goulot d’étranglement : est-ce un problème d’import, d’éligibilité au montage, de conflit de mountpoint, ou d’ordre de démarrage des services ?

First: confirm pool import and basic health

cr0x@server:~$ sudo zpool status
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 00:10:12 with 0 errors on Sun Dec 22 02:00:03 2025
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          sda3      ONLINE       0     0     0

Interprétation : Si le pool n’est pas importé ou est dégradé/faulted, arrêtez de courir après les mountpoints. Corrigez d’abord l’import/les périphériques. Les surprises de montage sont en aval d’un « pool utilisable ».

Second: look for obvious mount collisions and eligibility traps

cr0x@server:~$ zfs list -o name,canmount,mountpoint,mounted -r tank | egrep ' /(var|etc|usr|srv|home|)($|/)'

Interprétation : Vous scannez pour des datasets inattendus avec des mountpoints critiques. Faites attention à tout ce qui monte sur /, /var, ou /usr que vous n’avez pas explicitement conçu.

Third: verify what is mounted and what is missing

cr0x@server:~$ zfs mount
cr0x@server:~$ mount | grep ' type zfs '

Interprétation : Si des datasets attendus manquent, vérifiez canmount et mountpoint. Si des datasets inattendus sont présents, vérifiez l’import de pools étrangers ou des environnements de démarrage rendus éligibles par erreur.

Fourth: inspect inheritance and property source for the weird dataset

cr0x@server:~$ zfs get -o name,property,value,source canmount,mountpoint tank/suspect
NAME         PROPERTY   VALUE  SOURCE
tank/suspect canmount   on     inherited from tank
tank/suspect mountpoint /var   local

Interprétation : Un mountpoint local avec une éligibilité héritée est un pattern classique pour les surprises : quelqu’un a changé où ça se monte, mais pas si cela doit être éligible.

Fifth: if this is a recovery import, stop and use altroot

Si vous avez importé un pool non natif et qu’il commence à se monter dans des chemins de production, la correction consiste généralement à exporter et ré-importer avec un root alternatif.

cr0x@server:~$ sudo zpool export backup
cr0x@server:~$ sudo zpool import -o altroot=/mnt/recovery backup

Interprétation : Cela convertit un « montage surprise » en « tous les montages sont enfermés sous /mnt/recovery », ce qui est la posture correcte pendant la réponse à incident.

Erreurs courantes : symptômes et fixes

Mistake 1: Leaving container datasets as canmount=on

Symptôme : Un dataset parent comme tank/ROOT apparaît monté quelque part, ou tente de se monter et génère des avertissements confus.

Pourquoi cela arrive : Quelqu’un l’a créé et n’a jamais changé le comportement par défaut. Il hérite d’un mountpoint, ou un mountpoint a été défini lors d’expérimentations.

Correction : Rendez-le explicitement non-montable et retirez-lui un mountpoint plausible.

cr0x@server:~$ sudo zfs set canmount=off tank/ROOT
cr0x@server:~$ sudo zfs set mountpoint=none tank/ROOT

Mistake 2: Confusing canmount=noauto with “disabled”

Symptôme : Un dataset se monte de façon inattendue après un zfs mount -a manuel ou des automatisations, ou un opérateur le monte sans réaliser que c’est un dataset de type BE/racine.

Pourquoi cela arrive : noauto n’empêche pas le montage ; il empêche le montage automatique. Les humains et les scripts peuvent toujours le monter.

Correction : Si vous ne voulez vraiment jamais qu’il soit monté, utilisez canmount=off. Si vous voulez qu’il soit montable seulement dans des contextes isolés, combinez noauto avec des imports altroot et des outils prudents.

cr0x@server:~$ sudo zfs set canmount=off tank/archive/never-mount

Mistake 3: Importing a foreign pool without altroot

Symptôme : Après l’import d’un pool, des services se comportent bizarrement, des répertoires de données « changent », ou des chemins comme /srv contiennent soudainement un contenu différent.

Pourquoi cela arrive : Les datasets du pool se montent là où ils étaient configurés pour se monter, et votre hôte respecte cela.

Correction : Exportez immédiatement, puis ré-importez avec altroot. Optionnellement, définissez canmount=off ou mountpoint=none sur les datasets de haut niveau avant de les déplacer.

cr0x@server:~$ sudo zpool export foreignpool
cr0x@server:~$ sudo zpool import -o altroot=/mnt/foreign foreignpool

Mistake 4: Setting mountpoint=/ on multiple datasets (boot environment chaos)

Symptôme : Le boot tombe sur un shell d’urgence, ou le filesystem racine n’est pas celui attendu. Après le démarrage, vous voyez plusieurs datasets ressemblant à des BE qui pourraient logiquement monter comme racine.

Pourquoi cela arrive : Clonage ou renommage de BE sans mise à jour des propriétés de montage et d’éligibilité.

Correction : Assurez-vous que seule la racine sélectionnée est montée comme / au démarrage. Dans beaucoup de schémas BE, les racines non sélectionnées devraient être en canmount=noauto. Les ROOT conteneurs devraient être en off.

cr0x@server:~$ zfs list -o name,canmount,mountpoint -r tank/ROOT
cr0x@server:~$ sudo zfs set canmount=noauto tank/ROOT/oldbe

Mistake 5: Relying on “mounted=no” as proof it won’t mount at next boot

Symptôme : Tout semble correct maintenant, mais après un reboot le dataset se monte à nouveau et répète le problème.

Pourquoi cela arrive : mounted est un état, pas une politique. canmount et mountpoint sont des politiques.

Correction : Définissez la politique explicitement.

cr0x@server:~$ sudo zfs set canmount=off pool/surprise
cr0x@server:~$ sudo zfs set mountpoint=none pool/surprise

Mistake 6: Mixing legacy mounts and ZFS auto-mount without a clear contract

Symptôme : Certains datasets se montent deux fois, ou des montages échouent de façon intermittente au démarrage, ou vous voyez un état contradictoire entre zfs mount et mount.

Pourquoi cela arrive : Vous avez réparti la responsabilité entre ZFS et le système de montage de l’OS, mais personne n’a écrit quel dataset est géré où.

Correction : Soit remettez ces datasets sous gestion ZFS, soit engagez-vous pleinement sur des montages legacy pour eux et assurez-vous que canmount est aligné sur ce choix. Au minimum, auditez les datasets mountpoint=legacy et gardez-les intentionnels.

Listes de vérification / plan étape par étape

Checklist A: Designing a dataset hierarchy that won’t surprise you at boot

  1. Créer des datasets conteneurs pour le groupement et l’héritage de propriétés (compression, atime, recordsize).
  2. Définir immédiatement les datasets conteneurs sur canmount=off et mountpoint=none.
  3. Définir des mountpoints explicites seulement sur les datasets qui doivent se monter en fonctionnement normal.
  4. Utiliser canmount=noauto pour les environnements de démarrage ou les datasets qui ne doivent être montés que par action explicite.
  5. Éviter les mountpoints ambigus (surtout /, /var, /usr) sauf là où c’est conçu intentionnellement.
  6. Documenter une invariant : « Seuls ces datasets peuvent se monter sous les chemins critiques », et l’appliquer par des audits périodiques.

Checklist B: Before importing a pool from another system

  1. Décidez du répertoire de staging sûr (par ex., /mnt/recovery).
  2. Importer avec altroot.
  3. Lister les mountpoints et les valeurs canmount avant de monter quoi que ce soit explicitement.
  4. Si vous devez rendre le pool portable, définissez les datasets de haut niveau sur canmount=off et mountpoint=none avant l’export.

Checklist C: Before rebooting after storage changes

  1. Exécuter zfs list -o name,canmount,mountpoint,mounted -r pool et scanner les chemins critiques.
  2. Confirmer qu’aucun dataset inattendu n’a mountpoint=/.
  3. Confirmer que les datasets conteneurs ne sont pas montables.
  4. Si vous avez modifié l’héritage, vérifiez avec zfs get ... source pour savoir ce qui va se propager.
  5. Si possible, faites un reboot contrôlé en staging d’abord. Les problèmes d’ordre de démarrage apparaissent rarement sur un système en cours d’exécution.

FAQ

1) What’s the difference between canmount=off and mountpoint=none?

canmount=off rend le dataset inéligible au montage. mountpoint=none signifie « il n’a pas de cible de montage ». En production, les datasets conteneurs reçoivent souvent les deux : ils ne doivent pas se monter, et ils ne doivent pas non plus avoir une cible plausible.

2) If I set canmount=noauto, can the dataset still mount at boot?

En général, noauto empêche ZFS de le monter automatiquement. Mais les environnements de démarrage sont spéciaux : le processus de boot peut monter un dataset noauto comme racine parce qu’il est explicitement sélectionné. De plus, un admin ou un script peut toujours le monter manuellement.

3) Why did a dataset mount after I imported a pool? I didn’t run zfs mount.

Sur beaucoup de systèmes, l’import de pool déclenche les services de montage ZFS qui montent les datasets éligibles avec des mountpoints valides. L’import n’est pas « juste brancher du stockage » ; c’est « activer les intentions de système de fichiers du pool ». Utilisez zpool import -o altroot=... pour les pools étrangers.

4) Can I use canmount=off on a dataset that has children?

Oui. Les enfants peuvent toujours se monter normalement. C’est une bonne pratique pour des parents/datasets conteneurs comme pool/ROOT ou pool/containers.

5) How do I tell if a surprising mount is due to inheritance?

Utilisez zfs get ... source. Si canmount ou mountpoint affiche « inherited from … » vous avez une histoire d’héritage, pas un comportement aléatoire.

6) Is canmount the same as setting readonly=on?

Non. readonly=on monte toujours le dataset, mais en lecture seule. canmount=off empêche le montage entièrement. En scénario de récupération, vous pourriez utiliser les deux : garder des datasets montables mais en lecture seule, ou les garder non montables jusqu’à ce que vous soyez prêt.

7) What’s the safest way to inspect a boot environment dataset that has mountpoint=/?

Importez le pool avec altroot (ou montez le dataset sous un répertoire sûr en changeant temporairement son mountpoint). L’essentiel : ne montez pas un dataset « root » sur / d’un système en cours d’exécution à moins que vous aimiez vivre dangereusement.

8) Should I set canmount=noauto for all datasets and mount them via systemd/fstab instead?

Vous pouvez, mais vous échangez une source de vérité contre deux. Les montages gérés par ZFS sont généralement plus simples si vous vous y engagez. Réservez noauto aux cas spéciaux : environnements de démarrage, datasets de staging, et workflows de maintenance.

9) Why do I see datasets with canmount=on but mounted=no?

L’éligibilité ne garantit pas qu’il est monté actuellement. Le montage peut avoir échoué, le mountpoint peut ne pas exister, le pool peut avoir été importé avec -N (ne pas monter), ou le service de montage de l’OS n’a pas encore démarré. Traitez cela comme un indice, pas comme un verdict.

Conclusion

canmount n’est pas glamour. Il ne compresse pas les données, il n’accélère pas les lectures, et il n’impressionnera personne dans une démo produit. Ce qu’il fait, c’est prévenir les types de surprises au démarrage et à l’import qui vous font perdre des matinées entières et vous laissent le sentiment étrange que les systèmes de fichiers sont sensibles.

Si vous utilisez ZFS en production, faites-en une habitude : les datasets conteneurs sont canmount=off ; les datasets à usage spécial sont canmount=noauto ; tout le reste est explicitement conçu pour être on avec un mountpoint que vous pouvez défendre en postmortem. Les meilleurs incidents de stockage sont ceux que vous n’avez jamais à nommer.

← Précédent
La décision d’IBM qui a créé l’industrie des clones
Suivant →
Ubuntu 24.04 : disque « bloqué » sous charge — réglages de timeout qui évitent les blocages complets (cas n°90)

Laisser un commentaire