ZFS a une habitude étonnante : un pool atteint « 90% utilisés » et soudain il se comporte comme si la piste d’atterrissage était finie. Les écritures ralentissent, les allocations échouent et votre supervision s’illumine comme une machine à pinball. Mais zpool list affirme encore qu’il reste de l’espace. Si vous avez déjà murmuré « ZFS me ment », vous n’êtes pas seul — et vous n’avez pas totalement tort. ZFS fait des calculs que votre modèle mental n’inclut probablement pas.
Ceci est l’histoire du slop space, plus des autres mains invisibles qui font que les pools semblent pleins avant 100% : copy-on-write (CoW), métadonnées, alignement des blocs, snapshots, réservations, vdevs spéciaux et fragmentation. Nous l’aborderons comme un guide d’opérateur, pas une brochure : ce qui se passe, comment le mesurer, comment le corriger, et comment éviter de reproduire la même panne avec un autre numéro de ticket.
Ce qu’est le slop space (et ce que ce n’est pas)
Le slop space est la manière dont ZFS vous empêche de conduire un système de fichiers CoW droit dans un mur. Lorsqu’un pool se remplit trop, ZFS est de plus en plus susceptible de rencontrer des échecs d’allocation au pire moment possible : lors de la réécriture des métadonnées, lors de l’engagement des groupes de transactions, ou lorsqu’il tente d’allouer un nouveau bloc pour remplacer un ancien. CoW signifie « ne pas écraser sur place » ; cela signifie « allouer nouveau, puis mettre à jour les pointeurs ». Cette mise à jour elle-même nécessite de l’espace.
Le slop space est un coussin réservé que ZFS essaie de garder en réserve pour les usages généraux afin que le pool puisse encore faire le « travail interne » nécessaire pour rester cohérent et continuer à progresser. Pensez-y comme la voie de secours sur une autoroute : vous pouvez y circuler, mais si tout le monde le fait, les ambulances n’arrivent plus aux accidents.
Opérationnellement, le slop se manifeste par : « Available » dans zfs list étant plus petit que prévu, et parfois un dataset refusant des écritures même si zpool list indique de l’espace. Les détails varient selon la plateforme (version OpenZFS, intégration OS), mais la leçon opérateur est constante : ne prévoyez pas d’utiliser les derniers pourcents d’un pool, et ne considérez pas « 100% » comme une cible de conception exploitable.
Ce que le slop space n’est pas
- Ce n’est pas un quota : les quotas sont par dataset et appliqués à la frontière du dataset. Le slop concerne la survie du pool.
- Ce n’est pas de l’« espace perdu » : il fait toujours partie du pool. Il peut redevenir disponible dans certaines conditions et sert aux allocations internes.
- Ce ne sont pas des snapshots : les snapshots sont des blocs ordinaires maintenus vivants par des références. Le slop est un comportement de type réservation appliqué par l’allocateur.
Blague #1 : Si vous pensez pouvoir faire tourner un pool ZFS à 99,9% pour toujours, vous n’êtes pas optimiste — vous êtes un ingénieur du chaos bénévole.
Pourquoi les pools semblent pleins avant 100%
Le « slop space » est le titre accrocheur, mais dans les incidents réels ce n’est rarement le seul acteur. Les pools semblent pleins tôt parce que l’espace restant n’est pas également utilisable pour toutes les écritures futures. ZFS a besoin d’espace de la bonne forme : des extents libres correspondant aux tailles de blocs, de l’espace sur les vdevs adéquats, et suffisamment de marge pour faire CoW et mettre à jour les métadonnées. Voici les causes pratiques, dans l’ordre où elles mordent le plus souvent les systèmes de production.
1) CoW a besoin d’espace de travail
Sur un système de fichiers CoW, une petite écriture utilisateur peut déclencher une chaîne d’allocations :
- Nouveaux blocs de données
- Nouveaux blocs indirects (si l’arbre s’agrandit ou doit être réécrit)
- Nouveaux blocs de métadonnées (pointeurs de bloc, dnodes, classes d’allocation)
- Frees différés (espace récupéré plus tard, pas immédiatement)
C’est pourquoi « j’écris seulement 1 GB » peut échouer alors que le pool affiche « 2 GB libres ». Dans la dernière phase d’un pool plein, l’allocateur peut devoir tester de nombreux candidats, fragmentant davantage et consommant des métadonnées. ZFS n’est pas dramatique ; il essaie de ne pas se corrompre.
2) Slop space : la marge de sécurité au niveau du pool
Le slop space est typiquement calculé comme une fraction de la taille du pool avec un plafond, et la mécanique exacte a évolué selon les implémentations et versions. L’effet : une partie de l’espace libre est traitée comme effectivement indisponible pour les datasets ordinaires afin de préserver l’opérabilité du pool. Quand vous voyez un dataset indiquer « 0 avail » alors que le pool a encore « un peu » d’espace, vous regardez souvent le slop en action.
Ce n’est pas un « 3% » fixe. Sur les grands pools il peut être plafonné ; sur les petits pools il peut être proportionnellement significatif. Dans la vraie vie, sur de petites machines de labo on a l’impression que ZFS vous pique votre argent de poche ; sur des pools de plusieurs centaines de téraoctets c’est perçu comme une taxe sensée pour la stabilité.
3) Enregistrements 128 KiB, secteurs 4K et la tyrannie de l’alignement
La plupart des datasets ZFS ont par défaut un recordsize autour de 128 KiB. La plupart des disques présentent des secteurs physiques de 4K ; certains présentent du 512e ; certains dispositifs flash ont des tailles de page internes plus grandes. ZFS doit aligner les écritures sur ashift, l’exposant de la taille de secteur du pool. Un pool avec ashift=12 utilise des secteurs 4K ; ashift=13 utilise 8K, etc.
Si votre charge écrit beaucoup de petits blocs, le pool peut « utiliser » l’espace plus vite que ne le suggèrent vos tailles de fichiers à cause du padding, de l’overhead de parité et des métadonnées. Et si vous avez choisi un ashift trop grand « pour la performance », vous avez peut-être aussi choisi de brûler de la capacité de façon permanente. (Nous verrons l’histoire où ça a mal tourné.)
4) Parité RAIDZ et le problème du « dernier tour »
L’allocation RAIDZ est intelligente, mais ce n’est pas magique. À mesure que l’espace libre se fragmente, ZFS a moins d’options pour assembler des stripes complets. Cela peut augmenter l’amplification d’écriture et réduire l’espace libre effectif. Les derniers 10–20% d’un pool RAIDZ peuvent paraître comme de la mélasse : pas parce que ZFS est paresseux, mais parce que les « bonnes » allocations sont plus difficiles à trouver, et l’allocateur doit travailler plus dur pour éviter des layouts pathologiques.
Les mirrors se dégradent généralement plus gracieusement sous fragmentation que RAIDZ, mais ils n’échappent pas aux besoins de marge pour CoW.
5) Les métadonnées sont de l’espace réel
ZFS stocke beaucoup de métadonnées : pointeurs de blocs, dnodes, blocs indirects, spacemaps, checksums, et (selon les fonctionnalités) des structures supplémentaires. Plus vous avez de fichiers, de snapshots, de clones et de petits blocs, plus vous transportez de métadonnées.
Si vous ajoutez un vdev spécial pour les métadonnées et petits blocs, la comptabilité change : les métadonnées peuvent ne plus consommer l’espace « normal », mais vous pouvez alors heurter un autre mur — la capacité du vdev spécial. Quand le vdev spécial se remplit, le pool peut être effectivement immobilisé même si les vdevs principaux ont de la place.
6) Snapshots : l’espace qu’on ne voit pas tant qu’il n’est pas trop tard
Les snapshots ne copient pas les données à la création ; ils épinglent des blocs. Vous pouvez supprimer un répertoire de 10 TB et ne voir presque aucun espace revenir, parce que les snapshots référencent encore ces blocs. C’est là que « le pool semble plein tôt » devient « le pool est plein et on ne sait pas pourquoi ».
Les snapshots augmentent aussi les métadonnées et peuvent accroître la fragmentation parce que les blocs libérés ne sont pas réellement libérés tant que toutes les références n’ont pas disparu.
7) Réservations, refreservations et pièges volblocksize
Les réservations (pour les filesystems) et les refreservations (souvent utilisées pour les zvols) réservent de l’espace pour qu’un dataset puisse continuer à écrire sous pression. C’est bien — mais cela peut aussi faire paraître d’autres datasets pleins tôt parce que l’espace restant est promis ailleurs.
Les zvols ajoutent une couche : la taille de bloc du volume (volblocksize) interagit avec la charge et ashift. Un mauvais ajustement peut gaspiller de l’espace et accroître l’amplification d’écriture, ce qui rend la « queue » du pool plus laide.
8) Frees différés et timing des groupes de transactions
ZFS regroupe le travail en groupes de transactions (TXGs). Les frees peuvent être différés ; l’espace peut être « logiquement libéré » mais pas immédiatement réutilisable tant que le TXG concerné n’est pas commité et que le sync n’est pas terminé. Dans un pool sous forte charge d’écriture, vous pouvez constater un écart entre ce que les applications pensent avoir libéré et ce que l’allocateur peut réellement réutiliser tout de suite.
Blague #2 : « Il dit 5% libres » est l’équivalent stockage de « j’arrive dans cinq minutes » — cela peut être vrai, mais vous ne devriez pas parier votre déploiement de production dessus.
Faits intéressants et historique
Quelques points de contexte qui expliquent pourquoi ZFS se comporte ainsi — et pourquoi la falaise du « pool plein » est un compromis connu et conçu plutôt qu’un bug.
- ZFS est né au milieu des années 2000 avec le checksum bout-en-bout et CoW comme principes centraux, sacrifiant la simplicité de l’écrasement en place pour l’intégrité et la sémantique des snapshots.
- La règle des « 80% » prédatent ZFS : les systèmes de fichiers Unix classiques (comme UFS/FFS) réservaient de l’espace (souvent ~10%) pour root et pour réduire la fragmentation. Mécanisme différent, même objectif : éviter la mort par disque plein.
- RAIDZ n’est pas RAID5 : RAIDZ intègre allocation et parité dans le système de fichiers, évitant le write hole grâce à CoW et des sémantiques transactionnelles.
- ashift est pour toujours : une fois un pool créé, le choix de la taille de secteur est effectivement permanent pour ce vdev. Les gens apprennent cela juste après avoir appris ce que signifie ashift.
- Les vdevs spéciaux ont changé la donne : les métadonnées et petits blocs peuvent être redirigés vers des dispositifs plus rapides, mais cela a aussi introduit un nouveau mode « pool semble plein » : l’épuisement du vdev spécial.
- Le sprawl de snapshots est un tueur de capacité moderne : ZFS a rendu les snapshots bon marché à créer, ce qui a mené à l’habitude de « snapshotter tout » — et à la réalisation que supprimer ne signifie pas reclamation.
- La compression a changé la planification de capacité : avec lz4 et consorts, « used logique » et « used physique » divergent, ce qui complique l’intuition humaine et rend l’espace libre incohérent.
- Copy-on-write implique une amplification d’écriture : surtout sous fragmentation et RAIDZ, de petites écritures aléatoires peuvent coûter plus d’IO physique et de mises à jour de métadonnées que leur taille ne le suggère.
- La comptabilité d’espace a évolué avec les versions OpenZFS : des champs comme usedby* et des améliorations de reporting ont facilité l’attribution d’espace, mais ont aussi exposé combien de compartiments différents l’espace peut remplir.
Trois mini-récits du monde de l’entreprise
Mini-récit #1 : L’incident causé par une mauvaise hypothèse
Ils avaient un joli tableur : taille du pool, croissance prévue, et une ligne « alerte à 90% ». Le pool était en RAIDZ2, principalement des backups séquentiels la nuit, et un cluster de virtualisation occupé la journée. Tout le monde était d’accord : 90% est agressif, mais gérable. Le système avait tourné ainsi pendant des mois, ce qui explique comment les mauvaises hypothèses gagnent de la longévité.
Puis ce fut la fin de trimestre. Un job batch a produit plus de données que d’habitude, quelques VM ont été migrées à chaud, et une fenêtre de backup a chevauché un job de rétention de snapshots. L’espace libre a plongé dans la zone inconfortable, mais le tableau de bord affichait encore « quelques téraoctets libres ». L’ingénieur de garde a essayé de rester calme, parce que le calme coûte moins cher que la panique.
À 02:13, la latence d’écriture a grimpé. À 02:17, quelques VM ont signalé des erreurs disque. À 02:19, le pool a commencé à lancer des ENOSPC pour un dataset qui « aurait dû avoir de l’espace ». Ce n’était pas un mensonge : le dataset avait atteint le point où ZFS ne cédait plus la dernière marge du pool pour les allocations ordinaires. Slop space plus fragmentation signifiait que « libre » n’était pas « allocatable ».
La mauvaise hypothèse n’était pas « 90% c’est OK ». La mauvaise hypothèse était traiter l’espace libre du pool comme un nombre unique qui prédit l’allocabilité. Ils ont récupéré en supprimant des snapshots de courte durée, en mettant en pause les backups, et en déplaçant quelques disques VM hors du pool. Le post-mortem a changé deux choses : les alertes déclenchent plus tôt, et chaque rapport de capacité inclut désormais fragmentation et attribution de snapshots — pas seulement le pourcentage utilisé.
Mini-récit #2 : L’optimisation qui s’est retournée contre eux
Une équipe de stockage voulait « anticiper » les plaintes de performance. Ils avaient des NVMe inoccupés, ils ont donc ajouté un vdev spécial pour accélérer les métadonnées et les petits IO. Sur le papier, c’était parfait : opérations de répertoire plus rapides, latence plus basse pour les petites lectures, et une plateforme de virtualisation plus heureuse.
Ça a fonctionné — jusqu’à ce que ça ne fonctionne plus. Au fil des mois, le vdev spécial s’est rempli silencieusement. Pas avec les données utilisateurs, pas avec les « gros fichiers », mais avec des métadonnées, de petits blocs, et l’accumulation de fonctionnalités « utiles » du système de fichiers. Personne ne surveillait l’allocation du vdev spécial séparément ; le pool avait encore beaucoup de place sur le RAIDZ principal. La supervision voyait 60% d’utilisation du pool et se rendormait.
Puis un jour une opération routinière — créer beaucoup de petits fichiers lors d’un build CI — a commencé à échouer. ZFS devait allouer des métadonnées, et la classe qui stockait ces métadonnées était effectivement à court d’espace. Le pool principal avait de la place, mais l’allocateur ne pouvait pas placer les blocs requis là où la politique l’exigeait. L’équipe avait optimisé le chemin chaud et créé accidentellement un point unique de défaillance de capacité.
La correction n’était pas glamour : ajouter plus de capacité au vdev spécial (et le mirrorer correctement), rééquilibrer en déplaçant certains petits blocs de datasets vers l’allocation normale lorsque possible, et définir des alertes spécifiques sur l’utilisation du vdev spécial. La leçon : chaque optimisation crée une nouvelle ressource à épuiser. Si vous ne la surveillez pas, vous n’êtes que l’auteur d’un nouveau mode de panne.
Mini-récit #3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Un autre atelier utilisait ZFS pour des bases de données et des logs. Rien d’exotique. Leur responsable stockage avait une règle : aucun pool ne devrait passer du temps significatif au-dessus de ~75% sauf s’il existe un plan écrit pour l’extension. Ils avaient aussi une autre règle : les snapshots doivent avoir des politiques de rétention explicites, et chaque dataset doit déclarer s’il est « snapshot-heavy » ou « snapshot-light ».
Un après-midi, un développeur a déployé par erreur une build qui a doublé le volume des logs. Le dataset de logs a grossi. Le système de supervision n’a pas juste sonné sur « pool à 85% ». Il a sonné sur « pool projeté pour atteindre 80% dans 6 heures » et « tendance de l’espace snapshots en accélération ». Ce n’est pas de la magie ; c’est des maths ennuyeuses et un étiquetage cohérent.
L’ingénieur de garde a ralenti l’ingestion des logs, réduit la fréquence des snapshots pour les datasets non critiques, et augmenté la rétention pour le dataset de base de données seulement. Ils avaient aussi une étape de runbook pré-approuvée : augmenter temporairement un quota de dataset pour un service critique tout en réduisant le quota d’un dataset moins critique. Pas d’achats d’urgence. Pas de karaoké de reproches à minuit.
Le pool n’a jamais franchi la falaise où la fragmentation et le slop space deviennent existentiels. Le rapport d’incident tenait sur une page, ce qui est le meilleur type de rapport d’incident. La leçon : la marge est une fonctionnalité, et les règles ennuyeuses sont souvent les seules qui tiennent sous stress.
Tâches pratiques : commandes + interprétation
L’objectif ici n’est pas de balancer des commandes au mur. C’est de bâtir un modèle mental qui correspond à la comptabilité de ZFS : réalité au niveau pool, promesses au niveau dataset, et « pourquoi je ne peux pas allouer alors que ça a l’air d’être le cas ». Les exemples supposent OpenZFS sur un système Linux typique, mais les concepts se traduisent.
Tâche 1 : Vérifier la capacité du pool et la santé basique
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 87.2T 71.4T 15.8T - - 41% 81% 1.00x ONLINE -
Interprétation : CAP est l’instrument brut. FRAG est un voyant d’alerte, pas un verdict, mais une fois que la fragmentation monte, les « 10–15% restants » deviennent moins utilisables. Un pool à 81% avec 41% de frag peut déjà être serré pour des charges d’écriture aléatoire.
Tâche 2 : Voir l’allocation détaillée des vdev et ashift
cr0x@server:~$ zdb -C tank | sed -n '1,120p'
MOS Configuration:
version: 5000
name: 'tank'
vdev_tree:
type: 'root'
id: 0
guid: 1234567890123456789
children[0]:
type: 'raidz'
id: 0
ashift: 12
nparity: 2
children[0]:
type: 'disk'
path: '/dev/disk/by-id/ata-DISK0'
children[1]:
type: 'disk'
path: '/dev/disk/by-id/ata-DISK1'
children[2]:
type: 'disk'
path: '/dev/disk/by-id/ata-DISK2'
Interprétation : ashift vous indique l’unité d’allocation minimale. Si elle est plus grande que vos besoins physiques réels, vous avez réduit définitivement la capacité utilisable. Si elle est plus petite que la réalité (rare avec les valeurs par défaut modernes), la performance et l’amplification d’écriture peuvent en souffrir.
Tâche 3 : Comparer vue pool vs dataset de l’espace disponible
cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -S used tank
NAME USED AVAIL REFER MOUNTPOINT
tank 71.4T 9.82T 96K /tank
tank/vm 52.1T 2.40T 42.0T /tank/vm
tank/backup 18.9T 7.40T 18.9T /tank/backup
Interprétation : Le FREE du pool était 15.8T, mais le dataset de niveau supérieur montre 9.82T d’available. Cet écart est là où vivent slop space, réservations et réalités comptables. Le nombre qui importe pour les applications est souvent AVAIL du dataset, pas FREE du pool.
Tâche 4 : Inspecter réservations et refreservations
cr0x@server:~$ zfs get -H -o name,property,value,source reservation,refreservation tank tank/vm tank/backup
tank reservation none default
tank refreservation none default
tank/vm reservation 2T local
tank/vm refreservation none default
tank/backup reservation none default
tank/backup refreservation none default
Interprétation : Une réservation réduit ce que les autres peuvent utiliser. Si un dataset est réservé et majoritairement vide, le reste du pool peut paraître « plein tôt ». C’est souvent intentionnel, mais cela doit être visible dans les rapports de capacité.
Tâche 5 : Attribuer l’espace aux snapshots, enfants et refreservation
cr0x@server:~$ zfs list -o name,used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation tank/vm
NAME USED USEDBYSNAPSHOTS USEDBYDATASET USEDBYCHILDREN USEDBYREFRESERVATION
tank/vm 52.1T 8.4T 43.7T 0B 0B
Interprétation : 8.4T épinglés par des snapshots ne sont pas « libérables » tant que les snapshots existent. Si vous avez supprimé des images VM et que l’espace n’est pas revenu, c’est généralement la raison.
Tâche 6 : Lister les snapshots et trouver les plus lourds
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s used | tail -n 8
tank/vm@auto-2025-12-20 210G 39.8T Sat Dec 20 02:00 2025
tank/vm@auto-2025-12-21 240G 40.1T Sun Dec 21 02:00 2025
tank/vm@auto-2025-12-22 310G 40.5T Mon Dec 22 02:00 2025
tank/vm@auto-2025-12-23 480G 41.0T Tue Dec 23 02:00 2025
tank/vm@auto-2025-12-24 1.2T 42.0T Wed Dec 24 02:00 2025
tank/vm@pre-migration 3.6T 38.2T Wed Dec 24 18:11 2025
tank/vm@quarter-end 6.1T 37.9T Thu Dec 25 01:05 2025
tank/vm@baseline 7.8T 36.4T Fri Dec 12 03:00 2025
Interprétation : USED du snapshot montre les blocs uniques retenus par ce snapshot. Si un snapshot écrase tous les autres, c’est un candidat prioritaire pour revue (pas suppression automatique — revue).
Tâche 7 : Vérifier le ratio de compression et usage logique vs physique
cr0x@server:~$ zfs get -H -o name,property,value compressratio,compression tank/vm
tank/vm compressratio 1.45x -
tank/vm compression lz4 local
Interprétation : La compression peut masquer la croissance jusqu’à ce qu’elle ne le fasse plus. Quand les données entrantes deviennent moins compressibles (backups chiffrés, médias déjà compressés, blocs aléatoires), l’utilisation physique peut augmenter plus vite que votre tendance historique.
Tâche 8 : Vérifier la fragmentation de l’espace libre au niveau pool
cr0x@server:~$ zpool get -H -o name,property,value frag,capacity,free tank
tank frag 41% -
tank capacity 81% -
tank free 15.8T -
Interprétation : FRAG qui monte avec CAP est normal ; FRAG qui monte vite à CAP modéré indique souvent un mismatch de charge (VM random writes sur RAIDZ, snapshots intensifs, ou churn constant).
Tâche 9 : Inspecter l’allocation du vdev spécial (si présent)
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
ata-DISK0 ONLINE 0 0 0
ata-DISK1 ONLINE 0 0 0
ata-DISK2 ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
nvme-NVME0 ONLINE 0 0 0
nvme-NVME1 ONLINE 0 0 0
errors: No known data errors
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE
tank 87.2T 71.4T 15.8T
raidz2-0 87.2T 68.9T 18.3T
special 1.8T 2.5T 0B
Interprétation : Si special affiche « 0B free », vous avez un problème même si le vdev principal a de l’espace. Le pool peut échouer des allocations qui exigent des blocs de classe spéciale (métadonnées/petits blocs selon les propriétés). C’est le schéma « optimisation qui s’est retournée contre eux ».
Tâche 10 : Identifier les datasets utilisant des classes d’allocation spéciales
cr0x@server:~$ zfs get -H -o name,property,value special_small_blocks tank tank/vm tank/backup
tank special_small_blocks 0 default
tank/vm special_small_blocks 16K local
tank/backup special_small_blocks 0 default
Interprétation : Si special_small_blocks est défini, les petits blocs vont vers special. C’est génial jusqu’à ce que special se remplisse. Sachez quels datasets en dépendent.
Tâche 11 : Vérifier les quotas et refquotas qui peuvent créer un « semble plein » au niveau dataset
cr0x@server:~$ zfs get -H -o name,property,value quota,refquota tank/vm
tank/vm quota 55T local
tank/vm refquota none default
Interprétation : Le quota limite l’usage total (y compris les snapshots selon le type) ; refquota limite l’espace référencé. Si un dataset atteint son quota, il paraît plein indépendamment de l’état du pool.
Tâche 12 : Confirmer si les frees sont différés (espace non immédiatement récupéré)
cr0x@server:~$ zpool iostat -v tank 2 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 71.4T 15.8T 210 980 82.1M 311M
raidz2-0 68.9T 18.3T 200 940 80.4M 305M
special 2.5T 0B 10 40 1.7M 6.1M
---------- ----- ----- ----- ----- ----- -----
tank 71.4T 15.8T 190 1020 75.2M 340M
raidz2-0 68.9T 18.3T 180 980 73.6M 333M
special 2.5T 0B 10 40 1.6M 6.6M
---------- ----- ----- ----- ----- ----- -----
Interprétation : Des écritures soutenues lourdes, surtout avec un churn de snapshots, peuvent maintenir le pool dans un état où l’espace est « en mouvement ». Si vous supprimez des données et tentez immédiatement une grosse écriture, vous pouvez encore rencontrer ENOSPC car l’allocateur ne peut pas encore réutiliser l’espace libéré. C’est là que la patience (ou une pause contrôlée de la charge) peut compter.
Tâche 13 : Réduire la pression des snapshots en toute sécurité (exemple de workflow)
cr0x@server:~$ zfs destroy -nvp tank/vm@baseline
would destroy tank/vm@baseline
would reclaim 7.8T
cr0x@server:~$ zfs destroy -vp tank/vm@baseline
will destroy tank/vm@baseline
will reclaim 7.8T
destroyed tank/vm@baseline
Interprétation : Utilisez -nvp d’abord : simulation plus verbeux avec sortie relativement parsable. Si vous essayez d’échapper à un événement de pool plein, c’est l’un des leviers les plus propres — à condition que le snapshot soit effectivement sûr à supprimer.
Tâche 14 : Repérer « l’espace promis ailleurs » via logicalused/available
cr0x@server:~$ zfs get -H -o name,property,value logicalused,logicalavailable tank
tank logicalused 102T -
tank logicalavailable 14.2T -
Interprétation : Avec la compression, logique et physique divergent. Si logicalavailable est bas alors que vous « pensez » avoir de l’espace physique, vous pouvez être confronté à des contraintes comme slop, réservations, ou épuisement du vdev spécial.
Playbook de diagnostic rapide
Ceci est la séquence « le pager sonne ». L’objectif est d’identifier quel type de « plein » vous avez : pool plein, dataset limité, blocage par snapshot, espace promis par une réservation, vdev spécial épuisé, ou fragmentation / échec d’allocation.
Premier : établir quel type d’échec vous voyez
- Erreur applicative : ENOSPC ? EIO ? timeouts ? Ne supposez pas qu’ENOSPC signifie « pas d’octets libres » ; cela peut signifier « aucun espace allocable sous la politique ».
- Périmètre : un dataset ou tout le pool ? un zvol ou tous les systèmes de fichiers ?
- Timing : cela a-t-il commencé après un snapshot, une réplication, une grosse suppression, ou l’ajout d’un vdev spécial ?
Deuxième : vérifier la réalité du pool
cr0x@server:~$ zpool list tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 87.2T 71.4T 15.8T - - 41% 81% 1.00x ONLINE -
cr0x@server:~$ zpool status -x tank
pool 'tank' is healthy
Décision : Si HEALTH n’est pas ONLINE/healthy, arrêtez et corrigez cela d’abord. Les jeux de capacité ne comptent pas si vous êtes dégradé et en resilvering.
Troisième : comparer dataset avail avec pool free
cr0x@server:~$ zfs list -o name,used,avail,mounted -r tank | head
NAME USED AVAIL MOUNTED
tank 71.4T 9.82T yes
tank/vm 52.1T 2.40T yes
tank/backup 18.9T 7.40T yes
Décision : Si le free du pool est « raisonnable » mais que l’available du dataset est minuscule, vous traitez probablement avec slop, réservations, quotas, ou contraintes de capacité du vdev spécial.
Quatrième : attribuer rapidement l’espace
cr0x@server:~$ zfs list -o name,used,usedbysnapshots,usedbydataset,usedbychildren,usedbyrefreservation -r tank | head -n 15
NAME USED USEDBYSNAPSHOTS USEDBYDATASET USEDBYCHILDREN USEDBYREFRESERVATION
tank 71.4T 0B 96K 71.4T 0B
tank/vm 52.1T 8.4T 43.7T 0B 0B
tank/backup 18.9T 1.1T 17.8T 0B 0B
Décision : Si les snapshots dominent, récupérer de l’espace signifie changer la politique de snapshots, pas seulement supprimer des fichiers.
Cinquième : vérifier le vdev spécial et les contraintes de classe d’allocation
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE
tank 87.2T 71.4T 15.8T
raidz2-0 87.2T 68.9T 18.3T
special 1.8T 2.5T 0B
Décision : Si special est plein, traitez-le comme un incident de capacité haute sévérité. Libérer de l’espace « normal » n’aidera pas forcément si l’allocateur a besoin de blocs de classe spéciale.
Sixième : vérifier la fragmentation et la forme de la charge
cr0x@server:~$ zpool get frag tank
NAME PROPERTY VALUE SOURCE
tank frag 41% -
Décision : FRAG élevé plus CAP élevé suggère que vous avez besoin d’espace immédiatement. La correction la plus rapide est généralement de supprimer snapshots, déplacer des datasets, ou étendre le pool. La défragmentation n’est pas un bouton; c’est une discipline de capacité et de cycle de vie.
Erreurs courantes (symptômes et corrections)
Erreur 1 : Prendre « pool FREE » pour « dataset peut écrire »
Symptôme : zpool list montre des téraoctets libres, mais les écritures échouent sur un dataset ou un zvol.
Causes probables : seuil de slop atteint ; quota de dataset ; réservation ailleurs ; vdev spécial plein.
Correction : Comparez AVAIL de la cible avec zfs list ; vérifiez quotas/réservations ; inspectez l’utilisation du vdev spécial ; libérez l’espace au bon endroit.
Erreur 2 : Datasets à snapshots lourds sans discipline de rétention
Symptôme : Supprimer des fichiers ne libère pas d’espace ; usedbysnapshots augmente ; l’utilisation du pool grimpe « mystérieusement ».
Correction : Utilisez zfs list -t snapshot -o used pour identifier les snapshots lourds ; élaguez en toute sécurité ; redessinez la rétention (moins de snapshots, rétention plus courte, ou stratégie de réplication qui n’épinglera pas à jamais).
Erreur 3 : Ajouter des vdevs spéciaux sans dimensionnement ni alerting
Symptôme : Le pool semble correct, mais les opérations metadata/petits fichiers échouent ; special vdev montre proche du plein ou plein.
Correction : Surveillez l’allocation du vdev spécial ; étendez le vdev spécial (mirroir) ; envisagez d’ajuster special_small_blocks pour certains datasets ; évitez de faire du spécial le goulot d’étranglement de capacité.
Erreur 4 : Sur-optimiser ashift ou recordsize pour la « performance »
Symptôme : La capacité est inférieure aux attentes ; les charges à petites écritures consomment vite l’espace ; la performance se dégrade près du plein.
Correction : Choisissez ashift en fonction de la réalité physique, pas du folklore. Ajustez recordsize/volblocksize selon la charge. Si vous avez déjà fait un mauvais choix, planifiez une migration ; ashift n’est pas un réglage qu’on inverse.
Erreur 5 : Faire tourner des pools RAIDZ trop chauds avec des charges d’écriture aléatoire
Symptôme : Pics de latence à mesure que le pool se remplit ; fragmentation qui monte ; allocations qui ralentissent ; « espace restant » devient inutilisable.
Correction : Gardez plus de marge ; envisagez des mirrors pour charges random-write lourdes (VMs, bases de données) ; utilisez special vdev avec soin ; réduisez le churn de snapshots.
Erreur 6 : Supposer que les frees sont immédiats pendant un churn intense
Symptôme : Vous supprimez beaucoup, mais AVAIL n’augmente pas rapidement ; les écritures immédiates échouent encore.
Correction : Laissez les TXGs se synchroniser ; réduisez temporairement la pression d’écriture ; vérifiez que les snapshots n’épinglent pas les blocs ; évitez « supprimer puis réécrire tout » pendant un incident.
Listes de contrôle / plan pas à pas
Checklist A : Rapport de capacité qui reflète la réalité
- Rapporter zpool CAP, zpool FRAG, et zpool FREE.
- Rapporter les datasets principaux avec USED, AVAIL, et usedbysnapshots.
- Rapporter l’allocation du vdev spécial séparément (si présent).
- Inclure le résumé quotas/réservations pour les datasets « critiques ».
- Tracer la croissance en utilisant l’usage physique (pas seulement logique) si la compression est activée.
Checklist B : Quand un dataset dit « plus d’espace »
- Vérifier l’AVAIL du dataset : zfs list dataset.
- Vérifier quota/refquota : zfs get quota,refquota.
- Vérifier reservation/refreservation : zfs get reservation,refreservation.
- Vérifier l’épinglage par snapshots : zfs list -o usedbysnapshots et lister les snapshots.
- Vérifier la santé du pool et l’espace libre du vdev spécial.
- Si vous avez besoin d’espace d’urgence : supprimer des snapshots avec dry-run d’abord ; mettre en pause les charges très churny ; déplacer des données hors pool si possible.
Checklist C : Éviter la zone de « risque de queue »
- Définir des seuils d’alerte sur pool CAP (tôt) et sur le taux de changement (plus tôt).
- Définir des seuils d’alerte sur FRAG et sur l’utilisation du vdev spécial.
- Définir une utilisation maximale cible par pool selon la charge (les VMs ont besoin de plus de marge que les archives).
- Maintenir des politiques de snapshots explicites, revues et appliquées.
- Tester des scénarios « suppression de données » : confirmer que l’espace revient quand attendu (et apprendre quand ce n’est pas le cas).
- Planifier les extensions avant d’en avoir besoin ; les extensions d’urgence sont ce qui vous laisse avec une géométrie de vdev étrange pour toujours.
FAQ
1) Le slop space est-il configurable ?
Parfois, selon la plateforme et la version d’OpenZFS, il existe des tunables qui affectent le comportement. Opérationnellement, considérez le slop space comme une fonction de sécurité, pas comme une ligne budgétaire. Si vous « le supprimez », vous échangez un coussin prévisible contre des modes de panne imprévisibles sous pression.
2) Pourquoi zpool list FREE ne correspond-il pas à zfs list AVAIL ?
Parce que l’espace libre du pool n’est pas la même chose que l’espace allocable pour un dataset. La différence peut inclure le slop space, des réservations tenues pour d’autres datasets, et parfois des contraintes comme les classes d’allocation du vdev spécial. Faites toujours confiance à AVAIL d’un dataset pour les questions « puis-je écrire ici ? ».
3) J’ai supprimé un gros répertoire. Pourquoi l’espace n’est-il pas revenu ?
Le plus souvent : les snapshots. Les blocs sont encore référencés par des snapshots, donc ils ne peuvent pas être libérés. Vérifiez usedbysnapshots et listez les snapshots triés par USED.
4) La fragmentation compte-t-elle réellement dans ZFS ?
Oui, surtout en haute utilisation et sur RAIDZ. La fragmentation réduit les options de l’allocateur et augmente le travail par allocation. Elle peut convertir « espace libre » en « espace qui existe mais n’est pas utile », en particulier pour des écritures larges ou alignées en stripe.
5) La règle « garder les pools sous 80% » est-elle réelle ?
C’est une règle empirique, pas une loi physique. Certaines charges peuvent tourner au-dessus de 80% pendant longtemps (archives froides avec peu de réécritures). D’autres deviennent misérables à 70% (VM random writes avec snapshots lourds). Le bon chiffre dépend de la charge, du type de vdev, et de la tolérance opérationnelle au risque.
6) Un vdev spécial plein peut-il casser tout le pool ?
Oui. Si les métadonnées ou petits blocs sont dirigés vers special et que special est plein, les opérations nécessitant ces allocations peuvent échouer même si les vdevs principaux ont de la place. C’est pourquoi les vdevs spéciaux doivent être dimensionnés de manière conservatrice et surveillés agressivement.
7) Les réservations aident-elles à résoudre les problèmes de slop space ?
Les réservations garantissent qu’un dataset puisse écrire quand le pool est serré, mais elles ne créent pas d’espace. Elles déplacent simplement qui échouera en premier. Dans les pools multi-tenant, c’est souvent le bon choix — à condition de comprendre le compromis.
8) Pourquoi les écritures ralentissent-elles dramatiquement près du plein ?
Parce que l’allocation devient plus difficile, les mises à jour de métadonnées augmentent, et l’allocateur doit chercher des extents utilisables. Sur RAIDZ, assembler de bonnes stripes devient plus contraint. CoW signifie que chaque écriture est « allouer nouveau puis committer », ce qui coûte plus cher quand le pool est encombré.
9) Si j’ajoute des disques, le problème de « slop » va-t-il disparaître ?
Ajouter de la capacité aide parce que cela restaure de la marge et donne plus d’options à l’allocateur. Mais si la cause racine est le sprawl de snapshots, une mauvaise configuration de quota, ou l’épuisement d’un vdev spécial, ajouter des disques peut ne faire que retarder le prochain incident.
10) Quelle est la meilleure habitude pour éviter les surprises « semble plein » ?
Surveillez les snapshots et la marge comme des métriques de première classe. « Used » est un indicateur retardé ; « qu’est-ce qui épingle l’espace » et « à quelle vitesse nous approchons de la falaise » sont des indicateurs avancés.
Conclusion
Les pools ZFS semblent pleins avant 100% parce que ZFS n’est pas un simple seau d’octets. C’est un système transactionnel CoW qui a besoin de marge pour manœuvrer : de la place pour les nouveaux blocs, de la place pour les métadonnées, de la marge pour éviter les pièges de fragmentation, et de la marge pour tenir ses promesses (réservations) sans s’effondrer sous charge. Le slop space est la version explicite de cette réalité ; les snapshots, vdevs spéciaux et le comportement RAIDZ en sont les versions implicites.
Si vous retenez une leçon opérationnelle : la capacité n’est pas un nombre unique. Les runbooks qui ne vérifient que « pourcentage utilisé » sont la raison pour laquelle vous vous retrouvez à déboguer un incident de stockage avec l’outillage émotionnel d’une météo. Vérifiez l’espace allocable au niveau dataset, attribuez ce qui épingle les blocs, surveillez la fragmentation et les vdevs spéciaux, et gardez suffisamment de marge pour que ZFS fasse ce pour quoi vous l’avez engagé : préserver vos données pendant que tout le reste brûle.