Vous avez acheté « 20 To bruts », monté un pool RAIDZ et vous vous êtes senti satisfait. Puis, vers ~70 % d’utilisation, tout a commencé à vaciller :
les écritures ont ralenti, les durées de scrub ont explosé, et votre monitoring criait « pool full » alors que df prétendait
qu’il vous restait encore des téraoctets. Si c’est votre réalité, vous n’êtes pas malchanceux : vous utilisez ZFS tel qu’il a été conçu.
ZFS ne traite pas « l’espace libre » comme un simple seau. Il le traite comme un entrepôt avec des allées, des chariots élévateurs et des
palettes qui ne peuvent pas être réarrangées sans travail. Quand les allées se resserrent, vos chariots cessent d’être efficaces.
Le problème des 70 % : ce que « plein » signifie vraiment en ZFS
Les pools ZFS donnent souvent l’impression d’être pleins bien avant d’atteindre 100 % d’utilisation. Le « nombre magique » que
l’on cite—70 %, 80 %, parfois 85 %—n’est pas de la superstition. C’est une règle empiriquement utile pour savoir quand le comportement
d’allocation bascule de « surtout contigu, rapide et prévisible » vers « fragmenté, lourd en métadonnées et parfois dramatique ».
Deux choses sont vraies en même temps :
- ZFS peut continuer à accepter des écritures au-delà de 70 % d’utilisation du pool, et beaucoup de pools tournent bien à 90 %—jusqu’au jour où ils ne le font plus.
- Les performances et la fiabilité de ZFS sont fortement corrélées avec l’espace libre, surtout pour les écritures aléatoires, les snapshots, le RAIDZ et les charges lourdes en métadonnées.
« Plein » en production concerne moins la capacité brute que la capacité de l’allocateur à trouver des régions contiguës suffisamment grandes
(et à le faire sans brûler le CPU) pour satisfaire les écritures entrantes tout en préservant la redondance et les performances.
Voici le modèle mental qui vous gardera en poste : l’espace libre n’est pas fongible.
Dix téraoctets d’espace libre éparpillés en miettes de 128 Ko ne valent pas dix téraoctets en gros morceaux propres.
ZFS est copy-on-write. Il n’écrase pas les blocs en place ; il écrit de nouveaux blocs et met à jour les pointeurs.
Cela signifie qu’il a constamment besoin d’un nouvel espace pour les changements—même pour les plus petits.
Une petite blague courte, parce que l’univers exige de l’équilibre : ZFS ne manque pas d’espace. Il manque d’espace utile—comme un frigo rempli de condiments quand vous avez faim.
Alors pourquoi 70 % ?
70 % n’est pas une limite stricte ; c’est un témoin d’alerte. Au‑delà, surtout sur des pools avec vdevs RAIDZ, l’allocateur dispose
de moins de larges extents. La fragmentation augmente, le volume de métadonnées en mouvement monte, et chaque écriture devient une petite négociation avec la physique.
Le seuil « correct » dépend de :
- la configuration RAID (mirror vs RAIDZ1/2/3)
- la largeur du vdev
- le choix d’ashift
- recordsize/volblocksize versus la taille d’IO de la charge
- la fréquence et la rétention des snapshots
- l’utilisation de special vdev (métadonnées/petits blocs)
- la fréquence de réécriture des données (bases vs archives)
Si vous voulez une politique qui ne vous fera pas honte devant un comité de revue de changements : prévoyez
~70–80 % maximum pour les pools RAIDZ majoritaires qui traitent des écritures aléatoires ou beaucoup de snapshots, et autorisez
~80–90 % seulement si vous comprenez votre charge et avez un plan de croissance testé.
Faits et histoire qui expliquent ce comportement
Les ingénieurs stockage aiment le folklore. Voici des faits concrets—utiles—qui expliquent pourquoi ZFS se comporte différemment des
systèmes de fichiers et piles RAID traditionnels.
-
ZFS est né chez Sun Microsystems au début des années 2000 comme gestionnaire de volumes et système de fichiers combinés,
conçu pour éviter le jeu du blâme “contrôleur RAID + système de fichiers”. - Le copy-on-write n’a pas été inventé par ZFS, mais ZFS l’a popularisé en stockage d’entreprise : il a rendu les snapshots peu coûteux et assuré une forte cohérence sur disque sans rituels fsck.
-
ZFS utilise un modèle de stockage poolé : les datasets sont découpés logiquement, pas par partitions statiques.
Cette flexibilité signifie aussi que quotas, reservations et snapshots peuvent créer des scénarios « où est passé mon espace ». - RAIDZ n’est pas un “RAID5 logiciel”. Il utilise des écritures à bande variable et un comportement d’allocation différent. Il peut être efficace, mais il est aussi plus sensible à la fragmentation que les mirrors.
- L’allocateur travaille en metaslabs (blocs d’espace par vdev), avec plusieurs stratégies selon la fragmentation et les extents disponibles. Quand les metaslabs se remplissent, l’allocation devient plus coûteuse.
- Il existe un “slop space” volontaire : ZFS réserve une partie du pool pour maintenir le système opérationnel même quand les utilisateurs tentent de le remplir. Ce n’est pas de l’espace gaspillé ; c’est un mécanisme anti-panique.
- Le recordsize par défaut de 128 Ko a une histoire : c’est un compromis favorable au débit pour les charges séquentielles, pas une vérité universelle pour les bases de données ou disques VM.
-
Les disques à secteurs 4K ont tout changé : choisir
ashift=9vsashift=12impacte l’efficacité de l’espace et les performances. Une mauvaise décision coûte pour longtemps. - Les special vdevs sont plus récents dans l’évolution d’OpenZFS : ils sont puissants pour métadonnées/petits blocs, mais ils créent aussi un nouveau mode d’échec : « vous pouvez remplir la mauvaise chose en premier ».
Rien de tout cela n’est de la trivialité. C’est la raison pour laquelle votre pool peut être techniquement « pas plein » et pratiquement inutilisable.
Où va l’espace : la mécanique derrière la falaise
1) Le copy-on-write transforme les mises à jour en allocations
Sur un système de fichiers traditionnel qui réécrit en place, une mise à jour peut réutiliser le même emplacement. Sur ZFS, mettre à jour un bloc
signifie généralement écrire le nouveau bloc ailleurs, puis mettre à jour atomiquement la chaîne de pointeurs jusqu’à la racine.
C’est fantastique pour la cohérence. C’est aussi pourquoi ZFS a besoin d’air.
Quand le pool est spacieux, ZFS peut choisir des allocations contiguës et propres. Quand le pool est encombré, l’allocateur commence à prendre ce qu’il trouve.
Votre « petite mise à jour » devient plusieurs écritures fragmentées plus des mises à jour de métadonnées.
Multipliez par une base de données effectuant des écritures 8K et vous avez la situation.
2) Amplification RAIDZ : la parité n’est pas gratuite, et les petites écritures non plus
Les mirrors sont simples : écrire deux copies, c’est fini. RAIDZ nécessite de la parité, et pour les écritures partielles de bande il peut être nécessaire de lire
les anciennes données/parités pour recalculer la parité (read-modify-write) à moins de pouvoir effectuer des écritures en pleine bande.
Quand l’espace libre diminue et que la fragmentation augmente, ZFS a plus de mal à assembler des allocations en pleine bande.
Le résultat : plus de partial stripes, plus de RMW, plus d’opérations IO par écriture logique, plus de latence.
3) La fragmentation n’est pas un seul chiffre ; c’est le niveau de douleur de l’allocateur
ZFS rapporte la fragmentation au niveau du pool, mais l’allocateur la subit par metaslab.
Un pool peut afficher « seulement » 30 % de fragmentation et avoir quand même plusieurs metaslabs hostiles à l’allocation.
Point clé : la fragmentation augmente naturellement dans les systèmes COW quand les données sont réécrites et que des snapshots existent.
C’est gérable avec de l’espace libre. C’est moche sans.
4) Les métadonnées et blocs indirects grossissent avec les snapshots et le churn
Les snapshots ne dupliquent pas les données immédiatement ; ils préservent des versions de blocs. Cela signifie que supprimer des fichiers ne libère pas forcément l’espace si un snapshot référence encore les blocs anciens.
Fort churn + rétention longue des snapshots = usine à fragmentation. Chaque réécriture laisse derrière elle des blocs historiques épinglés par des snapshots. Votre « espace libre » devient un musée des données d’hier.
5) La réserve « slop space » : votre pool ne ment pas, vous n’êtes juste pas invité
ZFS garde un peu d’espace en réserve pour que le système puisse continuer à fonctionner, supprimer des données et terminer des transactions.
Cela évite la cascade classique « disque plein → le système ne peut plus écrire les logs → la situation empire ».
Concrètement, cela signifie que lorsque vous êtes très plein, vous pouvez constater des différences déroutantes entre :
zpool list, zfs list et df. Ils ne rapportent pas tous la même chose,
et une partie de cet « espace libre » n’est pas destinée à votre application.
6) Reservations, refreservations et quotas : les bloqueurs silencieux d’espace
ZFS permet de réserver de l’espace pour des datasets (reservation), et pour les volumes il existe
refreservation (espace garanti pour ce dataset lui‑même, pas pour ses descendants).
C’est un bon outil—jusqu’à ce que quelqu’un mette une reservation « au cas où » et l’oublie.
Les quotas peuvent aussi rendre le « pool free » sans rapport pour un dataset. Le pool peut avoir de l’espace ; votre dataset peut être bloqué.
7) Special vdevs et petits blocs : vous pouvez remplir les métadonnées avant les données
Les special vdevs (souvent des SSD) stockent les métadonnées et, optionnellement, les petits blocs. Si vous les sous‑dimensionnez, votre pool peut se retrouver dans une mauvaise posture : le special vdev se remplit et l’allocation devient contrainte même si les vdevs de données principaux ont encore de la place.
C’est une de ces fonctionnalités qui est puissante entre de bonnes mains et une opportunité de développement de carrière entre de mauvaises.
8) ashift et overhead de padding : mort par alignement
Si vous choisissez ashift trop petit par rapport à la taille de secteur physique des disques, vous pouvez subir une amplification d’écriture et des problèmes de performance. Si vous le choisissez trop grand, vous perdez de l’espace sur les petits blocs à cause du padding.
Avec beaucoup de petits fichiers ou de métadonnées, ce gaspillage devient visible rapidement.
Mode opératoire de diagnostic rapide
Quand quelqu’un signale « ZFS est plein » ou « ZFS est lent » et que vous devez trier vite, ne vous égarez pas. Faites ceci dans l’ordre.
L’objectif est d’identifier si vous êtes face à (a) une vraie exhaustion de capacité, (b) un épinglage par snapshots, (c)
des contraintes de quota/reservation, (d) une saturation de special vdev, ou (e) de la fragmentation de l’allocateur.
Première étape : confirmer ce que « plein » signifie
- Vérifiez l’utilisation et la santé du pool :
zpool list,zpool status - Vérifiez l’usage des datasets vs disponible :
zfs list - Vérifiez si un quota/reservation bloque les écritures :
zfs get quota,reservation,refquota,refreservation
Deuxième étape : trouvez ce qui épingle l’espace
- Vérifiez les snapshots et leur espace « used » :
zfs list -t snapshot - Vérifiez si des suppressions ne libèrent rien à cause des snapshots : comparez dataset
usedvsrefer
Troisième étape : diagnostiquer la douleur de l’allocateur
- Vérifiez la fragmentation :
zpool list -o fragmentation - Vérifiez la saturation metaslab/special vdev :
zpool list -v,zpool status -v - Vérifiez la latence IO et l’ordonnancement :
zpool iostat -v 1
Quatrième étape : décidez si vous avez besoin d’un soulagement immédiat ou de changements structurels
- Soulagement immédiat : supprimer des snapshots, réduire la rétention, retirer des reservations, ajouter de l’espace, déplacer des datasets « chauds ».
- Structurel : repenser la largeur des vdevs, déplacer DB/VMs sur des mirrors, ajouter correctement un special vdev, ajuster recordsize/volblocksize, corriger la politique de snapshots.
Tâches pratiques : commandes, sorties et décisions (12+)
Voici les vraies manœuvres opérationnelles. Chaque tâche inclut : commande, sortie exemple, ce que cela signifie, et l’action à prendre.
Les commandes supposent OpenZFS sur Linux, mais les concepts s’appliquent largement.
Task 1: Check pool capacity and basic health
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 20.1T 7.08T - - 41% 74% 1.00x ONLINE -
Meaning: The pool is 74% used and 41% fragmented. “ONLINE” is good; the allocator pain is not.
Decision: At ~74% with 41% frag, treat this as “approaching the cliff” for RAIDZ + churny workloads.
Start planning relief (space, snapshot cleanup, workload moves).
Task 2: Check per-vdev allocation balance (and catch a stealth bottleneck)
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 20.1T 7.08T - - 41% 74% 1.00x ONLINE -
raidz2-0 18.1T 14.9T 3.22T - - 48% 82% - ONLINE
raidz2-1 9.06T 5.23T 3.83T - - 19% 57% - ONLINE
Meaning: One vdev is at 82% and highly fragmented while the other is at 57%. Allocation imbalance happens;
it hurts because the fullest vdev becomes the limiter.
Decision: Treat capacity planning at the vdev level, not pool level. Consider adding vdevs of equal size,
or migrating data to rebalance (send/receive), or rethinking the layout.
Task 3: Confirm dataset-level view (what apps experience)
cr0x@server:~$ zfs list
NAME USED AVAIL REFER MOUNTPOINT
tank 20.1T 5.92T 160K /tank
tank/prod 18.4T 5.92T 12.2T /tank/prod
tank/prod/vm 9.80T 5.92T 9.80T /tank/prod/vm
tank/prod/db 6.10T 5.92T 6.10T /tank/prod/db
tank/backups 1.62T 5.92T 1.62T /tank/backups
Meaning: Datasets all show the same AVAIL because they share pool free space unless constrained by quotas.
Decision: If the app says “no space left” but AVAIL looks fine, suspect quotas/reservations or slop space,
not raw capacity.
Task 4: Catch quotas and reservations that are quietly choking you
cr0x@server:~$ zfs get -r quota,refquota,reservation,refreservation tank/prod
NAME PROPERTY VALUE SOURCE
tank/prod quota none default
tank/prod refquota none default
tank/prod reservation none default
tank/prod refreservation none default
tank/prod/vm quota 10T local
tank/prod/vm refquota none default
tank/prod/vm reservation none default
tank/prod/vm refreservation 9T local
Meaning: tank/prod/vm is guaranteed 9T regardless of descendants, and capped at 10T.
This can starve other datasets or make “free space” look misleading.
Decision: If the pool is tight, remove or right-size refreservations unless they’re protecting something
mission-critical. Most environments overuse them.
Task 5: Identify snapshot space pinning (the classic “I deleted files and nothing happened”)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation | tail -n 6
tank/prod/db@auto-2026-02-03_0100 0B 6.10T Mon Feb 3 01:00 2026
tank/prod/db@auto-2026-02-03_0200 18G 6.10T Mon Feb 3 02:00 2026
tank/prod/db@auto-2026-02-03_0300 22G 6.10T Mon Feb 3 03:00 2026
tank/prod/db@auto-2026-02-03_0400 25G 6.10T Mon Feb 3 04:00 2026
tank/prod/db@auto-2026-02-03_0500 27G 6.10T Mon Feb 3 05:00 2026
tank/prod/db@auto-2026-02-03_0600 31G 6.10T Mon Feb 3 06:00 2026
Meaning: Snapshots are accumulating “USED” (changed blocks). This is space that cannot be freed until snapshots expire.
Decision: If you need emergency space, delete the biggest/oldest snapshots first (with care), then fix retention.
If “USED” is tiny, snapshots are not your main villain today.
Task 6: Find datasets with suspicious divergence between USED and REFER
cr0x@server:~$ zfs list -o name,used,refer,compressratio -r tank/prod | head
NAME USED REFER COMPRESSRATIO
tank/prod 18.4T 12.2T 1.12x
tank/prod/vm 9.80T 9.80T 1.01x
tank/prod/db 6.10T 6.10T 1.37x
Meaning: tank/prod USED is much larger than REFER because snapshots/children are pinning space.
Decision: Investigate snapshot policy and churny datasets. Move VM and DB datasets into separate pools or vdev types
if they’re dominating rewrite traffic.
Task 7: Check pool fragmentation and capacity in one glance
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag
NAME SIZE ALLOC FREE CAP FRAG
tank 27.2T 20.1T 7.08T 74% 41%
Meaning: FRAG is a coarse signal. Past ~50% fragmentation on a busy pool is where “free space exists” but “fast writes do not.”
Decision: If FRAG rises along with CAP, prioritize adding space or reducing churn. Fragmentation rarely “heals” on its own.
Task 8: Observe real-time IO pressure and which vdev is hurting
cr0x@server:~$ zpool iostat -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 20.1T 7.08T 220 980 18.4M 96.7M
raidz2-0 14.9T 3.22T 170 770 14.1M 76.2M
raidz2-1 5.23T 3.83T 50 210 4.3M 20.5M
-------------------------- ----- ----- ----- ----- ----- -----
Meaning: raidz2-0 is doing most writes and is also the fuller, more fragmented vdev. That’s a performance trap.
Decision: If one vdev is hot and full, add capacity in a way that doesn’t worsen imbalance (add another similar vdev,
or migrate data off the hot vdev via replication to a new pool).
Task 9: Confirm ashift (space/perf overhead you can’t “tune later”)
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head -n 8
38: vdev_tree:
49: ashift: 12
73: ashift: 12
Meaning: ashift: 12 means 4K sectors. Good for modern drives; it avoids misalignment pain.
Decision: If you discover ashift=9 on 4K drives, don’t “hope it’s fine.” Plan a migration rebuild.
It’s one of the few ZFS mistakes that sticks like gum.
Task 10: Check special vdev presence and whether it’s near full
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
sde ONLINE 0 0 0
sdf ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
Meaning: A special vdev exists (mirrored NVMes). Now you must treat it as critical metadata storage.
Decision: Immediately check special vdev allocation and ensure monitoring alerts on it separately. If it fills,
the pool can become write-constrained in ugly ways.
Task 11: See special vdev allocation at the top level
cr0x@server:~$ zpool list -v tank | sed -n '1,8p'
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 20.1T 7.08T - - 41% 74% 1.00x ONLINE -
raidz2-0 18.1T 14.9T 3.22T - - 48% 82% - ONLINE
special 1.81T 1.55T 260G - - 12% 85% - ONLINE
Meaning: Special vdev is 85% full. That’s not “fine.” That’s a fire drill in slow motion.
Decision: Stop allocating small blocks/metadata there if it’s undersized (special_small_blocks),
add special capacity if your platform supports it safely, or migrate to a corrected pool design.
Task 12: Check recordsize and volblocksize (misalignment with workload causes amplification)
cr0x@server:~$ zfs get recordsize,compression tank/prod/db
NAME PROPERTY VALUE SOURCE
tank/prod/db recordsize 128K default
tank/prod/db compression zstd local
Meaning: DB dataset is using 128K recordsize. For many databases doing 8K/16K IO, this can increase read/modify/write and
fragmentation under churn.
Decision: Consider setting recordsize=16K (or aligned to your DB page size) for new data,
and validate with a benchmark. Don’t cargo-cult; test.
Task 13: Show compression ratio and decide whether compression is a capacity lever
cr0x@server:~$ zfs list -o name,used,refer,compressratio -r tank/prod | grep -E 'db|vm'
tank/prod/vm 9.80T 9.80T 1.01x
tank/prod/db 6.10T 6.10T 1.37x
Meaning: VM data is barely compressible; DB data is moderately compressible.
Decision: Compression won’t save VM pools. For DBs, it might buy time, but don’t treat it as a capacity plan.
Task 14: Identify large snapshots quickly (who’s hoarding yesterday)
cr0x@server:~$ zfs list -t snapshot -o name,used -s used | tail -n 5
tank/prod/vm@weekly-2026-01-05 412G
tank/prod/vm@weekly-2026-01-12 438G
tank/prod/vm@weekly-2026-01-19 451G
tank/prod/vm@weekly-2026-01-26 466G
tank/prod/vm@weekly-2026-02-02 489G
Meaning: Weekly VM snapshots are each pinning hundreds of GB. That’s expected with churn, but it’s also why your pool “fills early.”
Decision: Tighten snapshot retention for VM datasets or move VM snapshots to a dedicated backup target via replication.
Task 15: Validate that deletes can actually reclaim space (not blocked by holds)
cr0x@server:~$ zfs holds tank/prod/vm@weekly-2026-02-02
NAME TAG TIMESTAMP
tank/prod/vm@weekly-2026-02-02 keep Mon Feb 3 09:12 2026
Meaning: A snapshot hold exists. Someone “protected” it. Space won’t be freed until the hold is released.
Decision: Audit holds. If the hold is unjustified, release it and delete the snapshot. If it’s required for compliance, stop arguing with physics and add storage.
Task 16: Estimate logical vs physical usage (and catch hidden overhead)
cr0x@server:~$ zfs get -o name,property,value -s local,default logicalused,logicalreferenced,used,refer tank/prod
NAME PROPERTY VALUE
tank/prod logicalused 19.9T
tank/prod logicalreferenced 12.2T
tank/prod used 18.4T
tank/prod refer 12.2T
Meaning: Logical used is higher than physical used due to compression, but the gap between USED and REFER indicates snapshot/child effects.
Decision: Use logical metrics for “how much the application thinks it has,” and physical used for “how full the pool really is.”
Trois mini-récits d’entreprise depuis les tranchées du stockage
1) L’incident causé par une mauvaise hypothèse : « L’espace libre est de l’espace libre »
Une entreprise de taille moyenne faisait tourner une plateforme d’analytics interne sur un pool ZFS RAIDZ2. Rien de sophistiqué : quelques gros datasets,
snapshots nocturnes, et un flux régulier de jobs ETL. Quelqu’un a remarqué que le pool restait autour de 78 % pendant des mois et a jugé que c’était « sûr » parce qu’il n’atteignait jamais 90 %.
Puis ils ont ajouté une nouvelle charge : un job intensif en écritures qui réécrivait quelques téraoctets par jour. Sur le papier, ça rentrait. En pratique, le pool a commencé à timeout.
Les logs applicatifs montraient des messages sporadiques « no space left » ; le pool avait pourtant plusieurs téraoctets libres. On a blâmé la base de données. On blâme la base de données comme les marins blâment la mer.
La vraie cause était le comportement de l’allocateur sous churn avec des snapshots. Le job ETL n’écrivait pas seulement de nouvelles données ; il réécrivait des blocs existants.
Copy-on-write plus snapshots signifiait que les anciennes versions restaient épinglées. L’espace libre s’est fragmenté, les metaslabs sont devenus plus difficiles à allouer,
et RAIDZ a eu du mal à construire des bandes efficaces. La latence a monté jusqu’à ce que la logique de retry du job achève le reste.
La correction fut peu glamour. Ils ont mis en pause les nouveaux snapshots pour le dataset churny, réduit la rétention, et déplacé l’espace scratch ETL vers un pool en mirror conçu pour les écritures aléatoires.
La leçon retenue : « pool free » n’est pas la même chose que « pool healthy », et %CAP n’est pas une garantie de performance.
2) L’optimisation qui s’est retournée contre eux : « Activez la dédup ; le stockage coûte cher »
Une autre organisation voulait réduire la facture de stockage pour images VM et backups. Ils ont activé la déduplication sur un dataset avec beaucoup de données similaires. La première semaine semblait prometteuse—jusqu’à ce que la pression mémoire apparaisse,
et que les boîtiers de stockage commencent à swapper sous charge.
La dédup dans ZFS n’est pas un interrupteur que l’on actionne parce que ça paraît efficace. C’est un engagement de conception : elle crée une table de dédup qui doit être consultée pour les écritures, et elle prospère avec de la RAM et un accès métadonnées à faible latence.
Ils n’avaient pas assez de RAM, et leurs métadonnées vivaient sur les mêmes disques rotatifs que tout le reste.
À mesure que le pool se remplissait, les tables de dédup et l’activité métadonnées empirèrent. La latence monta, les performances devinrent instables, et la suppression de snapshots—normalement une tâche d’entretien—se transforma en un événement du nuit.
Ils n’avaient pas seulement réduit la taille du pool ; ils avaient rendu le pool plus difficile à gérer quand l’espace devient limité.
Ils ont fini par désactiver la dédup (après migration des données, car on ne la « désactive » pas et ne récupère pas tout immédiatement),
se sont appuyés sur la compression, et ont repensé les backups pour éviter de stocker plusieurs copies complètes. L’optimisation qui a mal tourné n’était pas la dédup en soi : c’était l’hypothèse que l’efficience de capacité est toujours la bonne priorité.
3) La pratique ennuyeuse mais correcte qui a sauvé la mise : « Toujours garder de la marge, toujours avoir un plan de croissance »
Une société liée à la finance faisait tourner des API client sur un pool ZFS, avec des SLOs de latence stricts. Leur ingénieur stockage n’était pas populaire en période de budget parce qu’il insistait pour garder 25–30 % d’espace libre et un plan de croissance écrit. Ça semblait conservateur. Ça l’était.
Un trimestre, un changement produit a augmenté l’amplification d’écriture : plus d’index, plus de petites mises à jour, plus de churn de snapshots. Ils ont vu les signaux précoces—fragmentation qui grimpe, special vdev qui se remplit, temps de scrub qui s’allongent—et l’ont traité comme un événement planifié plutôt que comme une urgence.
Ils ont exécuté un playbook pré‑approuvé : ajouter un nouvel ensemble de vdevs, rééquilibrer en migrant le dataset le plus bruyant par réplication vers un pool neuf, puis ajuster la rétention des snapshots sur les datasets à churn. Les utilisateurs n’ont rien remarqué.
La direction n’a rien remarqué non plus, et c’est le meilleur compliment que l’on puisse faire aux opérations.
La pratique ennuyeuse n’était pas seulement « garder de la marge ». C’était instrumentation plus une règle de décision : si CAP > 75% et frag > 35% sur un pool chaud, démarrez la ticket d’extension. Pas de héros. Pas de calculs nocturnes.
Erreurs courantes : symptôme → cause racine → correctif
1) « J’ai supprimé plein de données mais l’espace n’est pas revenu »
Symptôme : Le dataset montre moins de données actives, mais ALLOC du pool change à peine.
Cause racine : Des snapshots référencent encore les blocs ; les suppressions ne retirent que les références actuelles.
Correctif : Identifiez les snapshots à forte valeur USED (zfs list -t snapshot -o name,used -s used), supprimez‑les ou réduisez la rétention, et retirez les holds si approprié.
2) « Le pool dit 10 % libre, mais les écritures échouent avec ENOSPC »
Symptôme : Les applications renvoient des erreurs ; zpool list affiche encore du FREE.
Cause racine : Slop space, quota/refquota, ou exhaustion d’un metaslab sur un vdev (le free au niveau pool n’aide pas si un vdev est le goulot).
Correctif : Vérifiez quotas/reservations ; inspectez le CAP des vdevs individuellement ; ajoutez de la capacité ou migrez des données. Ne tentez pas de « tenir bon ».
3) « Les performances se sont effondrées après ~80 % »
Symptôme : Pics de latence, chute du débit, CPU en hausse dans les chemins IO du noyau.
Cause racine : Fragmentation + écritures partielles RAIDZ + churn de métadonnées.
Correctif : Créez de la marge (supprimez des snapshots, ajoutez des vdevs, déplacez les charges bruyantes). À long terme, utilisez des mirrors pour les datasets intensifs en écritures aléatoires.
4) « Un vdev est presque plein, mais le pool ne l’est pas »
Symptôme : Le CAP du pool semble OK, mais un vdev de premier niveau montre un CAP/FRAG très élevé.
Cause racine : Déséquilibre d’allocation suite à l’ajout de vdevs de tailles différentes ou à des décisions historiques d’allocation.
Correctif : Ajoutez des vdevs qui correspondent en taille aux vdevs existants ; envisagez la migration des données vers un nouveau pool pour réagencer.
5) « Le special vdev s’est rempli et maintenant tout est bizarre »
Symptôme : Les écritures sont lentes ou échouent ; les opérations metadata stagnent ; le pool a encore de la place sur les vdevs HDD.
Cause racine : Special vdev sous‑dimensionné pour les métadonnées/petits blocs ; c’est une dépendance critique.
Correctif : Arrêtez le déport agressif de petits blocs, redimensionnez en ajoutant du special vdev si c’est sûr, ou reconstruisez le pool. Surveillez le CAP du special vdev comme s’il s’agissait d’oxygène.
6) « On a mis des reservations “au cas où” et maintenant on manque d’espace »
Symptôme : Certains datasets ont de l’espace, d’autres ne peuvent pas croître ; le free du pool apparaît erroné.
Cause racine : Reservations/refreservations qui accumulent de l’espace hors de l’usage réel.
Correctif : Supprimez ou redimensionnez les reservations ; ne les conservez que pour des datasets réellement critiques avec des garanties dures.
7) « On a tuné recordsize et c’était pire »
Symptôme : Plus d’IO, plus de fragmentation, latence plus élevée après un « changement de perf ».
Cause racine : Mismatch recordsize/volblocksize avec la charge, et données existantes non réécrites aux nouvelles tailles.
Correctif : Benchmarquez avec des patterns IO représentatifs ; appliquez par dataset ; réécrivez les données si la nouvelle géométrie doit s’appliquer.
Listes de contrôle / plan étape par étape
Politique de planification de l’espace (quoi faire, pas quoi débattre)
- Fixez une cible de CAP par type de pool :
- RAIDZ + snapshots + écritures aléatoires : cible ≤ 75–80 % utilisé.
- Mirrors + majoritairement séquentiel + faible churn : cible ≤ 85–90 % utilisé (avec monitoring).
- Planifiez au niveau vdev : un seul vdev « chaud » peut définir votre seuil de panne.
- Supposez que les snapshots vont croître : la rétention consomme de la capacité, ce n’est pas une case à cocher.
- Documentez les actions de croissance : ce que vous ajoutez, combien de temps cela prend et qui l’approuve.
- Surveillez le special vdev séparément : traitez‑le comme un pool à l’intérieur du pool.
Plan de marge d’urgence (quand vous avez besoin d’espace aujourd’hui)
- Confirmez que vous n’êtes pas bloqué par des quotas/reservations ; retirez d’abord les éléments accidentels.
- Supprimez les snapshots les plus gros/anciens que vous pouvez enlever en sécurité ; faites attention aux holds.
- Arrêtez temporairement de créer de nouveaux snapshots pour les datasets churny.
- Déplacez les workloads temporaires/scratch hors du pool contraint.
- Ajoutez de la capacité (nouveaux vdevs) si vous pouvez le faire rapidement et en sécurité ; sinon répliquez vers un pool neuf.
Plan de correction structurelle (pour que cela n’arrive plus)
- Classifiez les workloads : disques VM, bases de données, logs, backups, archives médias.
- Adaptez l’agencement à la charge :
- Écritures aléatoires lourdes : mirrors (et suffisamment de vdevs pour l’IOPS).
- Majoritairement séquentiel, besoin de capacité : RAIDZ2/3 avec marge planifiée.
- Fixez recordsize/volblocksize par dataset selon le profil IO, pas selon l’intuition.
- Snapshotez avec intention : rétention plus courte pour les datasets churny, plus longue pour les données majoritairement append-only.
- Automatisez les alertes « CAP + FRAG » avec une politique opérationnelle dure : quand les seuils sont franchis, le processus d’extension démarre.
Une idée de fiabilité qui mérite d’être volée
Idée paraphrasée, attribuée : « How Complex Systems Fail » de Richard Cook soutient que la sécurité est un problème de contrôle, pas un problème de composant.
La planification d’espace est cela en miniature : vous n’obtenez pas de fiabilité en espérant que les pools restent propres ; vous l’obtenez en contrôlant la marge et le taux de changement.
FAQ
1) La règle « garder ZFS sous 80 % » est‑elle toujours correcte ?
Non. C’est un bouton de politique. Pour les pools RAIDZ avec churn et snapshots, 70–80 % est une valeur par défaut solide.
Pour les pools mirror avec des écritures majoritairement séquentielles et peu de churn, vous pouvez monter plus haut—si vous surveillez la fragmentation et la latence et que vous avez un chemin d’extension testé.
2) Pourquoi ZFS ralentit‑il plus qu’ext4 quand l’espace se resserre ?
Le copy‑on‑write implique que les mises à jour nécessitent de nouvelles allocations, et RAIDZ préfère des écritures larges et bien alignées pour être efficace.
Quand l’espace libre est fragmenté, ZFS passe plus de temps à trouver de l’espace, les écritures sont moins contiguës, et le travail de parité augmente.
3) Pourquoi df et zfs list ne sont‑ils pas d’accord sur l’espace libre ?
Ils rapportent des couches différentes. df rapporte ce que le système de fichiers monté annonce, tandis que zfs list rend compte de la comptabilité des datasets dans le pool,
influencée par quotas, reservations et comportement du slop space.
4) Les snapshots consomment‑ils de l’espace même si rien ne change ?
Un snapshot en lui‑même est peu coûteux, mais ce sont les changements après le snapshot qui coûtent de l’espace, parce que les anciennes versions de blocs doivent être préservées.
Si un dataset est majoritairement en append, les snapshots croissent lentement. S’il réécrit des données, le USED des snapshots augmente vite.
5) Puis‑je « défragmenter » un pool ZFS ?
Pas en place comme les vieux outils de défrag. La défragmentation pratique est la migration : zfs send | zfs receive vers un pool ou dataset neuf,
ou la réécriture des données vers de nouveaux blocs. La marge est le vrai antidote à la fragmentation.
6) Ajouter un vdev résout‑il la fragmentation ?
Cela améliore souvent le comportement d’allocation parce que les nouvelles écritures vont vers les vdevs plus vides, mais cela ne réécrit pas magiquement les anciens layouts fragmentés.
C’est un soulagement, pas une machine à remonter le temps.
7) Quelle est la plus grande erreur de planification d’espace avec les special vdevs ?
Les sous‑dimensionner puis oublier qu’ils sont désormais critiques. Si le special vdev se remplit, vous pouvez subir de graves problèmes de performances ou des échecs d’allocation.
Dimensionnez‑les avec marge, mirrorisez‑les et surveillez‑les explicitement.
8) Dois‑je utiliser la compression comme stratégie de capacité ?
Utilisez la compression car c’est souvent un gain net (surtout zstd) et cela peut réduire l’IO.
Mais ne bâtissez pas un plan qui nécessite un ratio de compression précis pour rester solvable ; les données changent et « compressible » n’est pas une propriété contractuelle.
9) Pourquoi mes snapshots VM hebdomadaires deviennent‑ils énormes ?
Les disques VM churnent : mises à jour OS, fichiers de log, swap, bases dans les VMs et écritures aléatoires. Les snapshots préservent les anciens blocs, donc churn = croissance des snapshots.
La solution est la politique de rétention, la stratégie de réplication, et parfois déplacer le stockage VM sur un agencement qui tolère mieux le churn.
10) Quelle est l’action immédiate la plus sûre quand le pool approche du plein ?
Créez de la marge : réduisez la rétention des snapshots et supprimez les snapshots à fort USED que vous pouvez enlever en sécurité, puis ajoutez de la capacité.
N’attendez pas le déclencheur « 100 % utilisé » ; ZFS peut se retrouver opérationnellement coincé avant cela.
Prochaines étapes que vous pouvez faire cette semaine
- Fixez une limite explicite par pool (selon workload et type de vdev). Si vous ne pouvez pas expliquer pourquoi c’est 85 % au lieu de 75 %, c’est 75 %.
-
Implémentez une alerte « CAP + FRAG » avec
zpool list, et alertez des humains avant que les utilisateurs ne vous alertent. - Auditez la rétention des snapshots sur les datasets churny (VMs, DBs). Si les snapshots servent de stratégie de backup, très bien—faites payer la cible de backup, pas la production.
- Auditez les quotas/reservations. Supprimez tout ce qui existe « au cas où ». Le « au cas où » est la manière dont le stockage meurt discrètement.
- Vérifiez l’utilisation du special vdev. S’il dépasse 70–80 %, considérez‑le comme un travail d’ingénierie urgent.
- Rédigez un playbook de croissance : qui approuve l’extension, comment ajouter des vdevs, et quand choisir la migration plutôt que l’extension.
Deuxième petite blague, parce que vous en aurez besoin lors du prochain examen de capacité : la seule chose plus optimiste que les prévisions de vente, c’est un pool de stockage planifié à 99 %.
La discipline centrale est simple : ZFS veut de la marge comme les bases de données veulent des index—parce que l’alternative, c’est le chaos,
et le chaos trouve toujours une fenêtre de maintenance que vous n’avez pas planifiée.