Vous avez des données sur une machine ZFS plus récente que la destination. Ou la source est ancienne et bancale, et la cible plus récente est stricte sur les fonctionnalités.
Dans les deux cas, votre premier zfs send | zfs receive paraît assuré jusqu’à ce qu’il échoue à 2h du matin avec un message d’erreur qui ressemble à un haussement d’épaules.
Voici la réalité opérationnelle : la réplication ZFS est puissante, mais ce n’est pas de la magie. Les flux de send transportent des suppositions — sur les fonctionnalités des datasets, les feature flags du pool,
les modes de chiffrement, et même sur la capacité de la destination à comprendre le dialecte que vous utilisez. Ce guide explique comment arrêter de deviner.
Un modèle mental qui prédit vraiment les échecs
La réplication ZFS est constituée de deux contrats distincts empilés :
-
Le contrat du pool : la pool de destination peut-elle représenter les structures sur disque nécessaires à l’état du dataset entrant ?
C’est là que les feature flags du pool et les « versions » ZFS entrent en jeu. -
Le contrat du flux : le format du send stream est-il compréhensible par l’implémentation ZFS de la destination, et contient-il des types d’enregistrements qu’elle peut appliquer ?
C’est là que des fonctionnalités récentes du send et des flags comme-L,-c,-w,-e, ou--rawpeuvent vous piéger.
Ces contrats sont liés mais pas identiques. Vous pouvez avoir une pool de destination qui supporte tous les feature flags requis, et échouer quand même parce que
vous avez utilisé une saveur de flux que le récepteur ne sait pas analyser. Ou l’inverse : le récepteur comprend le flux, mais la pool de destination
ne peut pas activer une fonctionnalité requise en toute sécurité.
Cessez de penser en « versions ZFS »
On demande souvent : « Est-ce que ZFS 2.1 peut envoyer vers ZFS 0.8 ? » Cette question se comprend mais est souvent inutile. Dans l’écosystème OpenZFS, ce qui compte est :
- Les feature flags du pool (
zpool get all,zpool status,zpool get feature@*). - Les fonctionnalités et propriétés du dataset (chiffrement, gros blocs, données embarquées, redaction, etc.).
- Les fonctionnalités du flux négociées par le récepteur (ce que
zfs receivepeut accepter). - Ce que vous avez demandé à
zfs send(raw vs déchiffré, compressé ou non, récursif, propriétés).
Opérationnellement, la compatibilité est une intersection à trois voies : capacités du pool source, capacités du pool destination, et format du send choisi.
Si l’un est « trop récent » pour les autres, la réplication échoue. Ou pire : elle réussit mais impose des choix dont vous n’aviez pas conscience (comme perdre le chiffrement
ou hériter de points de montage indésirables).
Une idée paraphrasée de Jeff Bezos (voisine de la fiabilité, et douloureusement vraie pour l’exploitation) : « Soyez obstiné sur la vision, flexible sur les détails. »
Pour la réplication ZFS, votre vision est l’intégrité des données. Vos « détails » sont les flags de flux. Soyez flexibles là-dessus.
Faits et historique utiles en production
- ZFS utilisait à l’origine des “versions de pool” (un entier unique). OpenZFS moderne est passé aux feature flags pour permettre l’évolution des pools sans un changement monolithique de version.
- Les feature flags sont par pool, pas par hôte. Mettre à jour l’OS n’active pas les fonctionnalités du pool tant que vous ne les activez pas explicitement (souvent via
zpool upgrade). - Une fois qu’une feature de pool est active, on ne revient généralement pas en arrière. « Downgrade » signifie typiquement « restaurer depuis une réplication vers une pool plus ancienne », pas une inversion in-place.
- Les flux de send peuvent inclure plus que des blocs : propriétés, snapshots, clones, et parfois des suppositions sur les mountpoints et les holds.
- Les datasets chiffrés ont changé la sémantique de la réplication. Les envois raw préservent le chiffrement et les clés restent sur la source ; les envois non-raw peuvent livrer du texte clair silencieusement.
- Les bookmarks existent pour rendre l’incrémental robuste sans garder les anciens snapshots indéfiniment, mais il faut le support du récepteur et une convention de nommage disciplinée.
- Le send compressé n’est pas « seulement compression sur le réseau ». Selon les flags et versions, il peut transporter des blocs déjà compressés ; bon pour la bande passante, source de confusion pour la compatibilité.
- Le support des gros blocs n’est pas qu’une propriété. Il interagit avec les feature flags du pool et le comportement du récepteur ; des mismatches peuvent produire des erreurs de receive ressemblant à de la corruption.
- OpenZFS est multiplateforme, mais pas parfaitement uniforme. FreeBSD, dérivés d’illumos et Linux s’alignent généralement, mais des fonctionnalités de pointe apparaissent à des moments différents.
Blague #1 : Les discussions sur la compatibilité ZFS ressemblent aux groupes familiaux — tout le monde jure parler la même langue, et personne ne la parle vraiment.
Règles de compatibilité : ce qui doit correspondre vs ce qui peut différer
Règle 1 : la destination doit supporter toutes les fonctionnalités on-disk requises par l’état du dataset entrant
Si le flux send représente un état de dataset qui requiert une fonctionnalité que la pool de destination ne peut pas activer, le receive échouera. Ce n’est pas négociable.
Peu importe la politesse de vos flags de send.
Nuance clé : une pool peut avoir des features « enabled » mais pas « active ». Une feature « enabled » est disponible ; « active » signifie qu’elle est déjà utilisée dans les métadonnées du pool.
La réplication peut déclencher l’activation lorsqu’elle matérialise des structures qui l’exigent.
Règle 2 : le récepteur doit comprendre le format du flux que vous générez
Certaines améliorations du send stream sont purement additives ; des récepteurs plus anciens peuvent planter sur des types d’enregistrements inconnus. Si votre destination est plus ancienne, vous devriez partir du principe
qu’il faut garder le flux conservateur à moins d’avoir vérifié le support du récepteur.
Règle 3 : le chiffrement multiplie les complexités de compatibilité
Les datasets chiffrés sont l’endroit où « ça marchait en labo » devient problématique en production. La décision critique est : envoyez-vous :
- Raw chiffré (préserve le chiffrement, nécessite le support du receive chiffré et des fonctionnalités d’encryption sur la destination).
- Déchiffré (les données arrivent en clair ; cela peut être acceptable pour migrer vers un autre domaine de chiffrement, mais c’est une décision de sécurité, pas une commodité).
Si la destination est plus ancienne et ne peut pas faire un receive chiffré correctement, vous devez soit la mettre à niveau, soit accepter une migration en clair et rechiffrer au repos sur la destination.
Prétendre qu’il existe une troisième option, c’est comment on se retrouve avec des sauvegardes « temporaires » non chiffrées au mauvais endroit.
Règle 4 : les streams incrémentaux requièrent une histoire partagée
L’incrémental dépend d’un snapshot commun (ou d’un bookmark) existant des deux côtés. Si la destination l’a perdu, renommé ou jamais reçu, l’incrémental échoue.
La correction est généralement de reseed avec un send complet ou d’utiliser une chaîne de bookmarks gérée correctement.
Règle 5 : la récursion et les propriétés peuvent être dangereuses
zfs send -R est pratique. Il réplique aussi volontiers des propriétés que vous ne voulez peut‑être pas sur la destination : mountpoints, sharenfs/smb,
réservations de quota, et paramètres legacy bizarres. Traitez -R comme un changement de production, pas comme une simple copie.
Tâches pratiques : commandes, sorties et décisions
Voici les vérifications que je fais réellement avant et pendant les migrations. Chaque item indique ce que la sortie signifie et la décision à en tirer.
Exécutez-les des deux côtés. Comparez. N’assumez rien.
Tâche 1 : Identifier l’implémentation ZFS et la version (Linux)
cr0x@server:~$ modinfo zfs | egrep 'version:|srcversion:'
version: 2.2.2-1
srcversion: 1A2B3C4D5E6F7G8H9I0J
Sens : Ceci indique la version du module OpenZFS sur Linux. Ce n’est pas toute l’histoire, mais ça ancre les attentes.
Décision : Si la destination est significativement plus ancienne, prévoyez des flux conservateurs et attendez-vous à des mismatches de fonctionnalités.
Tâche 2 : Identifier la version userland ZFS
cr0x@server:~$ zfs version
zfs-2.2.2-1
zfs-kmod-2.2.2-1
Sens : Le module kernel et le userland devraient être raisonnablement alignés. De gros décalages peuvent causer des comportements étranges.
Décision : Si ils sont désynchronisés, corrigez cela d’abord. Déboguer la réplication avec votre stack à moitié mise à jour, c’est un passe-temps, pas un boulot.
Tâche 3 : Vérifier les feature flags du pool sur la source
cr0x@server:~$ zpool get -H -o name,property,value all tank | head
tank size 23.8T
tank capacity 61%
tank health ONLINE
tank ashift 12
tank autotrim off
Sens : Sanity check de base, et cela prouve que la pool est lisible. Ce n’est pas encore suffisant pour la compatibilité.
Décision : Si l’état du pool n’est pas ONLINE, arrêtez et réparez avant de migrer. La réplication n’est pas une stratégie de réparation.
Tâche 4 : Énumérer explicitement les feature flags
cr0x@server:~$ zpool get -H -o property,value feature@* tank | egrep 'active|enabled' | head
feature@async_destroy active
feature@bookmarks active
feature@embedded_data active
feature@extensible_dataset active
feature@encryption active
feature@device_removal enabled
Sens : « active » signifie utilisé ; « enabled » signifie disponible. Tout ce qui est actif sur la source est un signal d’alerte pour des destinations plus anciennes.
Décision : Comparez cela avec les features de la pool destination. Si la destination ne supporte pas une feature active, vous devez mettre à niveau la destination ou migrer par une autre voie.
Tâche 5 : Vérifier quelles fonctionnalités la pool destination supporte
cr0x@server:~$ zpool get -H -o property,value feature@* backup | head
feature@async_destroy enabled
feature@bookmarks enabled
feature@embedded_data enabled
feature@encryption disabled
feature@extensible_dataset enabled
feature@filesystem_limits enabled
Sens : Si une feature requise est disabled parce que l’implémentation ne la connaît pas, vous êtes bloqué.
Si elle est disabled mais connue, vous pouvez potentiellement l’activer.
Décision : Si la source requiert feature@encryption active et la destination indique feature@encryption disabled, mettez à niveau OpenZFS/la pool destination ou planifiez une migration décryptée (en connaissance de cause).
Tâche 6 : Confirmer l’état de chiffrement du dataset et la gestion des clés
cr0x@server:~$ zfs get -o name,property,value -H encryption,keylocation,keystatus tank/prod
tank/prod encryption aes-256-gcm
tank/prod keylocation file:///root/keys/prod.key
tank/prod keystatus available
Sens : Vous avez affaire au chiffrement natif ZFS. Les choix de réplication comptent.
Décision : Si vous devez préserver le chiffrement, planifiez un send raw et assurez-vous que la destination le supporte. Si vous acceptez de rechiffrer, planifiez un envoi en clair et un chiffrement côté destination.
Tâche 7 : Lister les snapshots et vérifier qu’une base commune existe
cr0x@server:~$ zfs list -t snapshot -o name,creation -s creation tank/prod | tail -3
tank/prod@replica_2025-12-20 Sat Dec 20 02:00 2025
tank/prod@replica_2025-12-21 Sun Dec 21 02:00 2025
tank/prod@replica_2025-12-22 Mon Dec 22 02:00 2025
Sens : Cela montre la chaîne de snapshots disponible pour les envois incrémentaux.
Décision : Confirmez que la destination possède aussi le snapshot de base que vous utiliserez. Si non, vous ne pouvez pas faire d’incrémental sans reseed.
Tâche 8 : Vérifier que la destination a le snapshot de base
cr0x@server:~$ zfs list -t snapshot -o name backup/prod | head -3
NAME
backup/prod@replica_2025-12-20
backup/prod@replica_2025-12-21
Sens : Bon : l’historique partagé existe.
Décision : Procédez avec l’incrémental depuis le snapshot commun le plus récent. Si vous le manquez, arrêtez et reseed ou utilisez une stratégie basée sur bookmarks.
Tâche 9 : Estimer la taille du send avant d’envoyer sur le WAN
cr0x@server:~$ zfs send -nP -i tank/prod@replica_2025-12-21 tank/prod@replica_2025-12-22
size 14839210496
incremental tank/prod@replica_2025-12-21 tank/prod@replica_2025-12-22
Sens : Environ 13.8 GiB de données logiques de flux (pas forcément les octets sur le fil).
Décision : Si c’est plus grand que prévu, enquêtez sur le churn (images VM, bases de données, mismatch de recordsize) avant de programmer la réplication en période de pointe.
Tâche 10 : Dry-run receive pour valider permissions et dataset cible
cr0x@server:~$ zfs receive -nvu backup/prod
would receive incremental stream of tank/prod@replica_2025-12-22 into backup/prod
would destroy snapshots: none
would overwrite: none
Sens : -n no-op, -v verbeux, -u ne pas monter. Cela confirme que le récepteur comprend le flux et ce qu’il ferait.
Décision : Si ceci échoue, ne faites pas « lancez-le quand même ». Corrigez le nommage, les permissions et les mismatches de fonctionnalités d’abord.
Tâche 11 : Effectuer un envoi incrémental résilient
cr0x@server:~$ zfs send -v -i tank/prod@replica_2025-12-21 tank/prod@replica_2025-12-22 | ssh backup01 zfs receive -uF backup/prod
send from tank/prod@replica_2025-12-21 to tank/prod@replica_2025-12-22 estimated size is 13.8G
total estimated size is 13.8G
TIME SENT SNAPSHOT
02:00:12 13.8G tank/prod@replica_2025-12-22
Sens : -F peut faire un rollback de la destination pour correspondre; c’est utile mais dangereux.
Décision : N’utilisez -F que pour des cibles de réplication dédiées où le rollback ne détruira pas des changements locaux. Si des humains écrivent là, retirez -F et corrigez la divergence proprement.
Tâche 12 : Inspecter une erreur de receive et la mapper à la compatibilité
cr0x@server:~$ zfs receive -u backup/prod
cannot receive incremental stream: most recent snapshot of backup/prod does not match incremental source
Sens : Le snapshot de base sur la destination est différent de celui que vous envoyez depuis (ou a été rollback/renommé).
Décision : Trouvez le snapshot commun le plus récent ou reseed avec un send complet. Ne forcez pas avec un rollback sauf si la destination est jetable.
Tâche 13 : Vérifier les holds pour empêcher la suppression de snapshots en cours de stream
cr0x@server:~$ zfs holds tank/prod@replica_2025-12-22
NAME TAG TIMESTAMP
tank/prod@replica_2025-12-22 repl Fri Dec 22 02:00 2025
Sens : Un hold empêche le nettoyage automatisé de supprimer le snapshot dont vous avez encore besoin.
Décision : Si vous exécutez un pruning de snapshots, utilisez des holds (ou des bookmarks) pour que votre chaîne incrémentale ne soit pas « nettoyée » en échec.
Tâche 14 : Utiliser des bookmarks pour des ancres incrémentales longue durée
cr0x@server:~$ zfs bookmark tank/prod@replica_2025-12-22 tank/prod#bkm_2025-12-22
cr0x@server:~$ zfs list -t bookmark -o name,creation tank/prod | tail -1
tank/prod#bkm_2025-12-22 Mon Dec 22 02:00 2025
Sens : Les bookmarks sont des références légères qui permettent des envois incrémentaux sans garder le snapshot complet indéfiniment.
Décision : Si la pression de rétention est forte, passez aux bookmarks. Mais testez le support récepteur ; les systèmes plus anciens peuvent mal gérer les incrementals basés sur bookmarks.
Tâche 15 : Confirmer le comportement mountpoint et canmount avant d’utiliser -R
cr0x@server:~$ zfs get -o name,property,value -H mountpoint,canmount tank/prod
tank/prod mountpoint /prod
tank/prod canmount on
Sens : Si vous répliquez les propriétés, vous pourriez monter des chemins de production sur un serveur de sauvegarde. Ce n’est pas un « oups » ; c’est une panne.
Décision : Sur la destination, utilisez zfs receive -u et définissez des mountpoints sûrs. Envisagez de supprimer ou d’outrepasser les propriétés.
Tâche 16 : Confirmer que le dataset destination n’est pas monté (ou monter en sécurité)
cr0x@server:~$ zfs get -o name,property,value -H mounted,mountpoint backup/prod
backup/prod mounted no
backup/prod mountpoint /backup/prod
Sens : Bon : il ne va pas monter par surprise par-dessus quelque chose.
Décision : Gardez les cibles de réplication démontées par défaut ; montez seulement quand vous devez lire ou restaurer.
Choisir les bons flags de send (et quand ne pas le faire)
Commencez conservateur, puis ajoutez de la puissance
Quand vous envoyez vers un récepteur plus ancien, l’objectif est la compatibilité ennuyeuse, pas l’ingéniosité. L’approche la plus compatible est typiquement :
un envoi complet pour le seed, puis des envois incrémentaux, avec le moins de flags « fancy » possible.
Raw vs non-raw avec chiffrement
Si le dataset est chiffré et que vous souhaitez le préserver tel quel (même ciphertext, mêmes clés non exposées), vous voulez un send raw. Selon votre plateforme,
c’est habituellement zfs send -w (raw) et recevoir sur une destination qui supporte le receive chiffré.
cr0x@server:~$ zfs send -w tank/prod@replica_2025-12-22 | ssh backup01 zfs receive -u backup/prod
cannot receive: stream is encrypted but encryption feature is disabled
Sens : La destination ne peut pas accepter un flux chiffré/raw parce que sa pool ou son implémentation n’a pas le support de chiffrement.
Décision : Mettez à niveau OpenZFS et les features de la pool destination, ou passez à un send non-raw (acceptant le transfert en clair) et rechiffrez sur la destination.
Send compressé : génial jusqu’à ce que ça ne le soit plus
Le send compressé peut réduire la bande passante et le CPU dans certains cas, mais dépend du support du récepteur. Sur des versions mixtes, considérez-le comme une optimisation à gagner,
pas un défaut à supposer.
Streams de réplication (-R) et mines de propriétés
-R sert à répliquer un sous-arbre avec snapshots, propriétés et datasets descendants. C’est parfait pour des cibles DR.
C’est aussi une bonne manière de répliquer un mauvais mountpoint au mauvais endroit.
cr0x@server:~$ zfs send -R tank/prod@replica_2025-12-22 | ssh backup01 zfs receive -u backup/tank
receiving full stream of tank/prod@replica_2025-12-22 into backup/tank/prod
Sens : Vous recréez la structure sur la destination. Les propriétés peuvent suivre.
Décision : Si la destination n’est pas un environnement miroir, évitez -R. Ou utilisez-le, mais surchargez immédiatement les propriétés dangereuses sur la destination.
Force receive (-F) : parfois nécessaire, souvent abusé
zfs receive -F rollback la destination pour matcher le flux. Cela résout la divergence. Cela supprime aussi des snapshots et des changements locaux en conflit.
En termes corporatifs, -F est un licenciement : efficace, rapide, et mieux vaut avoir des approbations.
Compatibilité des datasets : recordsize, volblocksize et gros blocs
Les propriétés du dataset peuvent causer des surprises de performance même si le flux est accepté. Une source plus récente avec un recordsize large et des réglages optimisés
peut se répliquer vers une destination plus ancienne qui l’accepte techniquement, mais alors vos performances de restauration sont bizarres et vos hypothèses tombent.
Blague #2 : Si vous voulez de l’excitation, faites de la réplication de bases de données sur un VPN instable ; si vous voulez dormir, faites un full send pendant le week-end et prenez des snacks.
Trois mini-récits d’entreprise depuis les tranchées de la réplication
Incident : une fausse supposition sur la « compatibilité de version »
Une entreprise de taille moyenne migrainait depuis un stockage FreeBSD ancien vers un appliance Linux avec OpenZFS plus récent. Le plan semblait simple :
seed la nouvelle machine, couper les exports NFS, retirer l’ancien matériel. Quelqu’un a demandé « Les versions ZFS sont compatibles ? » et a reçu un confiant « Oui, ZFS c’est ZFS. »
Cette phrase leur a coûté un week-end.
La pool source avait plusieurs feature flags actifs (bookmarks, embedded_data, large_blocks). L’OpenZFS du système de destination supportait certains, mais la pool de l’appliance n’avait jamais été upgrade
et tournait avec un ensemble conservateur de features parce qu’elle était fournie ainsi. Pendant les tests initiaux, ils n’avaient répliqué qu’un petit dataset sans activer les nouvelles features. En production, l’état principal du dataset les nécessitait.
Le receive a échoué à mi-chemin du seed stream. Le message d’erreur n’était pas utile ; il ressemblait à une erreur générique de receive. Ils ont retenté, obtenu d’autres erreurs,
puis sont tombés dans le piège classique : « Peut-être que c’est le réseau. » Ce n’était pas le réseau. C’était le contrat du pool.
La correction a été ennuyeuse : mettre à niveau explicitement la pool de destination pour supporter les feature flags requis, puis reseed. Mais le reseed signifiait un autre long transfert,
et la fenêtre de bascule était déjà réservée avec d’autres équipes. Ils ont fini par faire une migration partielle et traîner l’ancien matériel pendant des semaines.
La leçon n’était pas « mettez tout à jour ». La leçon était : inventoriez les features avant de transférer les octets, et traitez les upgrades de pool comme des opérations gérées (avec plans de rollback, qui signifient généralement « répliquer ailleurs », pas « annuler »).
Optimisation qui a mal tourné : flags agressifs sur une flotte mixte
Un autre service avait une flotte de bureaux distants, chacun avec une petite box ZFS répliquant vers un cluster DR central. La bande passante était limitée et chère. Un ingénieur
a décidé de « rendre le flux plus petit » en activant le send compressé et la récursion partout, et en utilisant force receive pour garder les choses alignées.
Au début, ça marchait, ce qui est l’état le plus dangereux pour un changement. Avec le temps, quelques bureaux ont mis à jour ZFS lors de rafraîchissements OS, tandis que d’autres sont restés en arrière
pour des contraintes applicatives. Le script de réplication ne s’en souciait pas ; il utilisait les mêmes flags pour tous.
Un sous-ensemble de récepteurs a commencé à échouer de façon intermittente sur une classe de datasets — des zvols VM. Les échecs ont coïncidé avec un changement dans la façon dont ces systèmes créaient des snapshots
et ce que contenaient les flux. Le script, exécuté avec zfs receive -F, a commencé à rollbacker et réappliquer l’état en boucle. Les données n’étaient pas corrompues,
mais les objectifs de point de récupération se sont silencieusement dégradés parce que le pipeline passait son temps à se combattre.
Ils ont finalement découvert deux choses : premièrement, le send compressé n’était pas uniformément supporté à travers les versions de leurs récepteurs pour certaines formes de flux générées ;
deuxièmement, -F masquait le problème sous-jacent de « snapshot commun manquant » en réécrivant constamment l’historique de la destination, ce qui rendait les chaînes incrémentales fragiles.
La correction a été de scinder la flotte en niveaux de compatibilité. Flux conservateurs pour les récepteurs plus anciens, flux modernes pour les plus récents, et suppression de -F sauf pour
des cibles dédiées explicitement traitées comme miroirs jetables. La bande passante a un peu augmenté. Le sommeil s’est beaucoup amélioré.
Pratique ennuyeuse mais correcte qui a sauvé la situation : inventaire des features + seed par étapes
Une équipe de services financiers a planifié un déménagement de datacenter. Ils avaient ZFS des deux côtés, mais la destination était gérée par un autre groupe avec un rythme de mise à jour différent.
Au lieu de se précipiter sur les flags de flux, ils ont suivi une simple discipline : inventorier les features du pool et les propriétés des datasets, puis convenir d’une baseline de compatibilité.
Ils ont créé une pool de staging sur la destination dimensionnée pour accepter des seeds complets, et ont exécuté des dry-run receives pendant les heures ouvrables pour vérifier que les flux seraient parsés et qu’aucune action destructive n’était implicite.
Ils ont utilisé zfs send -nP pour estimer et planifier les transferts volumineux, et ont appliqué des holds sur les snapshots pour qu’un nettoyage automatisé ne casse pas les incrémentaux pendant la fenêtre de bascule.
Lors du transfert réel, le réseau a eu une mauvaise journée. Les transferts ont ralenti et stagné. Mais comme ils avaient déjà seedé la plupart des datasets et vérifié la compatibilité incrémentale,
ils n’avaient besoin que de petits deltas pour basculer. Le réseau lent est alors devenu une contrariété plutôt qu’une crise.
Rien d’héroïque n’est arrivé. C’est le point. La pratique correcte avait l’air ennuyeuse en réunion, mais elle a réduit le nombre d’inconnues à presque zéro.
Playbook de diagnostic rapide
Quand la réplication casse ou est lente, vous voulez une boucle serrée : identifiez si c’est une défaillance de compatibilité, un mismatch d’historique, ou un goulot d’étranglement de débit.
Vérifiez dans cet ordre. Ne sautez pas directement à l’optimisation des flags.
Premièrement : est-ce une défaillance de compatibilité ?
- Sur la destination, essayez un dry-run receive :
zfs receive -nvu target/ds. Si l’analyse échoue, c’est la compatibilité du flux ou les permissions. - Comparez les feature flags :
zpool get feature@* srcpoolvszpool get feature@* dstpool. - Vérifiez l’état du chiffrement :
zfs get encryption,keystatus. Si vous utilisez raw send, la destination doit supporter les features de chiffrement.
Deuxièmement : est-ce un mismatch d’historique incrémental ?
- Confirmez que les deux côtés ont le snapshot de base :
zfs list -t snapshot | grep. - Vérifiez la divergence de la destination : quelqu’un a-t-il pris des snapshots locaux, fait un rollback ou détruit des snapshots ?
- Si vous utilisez des bookmarks, vérifiez qu’ils existent des deux côtés (ou que votre send utilise snapshot-to-snapshot, pas bookmark-to-snapshot).
Troisièmement : est-ce un goulot d’étranglement de débit ?
- Estimez la taille du flux :
zfs send -nP. Si c’est énorme, le « goulot » peut être le churn, pas le réseau. - Vérifiez CPU et compression : si vous compressez à la volée en espace utilisateur, le CPU peut être votre limiteur.
- Observez le réseau et SSH : si vous utilisez SSH, le choix du cipher et le single-threading peuvent plafonner le débit.
- Vérifiez les performances de la pool : la pool destination peut être fragmentée, occupée, ou avoir des réglages sync qui contraignent les écritures.
Ce playbook est volontairement peu glamoureux. En production, la correction la plus rapide est généralement une « hypothèse correcte » plutôt qu’une « optimisation intelligente ».
Erreurs courantes : symptôme → cause racine → correction
1) « cannot receive: unsupported feature or stream »
Symptôme : Le receive échoue immédiatement ; l’erreur mentionne une feature/stream non supportée, ou simplement « invalid stream ».
Cause racine : Le récepteur de destination ne comprend pas les types d’enregistrements du flux, ou la pool de destination ne peut pas supporter les features requises.
Correction : Comparez zpool get feature@* et ajustez. Mettez à niveau OpenZFS/la pool destination, ou envoyez un flux plus conservateur (évitez raw/compressé/réplication jusqu’à vérification).
2) « most recent snapshot does not match incremental source »
Symptôme : L’envoi incrémental échoue ; la destination a des snapshots mais pas celui attendu.
Cause racine : Le snapshot commun de base a été perdu à cause du pruning, des renommages, des suppressions manuelles, ou d’un rollback sur la destination.
Correction : Trouvez le snapshot commun le plus récent et envoyez les incrémentaux depuis celui-ci. Si aucun n’existe, reseed avec un send complet. Ajoutez des holds ou bookmarks pour prévenir la récurrence.
3) La réplication « réussit » mais la destination monte par-dessus quelque chose de critique
Symptôme : Contenu filesystem étrange sur la destination ; services lisant de mauvaises données ; changements de mountpoints.
Cause racine : Des propriétés répliquées (souvent via -R) ont appliqué mountpoint/canmount/shares.
Correction : Recevez toujours avec -u dans des chemins sûrs ; définissez explicitement mountpoint/canmount après le receive. Évitez de répliquer les propriétés sauf si la destination est un vrai miroir.
4) Le send raw chiffré échoue sur la destination
Symptôme : Erreurs sur la feature encryption désactivée ou problèmes de clés.
Cause racine : L’OpenZFS/la pool destination ne supporte pas la feature de chiffrement, ou les clés ne sont pas gérées correctement pour le mode de receive.
Correction : Mettez à niveau la destination pour supporter le chiffrement et activez les features requises du pool, ou faites une migration décryptée et rechiffrez sur la destination avec de nouvelles clés.
5) « zfs receive » est lent et sollicite fortement le CPU
Symptôme : Le réseau est relativement inactif ; le CPU est fortement utilisé ; la réplication est lente.
Cause racine : Vous faites une forte compression/chiffrement en espace utilisateur (cipher SSH, compression externe), ou la pool destination est un goulot.
Correction : Réduisez la surcharge utilisateur (choisissez des ciphers SSH raisonnables, évitez une compression inutile), vérifiez les IOPS de la pool destination, et évitez d’empiler la compression sur des données déjà compressées.
6) Les incrémentaux cassent aléatoirement après des « jobs de nettoyage »
Symptôme : Ça marche pendant des jours, puis échoue après que le pruning des snapshots s’est exécuté.
Cause racine : La politique de rétention supprime le snapshot de base nécessaire aux incrémentaux.
Correction : Utilisez des holds sur les snapshots pour ancrer la réplication ou passez aux bookmarks ; alignez la rétention avec la fréquence et le retard de réplication.
7) L’utilisation d’espace de la destination explose après migration
Symptôme : Même données logiques, mais plus d’espace consommé sur la destination.
Cause racine : Paramètres de compression différents, différences de recordsize, comportement particulier des vdev, ou la destination ne reçoit pas les blocs compressés comme attendu.
Correction : Vérifiez zfs get compression,recordsize et les différences de layout de pool. Ne supposez pas une utilisation physique identique entre pools ; validez avec de petits exemples d’abord.
Checklists / plan pas-à-pas
Plan A : Migration sûre d’une source plus récente vers une destination plus ancienne
-
Inventoriez features et chiffrement.
Exécutez sur source et destination :zpool get feature@*,zfs get encryption,keystatus.
Décidez : mettre à niveau la destination vs accepter une migration déchiffrée. -
Créez un dataset cible dédié.
Gardez-le démonté : receive avec-u. Décidez : avez-vous besoin d’un miroir (-R) ou d’un dataset unique ? -
Dry-run receive.
Utilisezzfs receive -nvupour détecter les problèmes d’analyse/permissions sans écrire. -
Seed avec un full send.
Évitez les flags fancy initialement. Validez que le snapshot destination existe après le receive. -
Passez aux envois incrémentaux.
Utilisez un nommage cohérent des snapshots ; protégez les ancres avec holds ou bookmarks. -
Basculer.
Envoi incrémental final, vérifiez l’intégrité applicative, puis basculez les clients.
Plan B : Construire une norme de réplication sur des versions ZFS mixtes
- Définissez des niveaux de compatibilité. Groupez les systèmes par capacité réceptrice ; n’exécutez pas un ensemble unique de flags « universel ».
- Choisissez un flux conservateur par défaut. N’ajoutez raw/compressé/récursion qu’après validation par niveau.
- Standardisez le nommage des snapshots et bookmarks. Votre futur vous a besoin d’un matching déterministe, pas de créativité.
- Automatisez la validation. Confirmez périodiquement que des snapshots communs existent et que les dry-run receives réussissent.
- Documentez les politiques d’upgrade de pool. Les upgrades de pool sont irréversibles ; traitez-les comme des événements de changement avec approbation.
Plan C : Quand les incrémentaux sont cassés et vous devez rétablir le service
- Arrêtez de supprimer des snapshots. Suspendez les jobs de rétention.
- Trouvez le snapshot commun le plus récent. Si il existe, reprenez les incrémentaux depuis là.
- Si aucun snapshot commun, reseed. Envoi complet du snapshot le plus récent ; puis rétablissez la chaîne incrémentale.
- Après récupération, implémentez holds/bookmarks. Évitez que le nettoyage répète la panne.
Garde-fous opérationnels supplémentaires (faites-les, vous me remercierez)
- Traitez les cibles de réplication comme du bétail : dédiées, non interactives, et faciles à rollback.
- Ne répliquez jamais des mountpoints dans un hôte qui exécute aussi des applications sauf si vous contrôlez explicitement les propriétés.
- Testez un dataset de bout en bout, y compris la restauration, avant de programmer « la grande migration ».
FAQ
1) Un OpenZFS plus récent peut-il toujours envoyer vers un OpenZFS plus ancien ?
Non. Le récepteur doit comprendre le format du flux, et la pool de destination doit supporter les features requises. « ZFS est ZFS » n’est pas un contrat.
2) Quel est le plus grand indicateur d’incompatibilité ?
Les features de pool actives sur la source que la destination ne supporte pas. Vérifiez zpool get feature@* et cherchez « active » sur la source et « disabled/unsupported » sur la destination.
3) Si la pool destination supporte une feature mais qu’elle n’est pas activée, le receive l’activera-t-il ?
Cela peut arriver, selon l’implémentation et les structures écrites. En pratique, vous devriez gérer explicitement les upgrades/enables de pool pour ne pas activer « accidentellement » des features irréversibles pendant une migration.
4) Le send raw est-il requis pour les datasets chiffrés ?
Requis si vous voulez préserver le chiffrement en tant que ciphertext de bout en bout. Si vous n’utilisez pas raw, vous pouvez envoyer du texte clair (même si la source est chiffrée), et c’est une décision de sécurité.
5) Pourquoi l’envoi incrémental se plaint-il que les snapshots ne correspondent pas quand les noms semblent identiques ?
Les noms ne sont pas l’identité. Le snapshot de la destination peut ne pas être le même état (il peut provenir d’une histoire différente), ou la destination a été rollbackée. Les incrémentaux exigent une chaîne d’ascendance partagée.
6) Dois-je toujours utiliser zfs send -R pour la réplication ?
Seulement si la destination doit être une réplique fidèle d’un sous-arbre, propriétés et descendants inclus. Sinon, -R peut répliquer des « hypothèses d’environnement » que vous ne vouliez pas copier.
7) Quand zfs receive -F est-il approprié ?
Pour des cibles de réplication dédiées où le rollback est acceptable et attendu. Pas pour des datasets partagés, pas pour des éléments modifiés par des humains, et pas comme pansement pour une rétention cassée.
8) Comment les bookmarks aident-ils pour la compatibilité et la rétention ?
Les bookmarks permettent des envois incrémentaux sans garder chaque ancien snapshot. Ils réduisent la pression de rétention et empêchent les jobs de nettoyage de casser les incrémentaux — à condition que les deux côtés supportent les envois basés sur bookmarks.
9) Pourquoi l’estimation de taille du send ne correspond-elle pas aux octets réellement transférés ?
L’estimation est la taille logique du flux. Les octets sur le fil dépendent si les blocs sont déjà compressés, si le flux est compressé, et des overheads SSH/réseau.
10) Puis-je « downgrader » une pool pour correspondre à un système plus ancien afin que la réplication fonctionne ?
Pas vraiment en place. Une fois que des features sont actives, on ne revient généralement pas en arrière. Le downgrade pratique est : créez une pool compatible plus ancienne ailleurs et répliquez-y en utilisant des flux compatibles.
Conclusion : prochaines étapes pratiques
Si vous ne retenez qu’une posture opérationnelle : la compatibilité n’est pas un ressenti, c’est un inventaire. Vous ne « testez un send » pas en espérant que ça marche. Vérifiez les feature flags du pool,
l’état de chiffrement du dataset, la capacité du récepteur, et l’historique des snapshots d’abord — puis transférez les octets.
Prochaines étapes que vous pouvez faire aujourd’hui :
- Exécutez
zpool get feature@*sur chaque point de réplication et conservez les sorties quelque part où les humains peuvent comparer. - Choisissez un mode de réplication conservateur par défaut pour les récepteurs plus anciens et appliquez-le par niveau.
- Implémentez des holds ou bookmarks pour que la rétention ne casse pas votre chaîne incrémentale.
- Entraînez-vous aux restaurations, pas seulement aux envois. Une réplication qui ne peut pas être reçue et montée en toute sécurité n’est qu’un streaming coûteux.