La première fois que vous voyez un envoi incrémental ZFS se terminer en quelques secondes — après qu’une sauvegarde complète de la nuit précédente a pris des heures — on ressent cette rare sensation en infrastructure : quelque chose à la fois élégant et pratique. Les instantanés ZFS vous donnent des points dans le temps immuables, et zfs send/zfs receive peuvent répliquer uniquement ce qui a changé entre ces instantanés. Le bénéfice est évident : sauvegardes plus rapides, moins de trafic réseau et une histoire de restauration qui ne commence pas par « désolé, nous sommes encore en train de copier ».
Mais la réplication incrémentale n’est pas magique. C’est un contrat : votre chaîne d’instantanés, les propriétés des datasets et l’état du récepteur doivent s’aligner. Quand ils ne le font pas, ZFS refusera de deviner. C’est une bonne ingénierie, et parfois une mauvaise journée pour qui a supposé que « incrémental » veut dire « ça marchera tout seul ».
Ce que signifie réellement « envoi incrémental »
Un « flux d’envoi » ZFS est une représentation sérialisée d’un dataset (ou instantané) qui peut être reconstruit sur un autre pool avec zfs receive.
Un envoi complet transfère l’instantané entier. Un envoi incrémental transfère uniquement les blocs qui ont changé entre deux instantanés — « de A à B ».
Les envois incrémentaux ne sont pas des diffs fichier par fichier. ZFS travaille au niveau des blocs. Si un fichier change d’une manière qui réécrit des blocs, ces blocs sont envoyés.
Si rien n’a changé, rien n’est envoyé. Si un fichier a été réécrit en totalité (par exemple une image de disque VM avec une petite modification qui déclenche un grand remaniement des blocs),
vous pouvez tout de même envoyer beaucoup de données de façon incrémentale. « Incrémental » concerne les blocs modifiés, pas les intentions de modification.
Il y a deux modes incrémentaux principaux que vous utiliserez :
-
Incrémental snapshot-à-snapshot :
zfs send -i pool/fs@old pool/fs@newenvoie les changements de@oldvers@new. -
Incrémental de réplication (aka « instantané de base ») :
zfs send -I pool/fs@base pool/fs@newpeut inclure des instantanés intermédiaires dans le flux.
Ce -I (i majuscule) est important lorsque vous voulez que le récepteur ait le même ensemble d’instantanés — pas seulement le plus récent. Dans la plupart des opérations de sauvegarde, l’historique des instantanés fait partie du produit.
Blague #1 : Les sauvegardes incrémentales, c’est comme un régime — si vous « trichez » en supprimant l’instantané de base, les mathématiques ne fonctionnent plus, et vous le ressentirez tout de suite.
Faits et contexte intéressants (ce qui influence vos décisions)
- Les instantanés ZFS sont peu coûteux car ce sont des métadonnées, pas des copies. Le coût apparaît plus tard lorsque les blocs divergent et doivent être conservés pour les anciens instantanés.
- Les flux d’envoi sont déterministes pour un état de dataset donné, mais pas forcément stables entre fonctionnalités. L’activation de nouvelles fonctionnalités de pool/dataset peut affecter la compatibilité avec des récepteurs plus anciens.
- OpenZFS unifie plusieurs lignées de plateformes. Le « ZFS » que vous utilisez sur Linux aujourd’hui descend de Solaris ZFS, avec des années de développement fonctionnel et d’endurcissement opérationnel.
-
De grandes tailles d’enregistrement peuvent rendre les incrémentaux étonnamment volumineux. Si votre dataset utilise
recordsize=1Met qu’une charge réécrit de petites régions de façon aléatoire, vous risquez de chainer de gros blocs. - L’envoi reprenable existe parce que les réseaux sont peu fiables. Les tokens de reprise permettent de continuer un receive après une interruption, plutôt que de redémarrer un flux de plusieurs téraoctets.
-
La réplication ZFS peut préserver les propriétés, ACL et xattrs — mais seulement si vous le demandez. Des options comme
-pet-xchangent ce qui est répliqué et ce qui est écrasé. - Les bookmarks ont été introduits pour conserver des bases incrémentales sans garder des instantanés complets. Ce sont des références légères à un état d’instantané, utiles dans les pipelines de réplication.
- La réplication chiffrée a deux modes : raw et non-raw. La réplication « raw » conserve le chiffrement intact et évite de déchiffrer/rechiffrer côté émetteur.
-
zfs receiveest volontairement strict sur la lignée. Si le dernier instantané du récepteur ne correspond pas à l’instantané de base de l’émetteur, ZFS n’essaiera pas de « faire au mieux » pour vos données.
Modèle opérationnel : instantanés, flux et lignée
Si vous ne retenez qu’un concept, retenez la lignée. L’envoi incrémental ne fonctionne que lorsque le récepteur possède un instantané qui est octet-pour-octet identique à l’instantané de base de l’émetteur.
C’est pourquoi les conventions de nommage des instantanés et les politiques de rétention ne sont pas des « agréables à avoir » — ce sont structurels.
1) La chaîne : base → changements → nouveau
Quand vous exécutez :
cr0x@server:~$ zfs send -i tank/app@2025-12-24_0000 tank/app@2025-12-25_0000 | zfs receive -u backup/app
ZFS calcule quels blocs diffèrent entre ces instantanés et les transmet. Sur le récepteur, ces blocs sont appliqués pour reconstruire @2025-12-25_0000.
Si backup/app n’a pas déjà @2025-12-24_0000 avec une lignée GUID correspondante, le receive échouera.
2) Ensembles d’instantanés vs « seulement le dernier »
Un objectif courant de sauvegarde est : « garder une semaine d’horaires et un mois de quotidiens. » Avec ZFS, cela signifie souvent que vous voulez que le récepteur possède aussi ces instantanés,
pas seulement l’état final. C’est là que -I aide : il peut envoyer une plage incluant des instantanés intermédiaires, préservant l’ensemble.
3) Propriétés, comportement de montage, et pourquoi votre serveur de sauvegarde a soudain monté des datasets de production
Les receives peuvent créer des datasets et des instantanés. Par défaut, un filesystem reçu peut être monté selon des propriétés héritées. Sur un hôte de sauvegarde, vous voulez généralement
que les fichiers répliqués restent démontés pour éviter accès accidentels, indexation, ou agents « serviables » qui les parcourent.
C’est pourquoi vous verrez zfs receive -u partout dans des playbooks sensés : il reçoit le dataset mais ne le monte pas.
Concevoir un workflow de réplication tolérant à la réalité
Un design de réplication exploitable répond à quatre questions :
- Quelle est l’unité de réplication ? Un seul dataset, ou un sous-arbre avec enfants (par ex.
tank/serviceset tous ses descendants). - Comment nommons-nous les instantanés ? Pour que l’automatisation puisse calculer le « dernier instantané commun » et que les humains puissent trier les erreurs rapidement.
- Comment gérons-nous les interruptions ? Receives reprenables, timeouts et surveillance.
- Quelles sont nos invariantes ? « Les sauvegardes sont démontées », « nous répliquons les propriétés », « nous ne répliquons pas les datasets temporaires », etc.
Nommage d’instantanés que les opérateurs peuvent interpréter à 03:00
Évitez les idées trop astucieuses. Utilisez des timestamps triables et un préfixe stable. Par exemple :
auto-2025-12-25_0100, auto-2025-12-25_0200.
Si vous maintenez plusieurs politiques, incluez le nom de la politique :
hourly-2025-12-25_0200, daily-2025-12-25.
Réplication chiffrée raw : quand les sauvegardes ne doivent pas voir le texte en clair
Si vos datasets sont chiffrés et que vous voulez que votre hôte de sauvegarde stocke le ciphertext (et n’exige pas les clés), visez les envois raw :
zfs send -w (ou --raw, selon votre plateforme). Cela conserve la représentation chiffrée sur disque intacte.
C’est un énorme avantage opérationnel : votre système de sauvegarde peut être traité comme du « stockage », pas du « calcul de confiance ».
Réplication des enfants : un flux, plusieurs datasets
Utilisez -R (flux de réplication) quand vous voulez répliquer un dataset et ses descendants, plus les propriétés et instantanés. C’est l’option « apporter tout l’arbre ».
C’est aussi l’option qui peut vous surprendre si vous ne vouliez pas répliquer ce dataset imbriqué qu’un collègue a créé pour des tests temporaires.
Bande passante et CPU : votre pipeline est un système
zfs send produit un flux ; la façon dont vous le transportez importe. Le chiffrement SSH peut devenir votre goulot avant même que les disques le soient.
La compression peut aider si vos données se compressent et si votre CPU est moins cher que votre WAN. Le buffering peut lisser les pics et garder les deux bouts occupés.
Blague #2 : SSH, c’est super jusqu’à ce que vous réalisiez que votre « appliance de sauvegarde » est en fait un radiateur d’appoint avec un port réseau.
Tâches pratiques : commandes que vous exécuterez réellement
Les commandes ci-dessous supposent deux hôtes : une source (prod) et un récepteur de sauvegarde (backup).
Adaptez les noms de pool et de dataset à votre environnement.
Tâche 1 : Confirmer la disposition des datasets et les propriétés de base
cr0x@prod:~$ zfs list -o name,used,avail,refer,mountpoint,compression,recordsize -r tank/app
NAME USED AVAIL REFER MOUNTPOINT COMPRESS RECSIZE
tank/app 220G 1.4T 180G /srv/app lz4 128K
tank/app/db 40G 1.4T 40G /srv/app/db lz4 16K
tank/app/uploads 60G 1.4T 60G /srv/app/uploads lz4 128K
Interprétation : les décisions de réplication se prennent par dataset. Ici, db est réglé avec un recordsize plus petit.
Si vous répliquez le parent avec -R, vous obtenez aussi les enfants — utile quand c’est volontaire.
Tâche 2 : Créer un instantané (dataset unique)
cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs snapshot tank/app@${SNAP}
Interprétation : les instantanés sont instantanés. Le dataset continue d’évoluer après l’instantané ; l’instantané est une vue cohérente dans le temps.
Tâche 3 : Créer des instantanés récursifs (dataset + enfants)
cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs snapshot -r tank/app@${SNAP}
Interprétation : chaque descendant reçoit un instantané avec le même nom. Cette symétrie facilite grandement le scripting de réplication.
Tâche 4 : Faire la première réplication complète vers un récepteur vide
cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs send -R tank/app@${SNAP} | ssh backup 'zfs receive -u -F backup/app'
Interprétation : -R réplique l’arbre de datasets. receive -u le garde démonté. -F force un rollback sur le récepteur si nécessaire.
N’utilisez -F que lorsque vous comprenez l’ampleur des dégâts ; il peut supprimer des modifications côté récepteur.
Tâche 5 : Effectuer une réplication incrémentale vers l’instantané suivant
cr0x@prod:~$ OLD=auto-2025-12-25_0000
cr0x@prod:~$ NEW=auto-2025-12-25_0100
cr0x@prod:~$ zfs snapshot -r tank/app@${NEW}
cr0x@prod:~$ zfs send -R -i tank/app@${OLD} tank/app@${NEW} | ssh backup 'zfs receive -u backup/app'
Interprétation : cela envoie uniquement les changements depuis @OLD tout en maintenant la structure de datasets répliquée.
Si le récepteur manque @OLD (ou a une lignée différente), cela échoue.
Tâche 6 : Utiliser -I pour inclure des instantanés intermédiaires
cr0x@prod:~$ BASE=auto-2025-12-25_0000
cr0x@prod:~$ HEAD=auto-2025-12-25_0600
cr0x@prod:~$ zfs send -R -I tank/app@${BASE} tank/app@${HEAD} | ssh backup 'zfs receive -u backup/app'
Interprétation : le récepteur reçoit tous les instantanés entre @BASE et @HEAD existant sur l’émetteur, pas seulement @HEAD.
C’est ainsi que vous alignez l’historique des instantanés.
Tâche 7 : Vérifier que les instantanés existent et s’alignent aux deux bouts
cr0x@prod:~$ zfs list -t snapshot -o name,creation -s creation -r tank/app | tail -n 5
tank/app@auto-2025-12-25_0200 Wed Dec 25 02:00 2025
tank/app@auto-2025-12-25_0300 Wed Dec 25 03:00 2025
tank/app@auto-2025-12-25_0400 Wed Dec 25 04:00 2025
tank/app@auto-2025-12-25_0500 Wed Dec 25 05:00 2025
tank/app@auto-2025-12-25_0600 Wed Dec 25 06:00 2025
cr0x@backup:~$ zfs list -t snapshot -o name,creation -s creation -r backup/app | tail -n 5
backup/app@auto-2025-12-25_0200 Wed Dec 25 02:01 2025
backup/app@auto-2025-12-25_0300 Wed Dec 25 03:01 2025
backup/app@auto-2025-12-25_0400 Wed Dec 25 04:01 2025
backup/app@auto-2025-12-25_0500 Wed Dec 25 05:01 2025
backup/app@auto-2025-12-25_0600 Wed Dec 25 06:01 2025
Interprétation : les horodatages ne correspondront pas exactement, mais les noms d’instantanés doivent. Quand ils dérivent, c’est votre prochain envoi incrémental qui vous le dira.
Tâche 8 : Estimer la taille de l’envoi avant de s’engager (utile pour les liens WAN)
cr0x@prod:~$ zfs send -n -v -i tank/app@auto-2025-12-25_0500 tank/app@auto-2025-12-25_0600
send from @auto-2025-12-25_0500 to tank/app@auto-2025-12-25_0600 estimated size is 3.14G
total estimated size is 3.14G
Interprétation : -n est une simulation ; -v affiche des estimations. Traitez cela comme une estimation — la compression, le recordsize et le contenu du flux peuvent décaler la réalité.
Tâche 9 : Ajouter compression et buffering dans le pipeline de transport
cr0x@prod:~$ OLD=auto-2025-12-25_0500
cr0x@prod:~$ NEW=auto-2025-12-25_0600
cr0x@prod:~$ zfs send -R -i tank/app@${OLD} tank/app@${NEW} \
| lz4 -z \
| ssh backup 'lz4 -d | zfs receive -u backup/app'
Interprétation : si vos données sont déjà compressées (médias, sauvegardes de sauvegardes), cela peut gaspiller du CPU et réduire le débit.
Si votre goulot est la bande passante WAN, ça peut être un gros gain. Mesurez, ne vibrez pas.
Tâche 10 : Réplication de dataset chiffré avec envoi raw
cr0x@prod:~$ zfs get -H -o property,value encryption tank/secret
encryption on
cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs snapshot tank/secret@${SNAP}
cr0x@prod:~$ zfs send -w tank/secret@${SNAP} | ssh backup 'zfs receive -u backup/secret'
Interprétation : le récepteur stocke des données chiffrées et n’a pas besoin de la clé pour les conserver.
Votre workflow de restauration doit tenir compte de l’endroit où résident les clés et comment vous les chargerez.
Tâche 11 : Utiliser des bookmarks pour préserver une base incrémentale sans conserver les anciens instantanés
cr0x@prod:~$ zfs snapshot tank/app@auto-2025-12-25_0700
cr0x@prod:~$ zfs bookmark tank/app@auto-2025-12-25_0700 tank/app#base-0700
cr0x@prod:~$ zfs list -t bookmark -o name,creation -r tank/app
NAME CREATION
tank/app#base-0700 Wed Dec 25 07:00 2025
Interprétation : les bookmarks sont des « ancres » légères vers un état d’instantané. Ils peuvent servir de bases incrémentales dans de nombreux workflows,
vous permettant de supprimer des instantanés tout en conservant la continuité de réplication — lorsque votre plateforme le prend en charge de bout en bout.
Tâche 12 : Gérer un receive interrompu avec des tokens de reprise
cr0x@backup:~$ zfs get -H -o value receive_resume_token backup/app
1-7d3f2b8c2e-120-789c...
cr0x@prod:~$ ssh backup 'zfs get -H -o value receive_resume_token backup/app'
1-7d3f2b8c2e-120-789c...
cr0x@prod:~$ TOKEN=$(ssh backup 'zfs get -H -o value receive_resume_token backup/app')
cr0x@prod:~$ zfs send -t ${TOKEN} | ssh backup 'zfs receive -u backup/app'
Interprétation : vous reprenez le flux là où il s’est arrêté, au lieu de redémarrer. C’est une de ces fonctionnalités que vous n’appréciez pas avant qu’un lien instable ne tombe à 97%.
Tâche 13 : Élaguer en toute sécurité les instantanés sur l’émetteur après réplication réussie
cr0x@prod:~$ KEEP=auto-2025-12-25_0600
cr0x@prod:~$ zfs list -H -t snapshot -o name -s creation -r tank/app | head
tank/app@auto-2025-12-24_0000
tank/app@auto-2025-12-24_0100
tank/app@auto-2025-12-24_0200
cr0x@prod:~$ zfs destroy tank/app@auto-2025-12-24_0000
cr0x@prod:~$ zfs destroy -r tank/app@auto-2025-12-24_0100
Interprétation : la suppression d’un instantané est permanente. En production, l’élagage des instantanés doit être automatisé mais conservateur, idéalement conditionné par :
« le récepteur a l’instantané X » et « la dernière réplication a réussi ». Si vous supprimez la seule base commune, votre prochain incrémental devient un envoi complet.
Tâche 14 : Vérifier l’intégrité avec un scrub et vérifier les erreurs
cr0x@backup:~$ zpool scrub backup
cr0x@backup:~$ zpool status -v backup
pool: backup
state: ONLINE
status: scrub in progress since Wed Dec 25 09:12:48 2025
1.23T scanned at 2.11G/s, 410G issued at 701M/s, 3.02T total
config:
NAME STATE READ WRITE CKSUM
backup ONLINE 0 0 0
raidz1-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
errors: No known data errors
Interprétation : la réplication déplace les données ; les scrubs les valident au repos. Des sauvegardes sans vérifications d’intégrité périodiques ne sont que de l’optimisme coûteux.
Méthode de diagnostic rapide (trouver le goulot en minutes)
Quand la réplication est lente ou échoue, votre travail est d’éviter la digression de 90 minutes où tout le monde discute du « réseau » alors que le vrai coupable est un seul cœur CPU saturé par SSH.
Voici la séquence que j’utilise parce qu’elle converge rapidement.
Premier point : est-ce que ça échoue rapidement à cause de la lignée ?
Vérifiez le message d’erreur et confirmez que le récepteur possède l’instantané de base (ou la chaîne d’instantanés correcte).
cr0x@backup:~$ zfs list -t snapshot -o name -r backup/app | tail -n 10
backup/app@auto-2025-12-25_0300
backup/app@auto-2025-12-25_0400
backup/app@auto-2025-12-25_0500
backup/app@auto-2025-12-25_0600
Si le récepteur ne possède pas l’instantané de base, la solution n’est pas « réessayer plus fort ». C’est « envoyer un complet » ou « envoyer un incrémental depuis le dernier instantané commun ».
Deuxième point : le récepteur est-il bloqué sur l’I/O disque ?
La réplication est intensive en écritures côté récepteur. Si le pool de sauvegarde est lent, tous les ajustements en amont ne font que masquer le problème.
cr0x@backup:~$ zpool iostat -v 1 5
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
backup 3.2T 5.1T 0 820 0 210M
raidz1-0 3.2T 5.1T 0 820 0 210M
sda - - 0 275 0 70M
sdb - - 0 280 0 71M
sdc - - 0 265 0 69M
-------------------------- ----- ----- ----- ----- ----- -----
Si la bande passante d’écriture est faible et que la latence est élevée (vous le verrez dans les outils système), le récepteur est votre limite.
Causes courantes : surcharge de la parité RAIDZ sur de petites écritures, disques SMR, pool occupé, ou SLOG mal dimensionné pour des charges sync-heavy.
Troisième point : l’émetteur est-il bloqué sur la lecture ?
cr0x@prod:~$ zpool iostat -v 1 5
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 5.8T 2.1T 620 5 640M 2.1M
mirror-0 1.9T 700G 210 2 220M 900K
nvme0n1 - - 210 2 220M 900K
nvme1n1 - - 210 2 220M 900K
-------------------------- ----- ----- ----- ----- ----- -----
Si les lectures sont lentes, vérifiez si le pool émetteur est sous charge (apps, compactions, scrubs) ou si vous thrashiez l’ARC.
Quatrième point : le pipeline de transport est-il le goulot (SSH, compression, buffering) ?
Surveillez le CPU et le débit pendant un envoi. Un symptôme classique est un cœur CPU qui reste bloqué et un flux plafonné à un débit étonnamment constant.
cr0x@prod:~$ ps -eo pid,comm,%cpu,args | egrep 'zfs|ssh|lz4' | head
21433 zfs 35.2 zfs send -R -i tank/app@auto-2025-12-25_0500 tank/app@auto-2025-12-25_0600
21434 lz4 98.7 lz4 -z
21435 ssh 64.1 ssh backup lz4 -d | zfs receive -u backup/app
Si la compression monopolise le CPU et ne réduit pas significativement le volume, supprimez-la. Si SSH monopolise le CPU, envisagez des chiffrements plus rapides (là où la politique le permet)
ou déplacez la réplication sur un réseau dédié avec offload IPsec — toute solution qui déplace le coût crypto.
Cinquième point : est-ce bloqué à cause d’un receive partiel ?
cr0x@backup:~$ zfs get -H -o property,value receive_resume_token backup/app
receive_resume_token 1-7d3f2b8c2e-120-789c...
Si un token de reprise existe, vous avez probablement un receive interrompu qui doit être repris ou annulé avant que de nouvelles réplications puissent se dérouler proprement.
Trois mini-récits du monde de l’entreprise
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une équipe a déployé la réplication ZFS pour une flotte de serveurs de build. L’idée était solide : instantaner le dataset workspace toutes les heures, répliquer sur un hôte de sauvegarde,
et garder une semaine d’historique. Ils ont testé un envoi complet, testé un incrémental, et déclaré victoire.
Deux mois plus tard, un serveur de build est tombé en panne. Le jour de la restauration est arrivé avec la confiance d’un runbook bien répété — jusqu’à ce que zfs receive refuse
la chaîne incrémentale. Le récepteur avait les instantanés, oui. Mais pas les bons instantanés : quelqu’un avait « nettoyé d’anciens backups » sur l’hôte de sauvegarde
pour récupérer de l’espace et avait supprimé un instantané qui était encore la base pour la série incrémentale suivante.
La mauvaise hypothèse était subtile : « Si je supprime d’anciens instantanés sur le récepteur, l’émetteur peut quand même envoyer des incrémentaux depuis ce qu’il a. »
Ce n’est pas ainsi que fonctionne ZFS. Les incrémentaux nécessitent un instantané ancêtre commun. Supprimez l’ancêtre, et l’émetteur ne peut plus calculer un delta que le récepteur peut appliquer.
ZFS n’invente pas une nouvelle base.
La restauration s’est faite via un envoi complet sur un lien qui n’avait jamais été conçu pour cela. Le service est revenu, mais la leçon est restée : la rétention des instantanés sur le récepteur
n’est pas indépendante. Si vous voulez une rétention indépendante, vous avez besoin de bases indépendantes — les bookmarks peuvent aider, tout comme une politique qui élague uniquement après avoir confirmé
que la base suivante existe sur les deux côtés.
Après l’incident, ils ont implémenté une vérification du « dernier instantané commun » dans l’automatisation et ont bloqué les suppressions sur le récepteur à moins que l’instantané correspondant
n’ait été élagué sur l’émetteur en premier (ou qu’une bookmark de base existe). Ce n’était pas glamour, mais cela a transformé la réplication de « ça marche jusqu’à ce que ça casse » en un système.
Mini-récit 2 : L’optimisation qui a mal tourné
Une autre organisation avait un site distant connecté par un WAN modeste. Quelqu’un a eu la brillante idée de « compresser tout » dans le pipeline de réplication
parce que, sur le papier, la compression réduit la bande passante. Ils ont inséré une étape de compression et célébré quand les premiers tests avec des logs textuels ont montré de gros gains.
Puis ils ont pointé le même pipeline sur des datasets VM. Les VMs hébergeaient des bases de données, des caches et plein de blobs déjà compressés. Le résultat : une chute du débit
et un retard de réplication accru. La fenêtre de sauvegarde n’était plus techniquement une « fenêtre » — c’était juste « le temps entre maintenant et pour toujours ».
Le retour de bâton était classique : le CPU est devenu le goulot. La compression bouffait des cycles et générait peu de réduction. SSH chiffrant le flux compressé a aussi consommé plus de CPU
sur les deux bouts. Les baies de stockage étaient inactives tandis que les CPU souffraient.
La correction n’a rien d’héroïque. Ils ont mesuré. Ils ont retiré la compression pour les datasets qui n’en bénéficiaient pas, l’ont conservée pour ceux qui en profitaient, et ont programmé la réplication
aux heures creuses où elle ne concurrencerait pas le CPU applicatif. Pour quelques datasets critiques, ils ont utilisé des envois raw chiffrés, réduisant la surcharge des transformations redondantes.
L’« optimisation » n’était pas mauvaise en principe. Elle était mauvaise comme politique globale. En ingénierie des sauvegardes, les règles universelles sont la source de douleurs universelles.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une entreprise soucieuse de la sécurité utilisait des datasets chiffrés pour les données clients et les répliquait vers un environnement de sauvegarde séparé. L’environnement de sauvegarde était traité
comme semi-trusté : bonnes protections physiques, mais pas assez de confiance pour y stocker des clés de déchiffrement. Ils ont donc utilisé des envois raw et gardé les clés dans un emplacement contrôlé.
Un mercredi qui a commencé comme les autres, un contrôleur de stockage sur le système primaire a commencé à produire des erreurs intermittentes. Pas catastrophique — juste assez pour semer le doute. Ils ont initié
un basculement contrôlé : restaurer les derniers instantanés sur un cluster standby et basculer le trafic. Ce n’était pas la première fois qu’ils exécutaient la procédure ; ils la pratiquaient trimestriellement, comme des adultes.
La restauration s’est déroulée sans accroc parce que les sauvegardes n’étaient pas seulement « présentes », elles étaient prévisibles : les datasets reçus étaient démontés, les propriétés cohérentes,
et l’ensemble des instantanés complet. Surtout, ils avaient une cadence de scrub régulière sur le pool de sauvegarde et surveillaient les erreurs de checksum comme un indicateur critique.
Quand il a fallu faire confiance aux sauvegardes, ils ne faisaient pas confiance à l’espoir.
L’équipe a ensuite plaisanté en disant que la partie la plus excitante de l’incident avait été à quel point la récupération avait été peu excitante. C’est le résultat correct. Si votre histoire DR est palpitante,
elle est probablement aussi coûteuse.
Erreurs courantes, symptômes et corrections
Erreur 1 : Supprimer l’instantané de base (ou diverger le récepteur)
Symptômes : le receive incrémental échoue avec des messages sur des instantanés manquants ou « does not exist », ou « incremental source is not earlier than destination. »
Correction : identifiez le dernier instantané commun et répliquez à partir de là. Si aucun n’existe, faites un envoi complet. Prévenez la récurrence en coordonnant la rétention et/ou en utilisant des bookmarks.
Erreur 2 : Utiliser zfs receive -F par réflexe
Symptômes : « Ça a marché » mais un dataset côté récepteur a perdu des instantanés plus récents ou a été rollbacké de façon inattendue.
Correction : réservez -F aux cas où le récepteur est strictement une cible de réplication et où vous acceptez la sémantique de rollback. Préférez un namespace cible propre et des promotions contrôlées.
Erreur 3 : Répliquer des montages dans l’espace de noms du serveur de sauvegarde
Symptômes : des datasets se montent sur le serveur de sauvegarde ; des agents d’indexation/AV consomment de l’I/O ; des admins parcourent et modifient accidentellement les copies de sauvegarde (ou essayent).
Correction : recevez toujours avec -u, et envisagez de définir canmount=off sur les racines de réplication du récepteur.
Erreur 4 : Confondre -i et -I
Symptômes : le récepteur n’a que le dernier instantané, pas l’historique intermédiaire ; les politiques de rétention ne correspondent pas ; les restaurations manquent le point dans le temps attendu.
Correction : utilisez -I quand vous voulez inclure les instantanés intermédiaires. Utilisez -i quand vous voulez vraiment seulement le delta entre deux instantanés.
Erreur 5 : Supposer qu’« incrémental » signifie « petit » (surtout pour les images VM)
Symptômes : incrémentaux aussi volumineux que des envois complets ; la réplication prend du retard ; les réseaux se saturent de façon inattendue.
Correction : mesurez les estimations d’envoi (zfs send -n -v), ajustez le recordsize des datasets de façon appropriée, et envisagez des agencements conscients de la charge (séparer les données à fort churn dans des datasets distincts).
Erreur 6 : Ne pas planifier les interruptions
Symptômes : receives partiels bloquent les réplications futures ; les redémarrages répétés gaspillent des heures ; le retard de sauvegarde augmente.
Correction : utilisez le receive reprenable et surveillez receive_resume_token. Ajoutez de l’automatisation pour reprendre ou annuler proprement selon la politique.
Erreur 7 : Surprises liées aux versions/fonctionnalités croisées
Symptômes : receive échoue avec des messages sur des fonctionnalités ou versions de flux non supportées.
Correction : standardisez les versions OpenZFS entre émetteur/récepteur quand c’est possible, ou contraignez les envois aux fonctionnalités compatibles via des options spécifiques à la plateforme et une séquence d’upgrade disciplinée.
Listes de contrôle / plan étape par étape
Checklist A : Construire une base de réplication sensée (configuration initiale)
- Choisir une racine de dataset pour la réplication (ex.
tank/app) et décider si les enfants sont inclus. - Définir le nommage des instantanés : préfixe + heure triable.
- Côté récepteur, créer un namespace de pool/dataset dédié (ex.
backup/app) et s’assurer qu’il a suffisamment d’espace pour la rétention. - Définir les valeurs par défaut du récepteur :
canmount=offsur la racine, et planifier d’utiliser toujourszfs receive -u. - Créer le premier instantané récursif :
zfs snapshot -r tank/app@auto-.... - Faire le premier envoi complet avec
-Ret recevoir démonté. - Valider la présence des instantanés aux deux bouts.
- Décider de la rétention et l’implémenter prudemment (élaguer après confirmation que le récepteur a une base sûre).
Checklist B : Opérations quotidiennes (la routine « ne pas déclencher d’alerte »)
- Créer des instantanés selon un planning (horaire/quotidien).
- Répliquer les incrémentaux depuis le dernier instantané répliqué vers le nouvel instantané.
- Après réplication, valider : le dernier instantané existe sur le récepteur et
receive_resume_tokenest vide. - Élaguer les anciens instantanés sur l’émetteur (et éventuellement le récepteur) selon la politique sans casser la chaîne.
- Lancer des scrubs périodiques sur le récepteur et alerter sur les erreurs de checksum.
- Faire des exercices de restauration : monter un clone d’instantané reçu et vérifier l’intégrité au niveau applicatif.
Checklist C : Workflow de restauration (niveau filesystem)
- Identifier l’instantané que vous voulez restaurer.
- Le cloner dans un nouveau dataset sur la cible de restauration (éviter d’écraser des données en production pendant l’investigation).
- Monter le clone, valider le contenu, puis promouvoir ou copier en place selon les besoins de l’application.
FAQ
1) Quelle est la différence entre zfs send -i et -I ?
-i envoie le delta entre exactement deux instantanés. -I envoie une plage de réplication et peut inclure des instantanés intermédiaires, préservant l’historique.
Si vous tenez à conserver de nombreux points de restauration sur le récepteur, -I est généralement le bon choix.
2) Puis-je faire des envois incrémentaux sans garder les anciens instantanés indéfiniment ?
Vous avez besoin d’une base commune. Dans de nombreux environnements, les bookmarks peuvent agir comme bases légères, vous permettant de supprimer des instantanés tout en préservant la continuité de réplication.
Cela dépend de votre version d’OpenZFS et de votre workflow. Sans base (instantané ou bookmark), vous faites un envoi complet.
3) Pourquoi zfs receive échoue alors que les noms d’instantanés correspondent ?
Parce que la lignée compte, pas seulement les noms. Le récepteur doit avoir le même instantané de base en contenu et en GUID d’ascendance. Si l’instantané du récepteur a été créé indépendamment
ou si le dataset a été rollbacké/modifié de manière divergent, ZFS refusera le flux incrémental.
4) Dois-je répliquer les propriétés ?
Souvent oui, car des propriétés comme recordsize, compression et acltype peuvent affecter le comportement des restaurations. Mais soyez intentionnel :
sur les cibles de sauvegarde vous pouvez vouloir un comportement de montage différent (canmount=off, readonly=on) que la production.
5) Est-il sûr d’exécuter la réplication pendant que l’application écrit ?
Oui, parce que vous répliquez un instantané, qui est cohérent. Le dataset live continue de changer, mais l’instantané est une vue stable.
L’important est de prendre l’instantané au bon niveau : pour les bases de données, des instantanés cohérents applicatifs peuvent nécessiter des hooks pré/post (flush, freeze ou checkpoint).
6) Comment savoir si le chiffrement SSH est mon goulot ?
Si le débit est plafonné et l’utilisation CPU élevée sur un cœur pendant les envois, suspectez SSH. Validez en observant la consommation CPU des processus ssh
pendant la réplication et en comparant les performances sur un lien local de confiance ou avec des réglages de transport différents approuvés par votre politique de sécurité.
7) Que me dit vraiment zfs send -n -v ?
Il estime la taille du flux sans l’envoyer. C’est utile pour la planification et pour détecter « pourquoi cet incrémental est énorme », mais ce n’est pas un compteur de facturation.
Traitez-le toujours comme indicatif, pas comme absolu.
8) Les sauvegardes doivent-elles être montées sur le serveur de sauvegarde ?
Dans la plupart des configurations de production, non. Gardez-les démontées pour réduire les lectures/écritures accidentelles, l’indexation et la curiosité humaine. Montez uniquement lors des restaurations ou des tests.
Utilisez zfs receive -u et envisagez canmount=off sur les racines répliquées.
9) Puis-je répliquer un dataset chiffré vers un hôte de sauvegarde non fiable ?
Oui, avec des envois raw. L’hôte de sauvegarde stocke le ciphertext et n’a pas besoin des clés pour le recevoir. Votre processus de restauration devra avoir accès aux clés sur un système de confiance.
10) À quelle fréquence dois-je scrubber le pool de sauvegarde ?
Assez souvent pour détecter une corruption silencieuse avant qu’elle ne devienne votre histoire de restauration. Beaucoup d’opérateurs font un scrub mensuel sur de grands pools et plus fréquemment sur des plus petits,
avec alertes sur toute erreur de checksum. Le rythme « correct » dépend du type de disques, de la taille du pool et de l’importance de votre tranquillité d’esprit.
Conclusion
L’envoi incrémental ZFS est l’un des rares mécanismes de sauvegarde qui s’étend de « un serveur dans un placard » à « une flotte avec de véritables contraintes de conformité »
sans changer ses principes fondamentaux. Les instantanés créent des points dans le temps stables. Les flux incrémentaux déplacent uniquement les blocs modifiés. Les récepteurs reconstruisent l’historique
avec des vérifications d’intégrité intégrées au système de fichiers lui-même.
L’accroche opérationnelle est la lignée : les incrémentaux exigent une base partagée, et vos politiques de rétention, conventions de nommage et automatisation doivent respecter cela.
Faites ce travail, et vous obtiendrez des sauvegardes rapides, vérifiables et faciles à restaurer — sans recopier tout à chaque fois.