ZFS zfs receive : le côté import qui casse si vous ignorez les propriétés

Cet article vous a aidé ?

Si zfs send est le service d’export, zfs receive c’est la douane : tout semble en ordre jusqu’à ce que quelqu’un vérifie la paperasse. Les propriétés sont cette paperasse. Les ignorer ne donne pas seulement une importation un peu désordonnée — vous obtenez des jeux de données montés là où ils ne devraient pas l’être, des clés de chiffrement impossibles à charger, des réplications « réussies » qui ne démarrent pas, et des performances qui ressemblent à un système de stockage en pleine danse interprétative.

J’ai géré des réplications ZFS dans des entreprises routinières, des boutiques SaaS en panique, et ce genre de sous-sol corporate où « plan DR » signifie « espérer ». Le schéma se répète : les équipes traitent receive comme un tuyau passif. Ce n’en est pas un. zfs receive est l’endroit où ZFS décide de ce que vos données signifient sur la cible : où elles vont se monter, comment elles seront compressées, si elles seront lisibles, et ce qui arrive quand le flux contient des propriétés auxquelles vous n’aviez pas pensé.

Ce que zfs receive fait réellement (et pourquoi les propriétés sont le piège)

zfs receive prend un flux de réplication et reconstitue des jeux de données, des snapshots et éventuellement des propriétés. Cette dernière partie est le tueur silencieux : les propriétés ne sont pas juste des trucs agréables comme « compression=lz4 ». Elles incluent les points de montage, le comportement canmount, les métadonnées de chiffrement, les quotas, les réservations, le recordsize, le mode ACL, le stockage des xattr, et une pile de choses spécifiques à la plateforme qui peuvent ne pas signifier la même chose sur l’OS cible.

La mauvaise hypothèse est que les flux de réplication sont « uniquement des données ». En réalité, un flux ZFS typique peut inclure :

  • Les blocs de contenu du dataset.
  • Les métadonnées des snapshots.
  • Les propriétés du dataset (selon les options de send et l’implémentation ZFS).
  • Les flags de fonctionnalités et les attentes de compatibilité.

Puis zfs receive applique le flux dans un espace de noms du pool existant, où les politiques locales de la cible (et les datasets déjà présents) peuvent être en désaccord avec la source.

En production, « être en désaccord » ne veut pas dire un avertissement sympa. Ça veut dire :

  • Un dataset se monte sur un répertoire réel et masque des données dont vous aviez besoin.
  • Une propriété héritée sur la source devient locale sur la cible (ou vice versa) et vous passez des heures à chercher pourquoi le comportement a divergé.
  • Un dataset chiffré arrive sans le matériel de clé que vous attendiez.
  • La réplication « fonctionne » mais la restauration est cassée parce que vous avez répliqué un point de montage qui n’a de sens que sur l’hôte source.

Blague #1 : Traiter zfs receive comme « juste le côté import » revient à traiter les airbags comme « juste la partie gonflable ». On ne se rend compte qu’on a eu tort qu’à grande vitesse.

Faits et contexte historique qui comptent en production

  1. ZFS a été conçu avec la réplication comme primitive de première classe. Snapshots et send/receive faisaient partie de la vision Sun : transfert cohérent point-in-time sans gymnastique de gel du filesystem.
  2. Les propriétés font partie du contrat du filesystem. ZFS utilise les propriétés pour conduire des comportements que d’autres systèmes laissent aux options de mount ou à des outils externes.
  3. OpenZFS a divergé entre plateformes pendant des années. Le comportement autour des ACL, des xattr et de certains défauts de propriété peut varier entre illumos, FreeBSD et Linux — surtout dans les déploiements plus anciens.
  4. Les feature flags ont changé le risque d’upgrade. Les pools et datasets ont gagné des flags qui permettent des mises à niveau à chaud et une activation plus sûre, mais créent aussi des surprises « impossible à importer sur un système ancien » si vous répliquez sans vérifier.
  5. Le chiffrement est arrivé après les snapshots. Le chiffrement natif est une fonctionnalité moderne d’OpenZFS ; beaucoup d’environnements mélangent encore du chiffrement « legacy » (disques auto-chiffrants, LUKS) avec le chiffrement natif ZFS, ce qui affecte les hypothèses de réplication.
  6. lz4 est devenu le favori par défaut pour une raison. Il est assez rapide pour être « généralement activé » sans la honte opérationnelle de gaspiller du CPU lors des restaurations.
  7. Les resume tokens existent parce que les transferts longs échouent. La réplication ZFS à l’échelle rencontre des réseaux instables, des fenêtres de maintenance et de l’impatience humaine ; les resume tokens ont transformé un « recommencer » en « continuer ».
  8. mountpoint est une propriété, pas une option mount(8). Ce choix est puissant, mais signifie que la réplication peut transporter votre disposition de points de montage — que vous le vouliez ou non.
  9. Recordsize et volblocksize sont des leviers de performance avec conséquences. Ils peuvent faire voler des bases de données ou faire pleurer des workloads à petits fichiers, et la réplication peut transporter ces choix entre environnements involontairement.

Un modèle mental pratique : flux, dataset, propriétés et points de montage

Quand vous lancez :

cr0x@server:~$ zfs send poolA/app@weekly | zfs receive poolB/restore/app

vous ne « copiez pas des fichiers ». Vous appliquez un journal de transaction qui reconstruit un arbre de datasets. Cela a trois couches de conséquences :

1) Namespace : où le dataset atterrit-il ?

poolB/restore/app est le nom de dataset dans lequel vous recevez. Mais dans le flux, le(s) dataset(s) peuvent avoir leurs propres noms et propriétés, et si vous utilisez des flags de réplication qui incluent des datasets enfants, le côté receive peut créer tout un sous-arbre sous votre chemin cible.

2) Propriétés : quel comportement est emporté ?

Les propriétés peuvent être définies comme valeurs locales ou héritées. La réplication peut préserver ces choix. C’est bien quand vous voulez un miroir fidèle et terrible quand vous atterrissez les données dans un environnement opérationnel différent (DR, dev, analytics) avec des points de montage, quotas et réglages de performance différents.

3) Comportement de montage : quand et où ça se monte ?

Par défaut, un filesystem reçu peut se monter automatiquement (selon canmount, mountpoint et les flags de receive). Cela peut entrer en collision avec des chemins et des services existants. « Pourquoi mon application lit-elle soudain de l’ancienne config ? » est un thème de post-mortem fréquent.

Comportement des propriétés à la réception : héritées, locales et « surprise, ça a changé »

La clé pour rester sain d’esprit est de séparer deux questions :

  • Quelles propriétés sont dans le flux ? (décidé côté send, et par le fait que vous faites un raw send, un replication send, etc.)
  • Quelles propriétés sont appliquées sur la cible ? (décidé par les options de receive et l’arbre de datasets déjà présent sur la cible)

Propriétés qui causent souvent de vraies interruptions

  • mountpoint : réplique un chemin qui peut ne pas exister ou être utilisé sur la cible.
  • canmount : un dataset qui ne devrait pas se monter se monte (ou inversement), changeant ce que les services voient.
  • sharenfs / sharesmb : d’un coup vous exportez des données sur un réseau que vous ne vouliez pas toucher.
  • atime : change le churn des métadonnées ; peut transformer un workload lecture-intensif en écritures inattendues.
  • recordsize : inoffensif dans de nombreux cas, mais létal pour les BD et images VM quand c’est faux.
  • xattr et acltype : des différences cross-plateforme peuvent faire paraître les permissions « correctes » jusqu’à ce qu’une appli commence à échouer.
  • quota/reservation/refquota/refreservation : des limites répliquées peuvent bloquer une restauration en « remplissant » instantanément le dataset du point de vue de la cible.

Remplacer vs exclure des propriétés à la réception

Opérationnellement, vous voulez généralement un de ces schémas :

  • Miroir fidèle (DR miroir que vous pourriez promouvoir) : préserver les propriétés, mais assurer que la stratégie de namespace/montage est sûre (souvent recevoir avec -u puis définir explicitement les points de montage).
  • Zone d’atterrissage de données (analytics/dev/test) : remplacer les points de montage, désactiver les partages, et appliquer une politique de performance locale (compression, recordsize) adaptée à la cible.

Si votre ZFS supporte des flags d’exclusion/override de propriétés au receive (courant dans les OpenZFS modernes), ce sont vos rails de sécurité. Les flags exacts varient selon l’implémentation/version, donc validez dans votre environnement avec zfs receive -?. Dans cet article, je montrerai des schémas qui fonctionnent largement et indiquerai où les différences de version importent.

Chiffrement à la réception : clés, envois raw, et le genre « pourquoi je ne peux pas monter ça »

Le chiffrement natif ZFS introduit un concept que les administrateurs non chiffrés ratent souvent : le dataset peut exister et se répliquer parfaitement tout en étant inutilisable tant que les clés ne sont pas gérées correctement. Ce n’est pas un bug ; c’est le but.

Deux modes de réplication qui se comportent très différemment

Non-raw send (ou des envois qui déchiffrent/rechiffrent selon l’outil) peut permettre au receveur de stocker les données d’une manière qui n’est pas un clone chiffré octet-à-octet. Cela peut nécessiter des clés sur la source et peut exposer des propriétés que vous ne vouliez pas.

Raw send (zfs send -w dans beaucoup de builds OpenZFS) transmet le dataset chiffré sous forme de blocs chiffrés plus assez de métadonnées pour préserver le chiffrement. Le receveur n’a pas besoin du texte en clair, et la racine de chiffrement/paramètres sont préservés. C’est le mode voulu pour « répliquer vers un site DR non fiable » ou des configurations d’appliance de sauvegarde.

Gestion des clés sur la cible : un workflow opérationnel

Recevoir un dataset chiffré sans plan pour keylocation et keyformat est la façon dont vous créez le presse-papier le plus fiable du monde. Vous lancerez zfs list, verrez les datasets, et tout échouera au montage.

Blague #2 : Un dataset chiffré sans clé est la forme la plus pure de stockage en écriture seule. Les auditeurs adorent ; les utilisateurs moins.

Points de montage et automount : comment on s’auto-DOS avec succès

Le scénario le plus courant « receive a cassé la production » n’est pas une corruption. C’est une collision de montage. Un receive se produit, le dataset se monte automatiquement, et soudain :

  • /var sur la cible est masqué par un point de montage répliqué.
  • Un répertoire contenant des données vivantes est caché derrière le mount, faisant que les services lisent d’anciennes configs ou ne trouvent plus de fichiers.
  • Un environnement de restauration partage des données par NFS/SMB parce que les propriétés de partage ont été répliquées.

La correction est généralement simple — recevoir sans monter, puis définir explicitement les mountpoints et canmount avant de lancer les services. La partie difficile est de se souvenir de le faire quand vous êtes fatigué, qu’il est 02:00, et que la direction vous demande des ETA comme si c’était un sport.

Tâches opérationnelles réelles (commandes + interprétation)

Ci-dessous des tâches pratiques que j’ai réellement utilisées (ou vu quelqu’un souhaiter avoir utilisées) dans des workflows de réplication ZFS en production. Les commandes supposent un environnement OpenZFS typique ; adaptez les noms de pool/dataset à votre réalité.

Tâche 1 : Inspecter quelles propriétés importent sur la source avant d’envoyer

cr0x@src:~$ zfs get -r -o name,property,value,source mountpoint,canmount,compression,recordsize,quota,refquota,sharenfs,sharesmb poolA/app
NAME             PROPERTY     VALUE          SOURCE
poolA/app        mountpoint   /srv/app       local
poolA/app        canmount     on             default
poolA/app        compression  lz4            local
poolA/app        recordsize   128K           default
poolA/app        quota        none           default
poolA/app        sharenfs     off            default

Interprétation : Cela vous indique le comportement que vous vous apprêtez à répliquer. Si mountpoint est local et pointe vers un chemin qui n’existe pas sur la cible, décidez maintenant si vous allez l’outrepasser.

Tâche 2 : Simuler votre plan de receive en vérifiant le namespace et les conflits sur la cible

cr0x@dst:~$ zfs list -o name,mountpoint,canmount -r poolB/restore
NAME                 MOUNTPOINT        CANMOUNT
poolB/restore        /poolB/restore    on
poolB/restore/app    -                 -

Interprétation : Si poolB/restore/app existe déjà (ou si un parent a un mountpoint hérité qui causerait une collision), corrigez le namespace avant de recevoir.

Tâche 3 : Recevoir sans monter pour éviter les collisions

cr0x@src:~$ zfs send poolA/app@weekly | ssh dst 'zfs receive -u poolB/restore/app'

Interprétation : -u le laisse démonté même si le flux contient des réglages de mountpoint/canmount qui le monteraient normalement. C’est le défaut le plus sûr pour les restaurations et le seed DR.

Tâche 4 : Forcer un mountpoint sûr après receive (pattern landing zone)

cr0x@dst:~$ zfs set mountpoint=/srv/restore/app poolB/restore/app
cr0x@dst:~$ zfs set canmount=noauto poolB/restore/app
cr0x@dst:~$ zfs mount poolB/restore/app

Interprétation : Vous avez rendu le montage explicite et prévisible. noauto empêche les montages surprises au boot/import ; vous contrôlez quand les services voient les données.

Tâche 5 : Recevoir un sous-arbre complet (réplication) dans un chemin préfixé

cr0x@src:~$ zfs send -R poolA/app@weekly | ssh dst 'zfs receive -u poolB/restore'

Interprétation : Cela peut créer plusieurs datasets sous poolB/restore. Super pour des arbres applicatifs complets ; dangereux si vous n’attendiez pas d’enfants avec leurs propres mountpoints et propriétés de partage.

Tâche 6 : Valider les snapshots reçus et l’utilisation d’espace

cr0x@dst:~$ zfs list -t snapshot -o name,used,refer,mountpoint -r poolB/restore/app | head
NAME                              USED  REFER  MOUNTPOINT
poolB/restore/app@weekly          0B    220G   /srv/restore/app
poolB/restore/app@daily-2025-12   0B    218G   /srv/restore/app

Interprétation : Les snapshots sont présents, les tailles refer semblent correctes. Si USED est massivement élevé de façon inattendue, vous avez peut-être reçu des snapshots supplémentaires ou des incrémentaux non appariés plus tard.

Tâche 7 : Envoi/receive incrémental avec un snapshot de base clair

cr0x@src:~$ zfs send -I poolA/app@weekly poolA/app@daily | ssh dst 'zfs receive -u poolB/restore/app'

Interprétation : -I envoie tous les snapshots intermédiaires entre weekly et daily. Si la cible manque du snapshot de base, ceci échouera. Cet échec est sain — il empêche une divergence silencieuse.

Tâche 8 : Gérer un dataset qui existe déjà sur la cible (force receive)

cr0x@src:~$ zfs send poolA/app@weekly | ssh dst 'zfs receive -F -u poolB/restore/app'

Interprétation : -F fait revenir le dataset cible au dernier snapshot correspondant au flux et abandonne les changements divergents. C’est puissant et dangereux : si quelqu’un a écrit de nouvelles données sur la cible, elles sont perdues.

Tâche 9 : Utiliser les resume tokens quand un long receive est interrompu

cr0x@dst:~$ zfs get -H -o value receive_resume_token poolB/restore/app
1-2f4c9e1b7a-8000000000-1a2b3c4d5e6f...

cr0x@dst:~$ zfs send -t 1-2f4c9e1b7a-8000000000-1a2b3c4d5e6f... | zfs receive -u poolB/restore/app

Interprétation : Si votre environnement supporte les resume tokens, vous pouvez continuer sans renvoyer depuis le début. Si le token est - ou vide, la reprise n’est pas disponible ou n’est pas active.

Tâche 10 : Confirmer l’état de chiffrement et les exigences de clé après receive

cr0x@dst:~$ zfs get -o name,property,value encryption,keylocation,keystatus -r poolB/restore/app
NAME                 PROPERTY     VALUE        SOURCE
poolB/restore/app    encryption   aes-256-gcm  -
poolB/restore/app    keylocation  file:///...  local
poolB/restore/app    keystatus    unavailable  -

Interprétation : Le dataset existe mais les clés ne sont pas chargées. Vous ne pouvez pas le monter tant que vous n’avez pas chargé les clés. Si keylocation est incorrect pour la cible, corrigez-le avant d’essayer de monter.

Tâche 11 : Charger les clés et monter en toute sécurité (datasets chiffrés)

cr0x@dst:~$ zfs set keylocation=prompt poolB/restore/app
cr0x@dst:~$ zfs load-key poolB/restore/app
Enter passphrase for 'poolB/restore/app': 
cr0x@dst:~$ zfs mount poolB/restore/app

Interprétation : Passer à prompt est un choix opérationnel courant pour les restaurations DR. Pour un boot non supervisé, vous pouvez utiliser un keylocation basé fichier, mais c’est une discussion de sécurité séparée à avoir intentionnellement.

Tâche 12 : Mesurer si le receive est limité par CPU, disque ou réseau

cr0x@dst:~$ zpool iostat -v 1
                              capacity     operations     bandwidth
poolB                         alloc   free   read  write   read  write
----------------------------  -----  -----  -----  -----  -----  -----
poolB                          8.2T  10.1T      0   1200     0   450M
  raidz2-0                     8.2T  10.1T      0   1200     0   450M
    sda                            -      -      0    150     0    58M
    sdb                            -      -      0    148     0    57M

Interprétation : Si les écritures sur disque sont élevées et soutenues mais que le receive est lent, vous pouvez être limité par le CPU pour la décompression/checksum, ou limité par les réglages de sync. Si les écritures sont faibles, regardez le réseau/SSH ou le côté send.

Tâche 13 : Attraper les collisions de mountpoint avant qu’elles ne vous mordre

cr0x@dst:~$ zfs get -r -o name,property,value,source mountpoint,canmount poolB/restore | grep -E '(/var|/usr|/home|/srv)'
poolB/restore/app    mountpoint  /srv/app     local

Interprétation : Ce mountpoint est un problème si /srv/app est l’endroit où l’app réelle tourne sur la cible. Corrigez-le avant de monter.

Tâche 14 : Vérifier ce qui a changé pendant le receive en comparant les sources de propriétés

cr0x@dst:~$ zfs get -o name,property,value,source -s local,inherited compression,recordsize,atime,acltype,xattr poolB/restore/app
NAME                 PROPERTY     VALUE   SOURCE
poolB/restore/app    compression  lz4     local
poolB/restore/app    atime        off     inherited
poolB/restore/app    xattr        sa      local

Interprétation : C’est ainsi que vous repérez les problèmes « c’est différent en DR » sans deviner. Si une propriété est héritée d’un parent sur la cible, vous devrez peut-être la définir localement pour correspondre aux attentes.

Playbook de diagnostic rapide (quoi vérifier en premier, deuxième, troisième)

Voici la checklist que j’utilise lorsque la réplication est lente, cassée, ou « réussie mais incorrecte ». L’objectif est d’identifier la couche limitante en quelques minutes, pas en heures.

Premier : L’état du dataset est-il incorrect (propriétés/montage/chiffrement) ?

cr0x@dst:~$ zfs list -o name,type,mountpoint,canmount -r poolB/restore/app
cr0x@dst:~$ zfs get -o name,property,value,source mountpoint,canmount,readonly,sharenfs,sharesmb poolB/restore/app
cr0x@dst:~$ zfs get -o name,property,value encryption,keystatus,keylocation poolB/restore/app

Interprétation : Si ça ne se monte pas, le chiffrement et le mountpoint sont les principaux suspects. Si ça s’est monté n’importe où, mountpoint/canmount et les propriétés héritées sont généralement en cause.

Deuxième : La relation de flux est-elle incorrecte (base incrémentale manquante, rollback nécessaire, resume token) ?

cr0x@dst:~$ zfs list -t snapshot -o name -r poolB/restore/app | tail
cr0x@dst:~$ zfs get -H -o value receive_resume_token poolB/restore/app

Interprétation : Les échecs incrémentaux reviennent souvent à « la cible n’a pas le snapshot de base exact » ou « la cible a divergé ». La présence d’un resume token vous indique si vous pouvez continuer.

Troisième : Où est le goulot (disque, CPU, réseau, sync) ?

cr0x@dst:~$ zpool iostat 1
cr0x@dst:~$ iostat -xz 1
cr0x@dst:~$ vmstat 1

Interprétation : Vous cherchez une ressource saturée : disques au maximum, CPU saturé (souvent checksum/compress/decrypt), ou un pipeline lent parce que SSH/réseau est le limitateur. Si les écritures stagnent avec une latence élevée, vérifiez la santé du pool et un éventuel mismatch recordsize/workload.

Erreurs courantes : symptômes spécifiques et corrections

Erreur 1 : Recevoir dans un chemin qui se monte automatiquement par-dessus des données réelles

Syndromes : Les services « perdent » des fichiers, la config disparaît, des répertoires semblent vides, ou d’anciennes données réapparaissent. mount affiche un nouveau filesystem ZFS monté sur un chemin critique.

Correction : Démontez immédiatement le dataset reçu et définissez un mountpoint sûr.

cr0x@dst:~$ zfs unmount poolB/restore/app
cr0x@dst:~$ zfs set mountpoint=/srv/restore/app poolB/restore/app
cr0x@dst:~$ zfs set canmount=noauto poolB/restore/app

Erreur 2 : Receive incrémental échoue avec « does not exist » ou « most recent snapshot does not match »

Syndromes : Receive échoue ; la liste des snapshots de la cible ne contient pas la base incrémentale ; ou la cible a des snapshots supplémentaires non présents sur la source.

Correction : Re-seed avec le snapshot de base correct, ou forcer un rollback si vous voulez écraser la divergence.

cr0x@src:~$ zfs send poolA/app@weekly | ssh dst 'zfs receive -u poolB/restore/app'
cr0x@src:~$ zfs send -i poolA/app@weekly poolA/app@daily | ssh dst 'zfs receive -u poolB/restore/app'

Si une divergence existe et que les changements sur la cible sont jetables :

cr0x@src:~$ zfs send -i poolA/app@weekly poolA/app@daily | ssh dst 'zfs receive -F -u poolB/restore/app'

Erreur 3 : Quotas/réservations répliqués en DR et bloquant les restaurations

Syndromes : Vous recevez avec succès, mais les écritures échouent immédiatement avec « out of space » malgré beaucoup d’espace libre dans le pool. zfs get quota montre une valeur restrictive.

Correction : Remplacez quotas/réservations sur la cible après receive (ou excluez-les pendant le receive lorsque supporté).

cr0x@dst:~$ zfs get -o name,property,value quota,refquota,reservation,refreservation poolB/restore/app
cr0x@dst:~$ zfs set quota=none refquota=none reservation=none refreservation=none poolB/restore/app

Erreur 4 : Recevoir des datasets chiffrés sans plan pour keylocation/keystatus

Syndromes : Le dataset existe ; les montages échouent ; keystatus=unavailable.

Correction : Définissez le bon keylocation et chargez les clés, puis montez.

cr0x@dst:~$ zfs set keylocation=prompt poolB/restore/app
cr0x@dst:~$ zfs load-key poolB/restore/app
cr0x@dst:~$ zfs mount poolB/restore/app

Erreur 5 : « Optimisation » en changeant recordsize/compression sur la cible en plein flux

Syndromes : Le receive devient plus lent ; la fragmentation augmente ; la performance applicative change de façon imprévisible après promotion.

Correction : Traitez les propriétés de performance comme faisant partie du design système. Décidez d’une politique par rôle de dataset (BD vs logs vs images VM) et appliquez-la de façon cohérente — de préférence avant le premier ingest ou pendant des fenêtres de réécriture contrôlées.

cr0x@dst:~$ zfs set recordsize=16K poolB/restore/db
cr0x@dst:~$ zfs set compression=lz4 poolB/restore/db

Erreur 6 : Répliquer par accident des propriétés de partage dans la mauvaise zone réseau

Syndromes : Les données apparaissent sur le réseau de façon inattendue ; l’équipe conformité appelle ; les logs du firewall s’illuminent.

Correction : Assurez-vous que sharenfs/sharesmb sont désactivés sur les cibles de receive (ou hérités désactivés depuis un dataset parent).

cr0x@dst:~$ zfs set sharenfs=off sharesmb=off poolB/restore
cr0x@dst:~$ zfs inherit -r sharenfs poolB/restore/app
cr0x@dst:~$ zfs inherit -r sharesmb poolB/restore/app

Checklists / plan étape par étape

Plan A : Restauration sûre dans un nouvel environnement (par défaut recommandé)

  1. Créer un dataset parent d’atterrissage avec des propriétés héritées sûres (pas de partage, pas de montages auto).
  2. Recevoir avec -u pour empêcher l’auto-montage.
  3. Outrepasser les mountpoints vers un préfixe de restauration.
  4. Gérer explicitement les clés de chiffrement (prompt ou politique basée fichier).
  5. Vérifier snapshots et sources de propriétés avant d’exposer les données aux services.
cr0x@dst:~$ zfs create -o canmount=off -o mountpoint=/srv/restore poolB/restore
cr0x@dst:~$ zfs set sharenfs=off sharesmb=off poolB/restore
cr0x@src:~$ zfs send -R poolA/app@weekly | ssh dst 'zfs receive -u poolB/restore'
cr0x@dst:~$ zfs set mountpoint=/srv/restore/app poolB/restore/app
cr0x@dst:~$ zfs set canmount=noauto poolB/restore/app

Plan B : DR mirror que vous pouvez promouvoir (fidèle, mais contrôlé)

  1. Recevoir démonté.
  2. Préserver les propriétés sauf exception politique.
  3. Garder les mountpoints « DR-safe » en recevant sous une racine dédiée et ne changer les mountpoints qu’au moment de la promotion.
  4. Tester la promotion en répétitions (monter, charger les clés, démarrer les services) avec des runbooks qui tiennent compte des erreurs humaines sous stress.

Plan C : Réplication longue distance sur liens instables

  1. Privilégier receive résumable lorsque supporté.
  2. Suivre les resume tokens et éviter de tuer les receives sauf si vous le voulez vraiment.
  3. Mesurer les goulots avec zpool iostat et les métriques CPU système avant de « tuner ».

Trois mini-histoires issues du monde de l’entreprise

Mini-histoire 1 : L’incident causé par une mauvaise hypothèse (les mountpoints sont « locaux seulement »)

Une équipe infra corporate a construit un nouvel environnement DR. Le plan semblait solide : répliquer les datasets de production chaque nuit, les garder hors-ligne, et en cas de désastre « juste monter et partir ». Ils ont testé la mécanique de réplication — les flux circulaient, les snapshots apparaissaient, et les graphiques de stockage restaient calmes. Tout le monde est rentré tôt, ce qui est toujours suspect.

Des mois plus tard, une répétition DR de routine est devenue un incident de production, ce qui est la façon la plus efficace de répéter. Un ingénieur junior a lancé un receive dans un pool qui hébergeait aussi un environnement de staging. Le dataset est arrivé avec un mountpoint=/var/lib/app hérité de la production. Sur l’hôte DR, ce répertoire existait et contenait des données de staging. Le receive s’est monté automatiquement et a caché le répertoire de staging. Les services n’ont pas planté immédiatement ; ils ont silencieusement commencé à lire d’anciens fichiers répliqués qui semblaient valides.

L’instinct initial de l’équipe a été de debugger l’application. Ils ont suivi les logs, redéployé des conteneurs, blâmé le DNS, et regardé les dashboards jusqu’à ce que quelqu’un lance mount et voit un filesystem ZFS posé sur un chemin qui n’aurait jamais dû être touché.

La correction a pris des minutes : unmount, receive avec -u, définir un mountpoint sûr, puis monter explicitement. Le post-mortem a pris plus longtemps, parce que la mauvaise hypothèse (« le mountpoint est une configuration locale ») avait été institutionnalisée dans des scripts et des connaissances tribales. L’action corrective réelle fut culturelle : traiter les propriétés comme faisant partie de l’état répliqué, et toujours atterrir les restores dans un namespace qui ne peut pas entrer en collision avec des chemins live.

Mini-histoire 2 : L’optimisation qui s’est retournée contre eux (toucher aux réglages en cours de route)

Une autre org avait une pipeline de réplication « trop lente ». Des incrémentaux nocturnes débordaient parfois dans les heures de travail, ce qui a déplu à un VP qui aimait les lignes propres sur les graphiques. L’équipe storage a décidé d’accélérer en changeant la compression et le recordsize sur la cible, raisonnant que « receive écrit juste des blocs ; les propriétés côté cible vont rendre ça plus rapide ».

Ils ont mis une compression agressive et ajusté le recordsize pour des datasets contenant des images VM. Sur le papier, des blocs plus petits et une compression plus forte semblaient moins d’IO et des transferts plus rapides. En pratique, le côté receive est devenu lié au CPU. Les nœuds cibles n’étaient pas dimensionnés pour un gros travail de compression/décompression, et la fenêtre de réplication s’est aggravée. Parallèlement, les datasets d’images VM ont commencé à se fragmenter d’une manière qui a rendu les promotions ultérieures lentes et imprévisibles.

Le meilleur dans l’histoire : comme les receives restaient « réussis », personne n’a remarqué immédiatement. La douleur est apparue des semaines plus tard lors d’un test de basculement planifié, quand les storms de boot ont pris plus longtemps et que la latence stockage a grimpé. L’équipe avait optimisé pour la pipeline de réplication, mais dégradé accidentellement la charge opérationnelle qui importait réellement.

La récupération a été ennuyeuse : revenir à lz4, aligner le recordsize avec la réalité des workloads, et séparer les objectifs. Si vous voulez une réplication plus rapide, attaquez la pipeline (réseau, choix de cipher SSH, flags send, concurrence) et le design du pool (layout des vdev, politique sync), pas en changeant aléatoirement la sémantique des datasets en cours de route.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (recevoir démonté, puis promouvoir délibérément)

Le shop ZFS le plus résilient que j’ai vu n’était pas celui avec le hardware le plus tape-à-l’œil. C’était celui avec la discipline de réplication la plus terne : chaque receive atterrissait démonté sous un dataset racine dédié avec des « defaults sûrs ». Pas de partage. Pas d’automount. Nommage clair. Et chaque promotion avait un runbook avec des checks explicites de propriétés.

Pendant un incident réel — problème de contrôleur stockage sur le primaire — l’équipe a décidé de promouvoir le DR. La pression était réelle, mais les étapes étaient mécaniques : charger les clés de chiffrement, vérifier la présence des snapshots, définir les mountpoints vers les chemins de production, basculer canmount, monter, démarrer les services. Ils n’ont pas « juste tout monter et voir ce qui se passe », ce qui est l’équivalent stockage de l’escalade en solo.

Ils ont quand même eu des problèmes, parce que tout le monde en a. Un dataset avait un quota obsolète répliqué d’une expérience de capacité datant d’il y a longtemps. Mais parce qu’ils exécutaient toujours une checklist de diff de propriétés avant la promotion, ils l’ont repéré avant que l’application commence à écrire. Ils ont supprimé le quota, monté, et ont continué.

La leçon n’était pas héroïque. C’était que le processus ennuyeux tient sous stress. Un runbook DR qui anticipe les champs de mines liés aux propriétés vaut mieux que mille lignes de scripts de réplication futés.

FAQ

1) Pourquoi zfs receive se soucie-t-il des propriétés ?

Parce que dans ZFS, les propriétés définissent des comportements que d’autres systèmes de fichiers externalisent. Répliquer les données sans le comportement équivaut souvent à une restauration cassée : mauvais points de montage, mauvais profil de performance, mauvais modèle de permissions.

2) Dois-je toujours préserver les propriétés lors de la réplication ?

Non. Préservez les propriétés pour un vrai miroir que vous pourriez promouvoir. Outrepasser/exclure les propriétés pour des zones d’atterrissage (dev/analytics/forensics) où la disposition, les partages et les quotas de la source ne s’appliquent pas.

3) Quel est le flag receive le plus sûr par défaut dans un environnement inconnu ?

-u (ne pas monter). Il empêche la classe la plus commune d’incidents auto-infligés : collisions de mountpoint et interactions surprises avec les services.

4) Quand devrais-je utiliser zfs receive -F ?

Quand vous avez l’intention d’écraser l’état divergent de la cible et que vous acceptez de perdre tout changement fait sur la cible depuis le snapshot commun. C’est approprié pour des DR mirrors où la cible n’est pas un primaire écrivable.

5) Pourquoi mon receive incrémental a-t-il échoué alors que le dataset existe ?

Les incrémentaux dépendent du graphe des snapshots. La cible doit avoir exactement le snapshot de base (ou la chaîne de snapshots) référencée par le flux. Si la cible a divergé ou si le snapshot de base a été prune, receive échoue pour éviter une corruption par hypothèse.

6) Comment les clés de chiffrement affectent-elles la réplication ?

Les datasets chiffrés peuvent se répliquer chiffrés (raw) ou dans un mode impliquant du texte clair sur l’émetteur. Sur le récepteur, le dataset peut exister mais rester non montable tant que les clés ne sont pas chargées. Validez toujours keystatus et décidez comment keylocation doit se comporter en DR.

7) Pourquoi mon receive est-il lent ?

Les receives lents sont généralement limités par l’un des éléments suivants : débit/latence d’écriture disque, CPU (checksum/compress/decrypt), overhead réseau/SSH, ou réglages d’écriture synchrone. Mesurez avec zpool iostat, iostat et les métriques CPU avant de changer les propriétés des datasets.

8) Puis-je répliquer d’un OS à un autre en toute sécurité ?

Généralement oui, mais surveillez les propriétés liées aux ACL et xattr, les feature flags et les comportements par défaut. Traitez la réplication inter-plateforme comme un projet de compatibilité : validez la sémantique des permissions avec des tests applicatifs réels, pas seulement ls -l.

9) Est-il acceptable de changer les mountpoints après receive ?

Oui, et c’est souvent la bonne démarche. Recevez dans un namespace sûr, puis définissez les mountpoints dans le cadre de la promotion. Cela garde la réplication mécaniquement consistante tout en évitant les collisions.

10) Comment éviter de répliquer des propriétés « dangereuses » comme les partages ?

Définissez des defaults hérités sûrs sur le dataset parent sur la cible (partages désactivés), recevez démonté, puis configurez explicitement tout partage voulu. Si votre ZFS supporte l’exclusion de propriétés au receive, utilisez-la — mais conservez quand même le pattern parent-sûr comme filet de sécurité.

Conclusion

zfs receive n’est pas un endpoint passif. C’est l’endroit où vos données répliquées deviennent un vrai filesystem avec de vrais comportements, et ces comportements sont gouvernés par des propriétés — certaines évidentes, d’autres subtiles, et d’autres qui n’apparaissent que lorsque vous restaurez sous pression.

Si vous ne retenez qu’une règle opérationnelle : recevez démonté dans un namespace sûr, puis appliquez une politique de propriétés délibérée avant de monter ou partager quoi que ce soit. Cette seule habitude prévient la collision classique de mountpoint, rend les restaurations chiffrées prévisibles, et transforme la réplication d’un rituel d’espoir en un système d’ingénierie en lequel vous pouvez avoir confiance.

← Précédent
ZFS L2ARC sur SSD SATA : quand cela vaut la peine
Suivant →
Debian 13 : Système de fichiers monté en lecture seule après des erreurs — étapes de récupération minimisant les dégâts

Laisser un commentaire