Il est 02:17. Votre déploiement est en cours. Une écriture échoue avec ENOSPC. Quelqu’un lance df -h et annonce que le système de fichiers n’est rempli qu’à 72%. Félicitations : vous venez de rencontrer l’un des tours favoris de ZFS — techniquement correct et opérationnellement exaspérant.
Ce n’est pas que ZFS soit « buggy ». C’est ZFS qui est honnête sur des contraintes différentes de celles que vos réflexes vérifient en premier. L’espace dans ZFS n’est pas un seul nombre. C’est un empilement de règles comptables, de réalités de métadonnées et de marges de sécurité qui n’apparaissent que quand vous êtes déjà en sang.
Ce que signifie vraiment « no space left » dans ZFS
Quand une application reçoit ENOSPC sur ZFS, il ne s’agit pas toujours d’octets libres bruts. ZFS est un système de fichiers copy-on-write. Chaque « réécriture » est un nouvel écrit ailleurs, suivi de mises à jour de métadonnées, puis les anciens blocs deviennent libres seulement après que le groupe de transaction (TXG) s’engage — et uniquement si rien d’autre ne référence ces blocs.
Donc « pas d’espace » peut signifier l’un de ces cas :
- Le pool est effectivement presque plein et ZFS se protège d’un effondrement (slop space, contraintes de l’allocateur).
- Des snapshots épinglent des blocs que vous pensiez avoir supprimés.
- Des réservations consomment de l’espace que
dfdécrit mal. - Des quotas bloquent un dataset même si le pool a de la place.
- Des métadonnées ou le surcoût des petits blocs mangent l’espace allouable restant.
- Fragmentation et saturation des metaslabs rendent « l’espace libre » inutilisable pour le schéma d’écriture demandé.
- Les zvols en thin-provisioning paraissent spacieux jusqu’à ce qu’ils ne le soient plus ; ou ils semblent pleins à cause de volsize/volblocksize.
Opérationnellement, vous devriez traiter « ZFS no space left » comme : « l’allocateur ne peut pas satisfaire la requête compte tenu des politiques et des références en cours. » C’est différent de « disque plein », et cette différence compte car la solution est rarement « rm -rf jusqu’à que ça marche ».
Une citation à garder en tête quand vous êtes tenté de « simplement supprimer des trucs » en production : « L’espoir n’est pas une stratégie. »
— Rick Page
Playbook de diagnostic rapide (vérifiez ceci avant de supprimer quoi que ce soit)
Si vous ne faites rien d’autre, faites ceci dans l’ordre. C’est optimisé pour la vitesse, le signal, et pour ne pas empirer l’incident.
1) Est-ce au niveau du pool, du dataset, ou d’un zvol ?
- Pool :
zpool listetzpool status - Dataset :
zfs list,zfs get quota,reservation,refreservation - Zvol :
zfs list -t volume,zfs get volsize,volblocksize,refreservation
Décision : si c’est un quota/réservation de dataset, les correctifs sont locaux et rapides. Si c’est un pool proche de la saturation, il faut gérer la capacité, pas toucher des réglages au hasard.
2) Les snapshots épinglent-ils de l’espace ?
zfs list -t snapshot -o name,used,refer,creation -s usedzfs get usedbydataset,usedbysnapshots,usedbyrefreservation
Décision : si les snapshots dominent, supprimer des fichiers ne servira à rien ; supprimez des snapshots (avec précaution).
3) Des réservations ou refreservations volent-elles l’espace allouable ?
zfs get -r reservation,refreservation- Vérifiez des
refreservationexceptionnellement grandes sur les zvols et datasets.
Décision : retirez ou réduisez les réservations seulement si vous comprenez pourquoi elles existent. « Parce que quelqu’un a lu un blog de tuning » n’est pas une raison valide.
4) Le pool atteint-il le slop space / douleur de l’allocateur ?
zpool listpourcentage de capacité élevé.zpool get autotrimetzpool statuspour erreurs de périphérique et topologies dégradées.
Décision : si le pool est > ~80–90% et sous pression d’écritures, prévoyez un soulagement immédiat (libérer de l’espace réel, ajouter des vdevs, déplacer des charges). Ne « défragmentez pas ZFS » ; ce n’est pas comme ça que ça marche.
5) Vérifiez les consommateurs « cachés » : refreservation, dispositifs spéciaux, petits blocs et écritures sync
zfs get -r special_small_blockset confirmez que vous ne déposez pas les métadonnées sur un special vdev sous-dimensionné.zfs get recordsize,compressionpour les datasets avec écritures petites pathologiques.
Décision : si metadata/special est plein, vous pouvez voir « pas d’espace » même quand le pool principal a l’air correct. Cela nécessite une correction ciblée.
Faits intéressants et contexte historique
- ZFS est né chez Sun Microsystems au milieu des années 2000, conçu pour mettre fin à la séparation « système de fichiers + gestionnaire de volumes » en faisant du stockage une pile cohérente unique.
- Le copy-on-write était un choix de fiabilité délibéré : ZFS évite les réécritures en place pour toujours maintenir la cohérence sur disque, même après des crashs.
- Le concept de « pool » a renversé l’ancien modèle : au lieu que les systèmes de fichiers possèdent des partitions, les systèmes de fichiers sont des vues bon marché sur un pool de stockage partagé, ce qui rend la comptabilité plus nuancée.
- Les snapshots ne sont pas des copies ; ce sont des signets. Ils coûtent presque rien à créer, mais peuvent retenir d’énormes quantités de données « supprimées » indéfiniment.
- Le « slop space » de ZFS existe parce que les pools presque pleins se comportent mal : la fragmentation augmente, l’allocation devient coûteuse, et dans le pire des cas vous pouvez vous verrouiller hors de l’espace nécessaire pour en libérer.
- Les métadonnées sont de première classe et abondantes dans ZFS : checksums, pointeurs de blocs, spacemaps et journaux d’intention signifient que le système sait beaucoup — et stocke beaucoup.
- Ashift est devenu célèbre à cause des disques 4K : choisir un mauvais alignement de secteur taxe définitivement votre capacité utilisable et peut amplifier la douleur « pas d’espace » par de l’espace perdu.
- OpenZFS s’est scindé et a évolué sur plusieurs plateformes (Illumos, FreeBSD, Linux), et certains comportements de rapports d’espace et de fonctionnalités diffèrent subtilement selon la version.
- Les zvols ont rendu ZFS populaire pour la virtualisation, mais ils ont introduit des sémantiques de périphérique bloc (volsize, volblocksize, discard/TRIM) dans un monde de système de fichiers avec snapshots et COW.
Comptabilité d’espace qui fait paraître ZFS comme menteur
df n’est pas votre autorité ici
df demande au système de fichiers monté ce qu’il pense être disponible pour ce montage. Sur ZFS, le montage est un dataset avec des propriétés : quotas, réservations, refreservations, et une vue sur un pool qui peut avoir ses propres contraintes. Quand ZFS dit « pas d’espace », il peut s’agir de :
- Manque d’espace du point de vue de l’allocateur du pool.
- Bloqué par un quota de dataset même si le pool a de la place.
- Impossible d’allouer une région suffisamment contiguë dans un metaslab majoritairement plein (surtout pour certaines tailles de bloc et patterns).
Si vous déboguez l’espace ZFS, zfs et zpool sont la source de vérité. df est la météo collée à votre fenêtre.
Snapshots : la raison n°1 pour laquelle les suppressions ne « libèrent » pas d’espace
Dans ZFS, un bloc n’est libéré que lorsque personne ne le référence. Un snapshot est une référence. Donc si vous supprimez un fichier qui existait quand un snapshot a été pris, les blocs sont toujours référencés par ce snapshot. Résultat : votre suppression « fonctionne », votre application est contente, et votre pool reste plein.
Les calculs d’espace deviennent particulièrement confus parce que ZFS rapporte le USED d’un snapshot comme l’espace que ce snapshot tient de façon unique par rapport à l’état courant du dataset. Ce nombre peut être contre-intuitif. Les snapshots peuvent collectivement épingler beaucoup d’espace même si chacun semble petit, selon le churn.
Reservations et refreservations : de l’espace que vous ne pouvez pas utiliser bien qu’il soit « libre »
reservation garantit de l’espace pour un dataset. Cela signifie que ZFS considère cet espace comme indisponible pour les autres.
refreservation garantit de l’espace pour les données référencées du dataset (excluant les snapshots). Il est souvent utilisé pour les zvols afin de s’assurer qu’ils peuvent toujours être écrits, car manquer d’espace sous un périphérique bloc peut être catastrophique pour le système de fichiers invité.
Les deux sont des outils légitimes. Ce sont aussi de bons moyens d’affamer votre pool si vous les configurez comme si vous provisionniez un SAN d’entreprise en 2009.
Slop space : la politique « gardez un peu d’argent liquide » de ZFS
ZFS réserve un morceau d’espace du pool pour que le système puisse continuer à fonctionner près de la saturation : allouer des métadonnées, engager des TXG et récupérer. On appelle parfois cela « slop space ». Le comportement exact dépend de la version et des réglages, mais le principe est stable : ZFS commencera à refuser des allocations avant que vous n’atteigniez 100% d’utilisation brute.
Ce n’est pas ZFS qui dramatisе. Un système de fichiers COW à 99% plein est un cauchemar de performance et de fiabilité. Si vous exploitez des pools régulièrement au-delà de ~80–85% puis que vous vous étonnez que tout devienne bizarre, ce n’est pas un bug ZFS. C’est un choix de gestion.
Métadonnées et petits blocs : vous pouvez remplir le pool sans « gros fichiers »
ZFS stocke checksums, pointeurs de blocs, blocs indirects, spacemaps, structures de répertoire, attributs étendus, et plus. Si vous créez des millions de petits fichiers, ou si vous stockez des blocs très fragmentés, le surcoût des métadonnées augmente. De plus, les petits blocs ont proportionnellement plus d’overhead de pointeur et peuvent gaspiller de l’espace à cause des tailles minimales d’allocation.
Si vous utilisez un special vdev (pour les métadonnées et éventuellement les petits blocs), vous pouvez atteindre « pas d’espace » lorsque le special vdev se remplit — même si les vdevs de données principaux ont beaucoup de place. C’est une saveur particulière d’excitation.
Fragmentation et metaslabs : espace « libre » mais inallouable
ZFS alloue à partir de metaslabs. À mesure que le pool se remplit et churn, l’espace libre peut se disperser en morceaux trop petits pour les tailles de bloc demandées ou trop coûteux à allouer efficacement. L’allocateur peut échouer une requête même quand l’espace total libre semble non nul.
Cela apparaît le plus douloureusement avec :
- Grands écritures séquentielles en fin de vie du pool.
- Images VM avec écritures aléatoires, snapshots et clones.
- Recordsize trop petit avec churn élevé.
Zvols : périphériques bloc avec conséquences ZFS
Les zvols ressemblent à des disques. Les gens les traitent comme des disques. Puis ils apprennent que les zvols sont soutenus par des datasets ZFS, ce qui signifie que snapshots, refreservations, compression et comportement COW peuvent tous influencer le « no space left ».
Le thin provisioning rend la situation épicée. Vous pouvez avoir un zvol avec un grand volsize, beaucoup d’espace apparent côté invité, et pourtant coincer le pool hôte jusqu’à ce que les écritures échouent. L’invité voit un disque qui « devrait fonctionner ». L’hôte voit un pool qui ne peut pas allouer. Les deux ont raison. Tout le monde est en colère.
Blague #1 : Le moyen le plus rapide d’augmenter l’espace libre dans un pool ZFS est de prendre un snapshot de votre carrière avant de toucher à la production. Vous voudrez quelque chose pour revenir en arrière.
Tâches pratiques (commandes, sorties, décisions)
Voici les tâches que je lance réellement quand ZFS rapporte ENOSPC ou que le pool semble « plein » de façon incompatible avec df. Chaque tâche inclut un exemple de sortie réaliste et quelle décision en tirer.
Task 1: Confirm pool capacity and basic health
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 21.8T 19.9T 1.93T - - 62% 91% 1.00x ONLINE -
Ce que cela signifie : 91% de capacité et 62% de fragmentation sont une alerte rouge. Même avec 1.93T libre, l’allocation peut échouer sous pression, et les performances seront dégradées.
Décision : traitez ceci comme un incident au niveau pool. Votre correctif principal est de libérer un espace significatif ou d’ajouter de la capacité vdev, pas « supprimer quelques logs ».
Task 2: Check for a dataset quota causing local ENOSPC
cr0x@server:~$ zfs get -o name,property,value,source quota,refquota tank/app
NAME PROPERTY VALUE SOURCE
tank/app quota 2T local
tank/app refquota none default
Ce que cela signifie : le dataset tank/app est limité à 2T quelle que soit la place dans le pool.
Décision : si l’appli a besoin de plus, augmentez le quota ; sinon l’application doit nettoyer dans son budget.
Task 3: Check reservations and refreservations recursively
cr0x@server:~$ zfs get -r -o name,property,value,source reservation,refreservation tank | head
NAME PROPERTY VALUE SOURCE
tank reservation none default
tank refreservation none default
tank/vm reservation none default
tank/vm refreservation none default
tank/vm/win01 reservation none default
tank/vm/win01 refreservation 500G local
tank/vm/win02 reservation none default
tank/vm/win02 refreservation 500G local
Ce que cela signifie : deux VM ont 500G chacune réservés. C’est 1T d’espace pool effectivement retiré pour les autres.
Décision : ne gardez des refreservations que là où vous avez besoin de garanties fortes. Si le pool est sous tension, réduisez-les prudemment et documentez le pourquoi.
Task 4: See what is consuming space by category
cr0x@server:~$ zfs get -o name,property,value -s local,default used,available,usedbysnapshots,usedbydataset,usedbyrefreservation tank/app
NAME PROPERTY VALUE
tank/app used 1.89T
tank/app available 110G
tank/app usedbydataset 940G
tank/app usedbysnapshots 850G
tank/app usedbyrefreservation 0B
Ce que cela signifie : les snapshots retiennent presque autant que le dataset actif.
Décision : supprimer des fichiers dans tank/app vous décevra. Traitez d’abord les snapshots : rétention, réplication, ou suppression ciblée.
Task 5: Identify the biggest snapshots (by space “used”)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s used | tail -5
tank/app@auto-2025-12-20_0100 120G 820G Sat Dec 20 01:00 2025
tank/app@auto-2025-12-21_0100 128G 812G Sun Dec 21 01:00 2025
tank/app@auto-2025-12-22_0100 140G 800G Mon Dec 22 01:00 2025
tank/app@auto-2025-12-23_0100 155G 785G Tue Dec 23 01:00 2025
tank/app@auto-2025-12-24_0100 210G 740G Wed Dec 24 01:00 2025
Ce que cela signifie : les snapshots récents grossissent vite ; le churn est élevé.
Décision : examinez ce qui a changé (nouvelle charge, job de compaction, échec de rotation de logs) et ajustez la fréquence/la rétention des snapshots ou la disposition du dataset.
Task 6: Dry-run a safe snapshot deletion plan (human review first)
cr0x@server:~$ zfs list -t snapshot -o name -s creation | head -5
tank/app@auto-2025-11-01_0100
tank/app@auto-2025-11-02_0100
tank/app@auto-2025-11-03_0100
tank/app@auto-2025-11-04_0100
tank/app@auto-2025-11-05_0100
Ce que cela signifie : vous avez une liste ordonnée pour proposer des suppressions (par ex., les plus anciens d’abord) alignée avec la politique.
Décision : si vous devez supprimer des snapshots, commencez par les plus anciens sauf si vous avez un « mauvais snapshot » connu qui bloque un état spécifique dont vous n’avez plus besoin.
Task 7: Actually delete snapshots (carefully, and preferably in batches)
cr0x@server:~$ sudo zfs destroy tank/app@auto-2025-11-01_0100
cr0x@server:~$ sudo zfs destroy tank/app@auto-2025-11-02_0100
Ce que cela signifie : les références du snapshot sont retirées ; l’espace sera libéré une fois qu’aucune autre référence n’existera et que les TXG s’engageront.
Décision : après quelques suppressions, revérifiez zpool list et le available du dataset. Si rien n’améliore, une autre contrainte est en jeu (clones, holds, réservations, special vdev).
Task 8: Check for snapshot holds preventing deletion
cr0x@server:~$ zfs holds tank/app@auto-2025-11-03_0100
NAME TAG TIMESTAMP
tank/app@auto-2025-11-03_0100 keep Thu Dec 12 09:14 2025
Ce que cela signifie : un hold nommé keep épingle le snapshot.
Décision : coordonnez-vous avec la personne/outil ayant posé le hold (backup/replication). Retirez le hold seulement quand vous êtes sûr qu’il n’est plus requis.
Task 9: Release a hold (explicitly, with intent)
cr0x@server:~$ sudo zfs release keep tank/app@auto-2025-11-03_0100
cr0x@server:~$ sudo zfs destroy tank/app@auto-2025-11-03_0100
Ce que cela signifie : le snapshot peut maintenant être détruit.
Décision : si des holds apparaissent de façon inattendue, auditez votre pipeline de sauvegarde/réplication. Les holds sont utiles ; les holds surprises sont mauvais.
Task 10: Check for clones that keep snapshots alive
cr0x@server:~$ zfs get -o name,property,value clones tank/app@auto-2025-12-01_0100
NAME PROPERTY VALUE
tank/app@auto-2025-12-01_0100 clones tank/app-test
Ce que cela signifie : le dataset tank/app-test est un clone dépendant de ce snapshot. Vous ne pouvez pas détruire le snapshot sans gérer le clone.
Décision : soit détruisez le clone, soit promouvez-le, soit acceptez que le snapshot doit rester. Ne « forcez » pas la suppression sauf si vous aimez expliquer une perte de données.
Task 11: Identify whether a zvol is over-reserved or mis-sized
cr0x@server:~$ zfs list -t volume -o name,volsize,used,available tank/vm/win01
NAME VOLSIZE USED AVAIL
tank/vm/win01 800G 610G 0B
Ce que cela signifie : AVAIL est 0B pour la vue dataset de ce volume. Cela peut être un quota/refreservation ou une contrainte au niveau pool. Cela peut aussi signifier que votre zvol a atteint sa limite relative aux conditions du pool.
Décision : vérifiez refreservation et la capacité du pool. Si le pool est presque plein, augmenter volsize peut être risqué ou impossible.
Task 12: Inspect zvol properties that affect space behavior
cr0x@server:~$ zfs get -o name,property,value -s local,default volsize,volblocksize,compression,refreservation tank/vm/win01
NAME PROPERTY VALUE
tank/vm/win01 volsize 800G
tank/vm/win01 volblocksize 8K
tank/vm/win01 compression lz4
tank/vm/win01 refreservation 800G
Ce que cela signifie : une refreservation de 800G garantit le volsize complet. Bien pour la sécurité, brutal pour les pools partagés.
Décision : si vous avez besoin de garanties, conservez-la. Si vous gérez un hôte de virtualisation dense et manquez d’espace, envisagez de réduire la refreservation en acceptant le risque opérationnel (avec monitoring et marge).
Task 13: Check if the pool has a checkpoint consuming “phantom” space
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 21.8T 19.9T 1.93T 1.2T - 62% 91% 1.00x ONLINE -
Ce que cela signifie : un checkpoint existe et retient 1.2T d’ancien état du pool. C’est de l’espace réel que vous ne pouvez pas récupérer tant que le checkpoint n’est pas supprimé.
Décision : si vous n’avez pas besoin du checkpoint pour revenir en arrière, supprimez-le. Si vous en avez besoin, vous avez choisi de fonctionner avec moins de capacité — assumez ce choix.
Task 14: Discard a checkpoint (irreversible, so think first)
cr0x@server:~$ sudo zpool checkpoint -d tank
cr0x@server:~$ zpool list tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 21.8T 19.9T 1.93T - - 62% 91% 1.00x ONLINE -
Ce que cela signifie : le checkpoint est supprimé ; cet espace épinglé peut maintenant être récupéré au fur et à mesure que les blocs se libèrent.
Décision : ne faites cela que si vous êtes sûr de ne pas avoir besoin du filet de sécurité de rollback.
Task 15: Verify special vdev health and capacity (if you have one)
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
errors: No known data errors
Ce que cela signifie : un special vdev existe. S’il se remplit, le pool peut devenir effectivement « hors d’espace » pour les métadonnées/petits blocs.
Décision : surveillez l’utilisation du special vdev de près. S’il est proche de la saturation, vous devrez peut-être ajouter de la capacité spéciale ou ajuster la stratégie special_small_blocks.
Task 16: Check small-block policy that can overload special vdev
cr0x@server:~$ zfs get -r -o name,property,value special_small_blocks tank | head
NAME PROPERTY VALUE
tank special_small_blocks 16K
tank/app special_small_blocks 16K
tank/vm special_small_blocks 0
Ce que cela signifie : tous les blocs ≤16K pour tank et tank/app vont sur special. Cela peut être excellent pour les performances et catastrophique pour la capacité spéciale si la charge a beaucoup de petits blocs.
Décision : si le special est tendu, réduisez cela pour les datasets à churn élevé (les nouvelles écritures suivront la nouvelle règle ; les anciens blocs ne migreront pas magiquement).
Blague #2 : ZFS ne « dévore » pas votre espace libre. Il tient juste des reçus méticuleux, et vos suppressions ne sont pas déductibles d’impôt tant que les snapshots ne sont pas d’accord.
Trois mini-histoires d’entreprise issues des guerres d’espace
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
L’entreprise migravait un service legacy vers des containers. Le stockage était « simple » : un dataset ZFS monté dans les nœuds, avec des snapshots horaires pour la sécurité. Le playbook on-call était hérité des jours ext4 : si le disque est plein, supprimez d’anciens logs et redémarrez.
Pendant une journée de pic, des écritures ont commencé à échouer. L’application a lancé ENOSPC et est entrée en crash loop. L’on-call a vérifié df -h : 68% utilisé. Ils ont soupçonné un bug. Ils ont redémarré des pods. Ils ont forcé la rotation des logs. Rien n’a changé.
Puis quelqu’un a lancé zfs get usedbysnapshots et a découvert que les snapshots retenaient plus d’espace que le dataset vivant. L’équipe avait récemment activé un logging de requêtes verbeux pour le debugging et déployé un changement qui réécrivait lourdement un gros fichier JSON de façon répétée. Chaque heure, un nouveau snapshot épinglait une tranche supplémentaire de ce churn. Supprimer les logs courants n’affectait pas les blocs épinglés.
La correction fut banale : supprimer une tranche d’anciens snapshots et réduire la fréquence de snapshot pour ce dataset. Le postmortem fut plus instructif : « df n’est pas une autorité sur ZFS » est devenu une ligne du runbook, et l’équipe ajouta une tuile de dashboard pour snapshot-used et la capacité du pool.
Mini-histoire 2 : L’optimisation qui s’est retournée contre eux
Une équipe plateforme virtualization voulait des VMs plus rapides. Ils ont ajouté un special vdev sur NVMe en miroir pour accélérer les métadonnées et petits blocs. Ils ont aussi mis special_small_blocks=32K pool-wide car cela semblait bien en labo. Tout le monde a célébré ; les graphes sont devenus plus beaux.
Des mois plus tard, un nouveau système de build interne a atterri sur le même pool. Il a créé des océans de petits fichiers, les a churnés constamment et adorait réécrire de petits blobs. Le special vdev s’est rempli bien plus vite que les vdevs de données principaux. Personne ne l’a remarqué parce que le pool affichait encore des « téraoctets libres » et la seule alerte était une « capacité pool » générique.
Puis la partie amusante : des erreurs apparemment aléatoires « no space left » sur des services sans rapport. Les allocations de métadonnées échouaient parce que le special vdev était tendu. Certains datasets n’utilisaient même pas les petits blocs, mais avaient quand même besoin de métadonnées. L’équipe stockage voyait de l’espace libre sur les vdevs principaux et se faisait accuser de « ZFS qui ment encore ».
La correction finale a nécessité d’ajouter plus de capacité spéciale et de restreindre special_small_blocks aux datasets qui en bénéficiaient vraiment. La leçon fut douloureuse : les réglages « rapides » ont un rayon d’explosion. Ne les déployez pas pool-wide juste parce que le graphique de benchmark est beau.
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Un système financier stockait des rapports quotidiens et les servait aussi aux clients. L’équipe utilisait ZFS avec des quotas stricts par dataset et une politique que le pool ne devait jamais dépasser un plafond opérationnel. Ils traitaient 80–85% comme « plein » pour le pool, parce qu’ils avaient vu ce qui se passe après.
Un vendredi, un fournisseur upstream a changé un format de flux et le parseur a commencé à dupliquer des données. La consommation de stockage a grimpé. Le service restait sain, mais le pool approchait rapidement le plafond. Les alertes ont déclenché tôt parce qu’elles étaient liées à la capacité du pool et à la croissance des snapshots — pas à la visibilité client.
La réponse de l’équipe fut sans glamour : ils ont stoppé l’ingestion, ont maintenu la partie service en fonctionnement, et ont utilisé des snapshots pour conserver des preuves pour le debug. Comme le pool avait encore de la marge, ils ont pu le faire sans déclencher la panique de l’allocateur ni des suppressions d’urgence. Ils ont réparé le parseur, retéléchargé les données correctes, et ont repris.
Le postmortem fut court et un peu satisfait. La « pratique ennuyeuse » était simplement de maintenir une marge, d’appliquer des quotas et de surveiller l’espace utilisé par les snapshots. Personne n’a eu à supprimer quoi que ce soit au hasard. Personne n’a eu à expliquer pourquoi des sauvegardes avaient disparu. Parfois la meilleure ingénierie est juste de refuser d’opérer en bord de falaise.
Erreurs courantes : symptômes → cause racine → correction
1) « df montre plein d’espace, mais les écritures échouent »
Symptôme : l’application reçoit ENOSPC ; df -h montre de l’espace confortable.
Cause racine : quota de dataset, reservation/refreservation, ou slop space / contraintes de l’allocateur du pool.
Correction : vérifiez zfs get quota,refquota,reservation,refreservation et zpool list. Ajustez la propriété pertinente ou libérez de l’espace pool.
2) « J’ai supprimé beaucoup, mais rien ne s’est libéré »
Symptôme : vous supprimez des fichiers ; l’allocation du pool ne diminue pas.
Cause racine : snapshots (ou clones) épinglent les blocs.
Correction : quantifiez usedbysnapshots. Identifiez les snapshots lourds, les holds et les clones ; supprimez des snapshots conformément à la rétention, ou retirez/promouvez des clones.
3) « Le pool est à 90% et soudain tout est lent et échoue »
Symptôme : pics de latence, allocations qui échouent de façon intermittente, scrubs lents, IO aléatoire misérable.
Cause racine : comportement de l’allocateur en quasi-saturation + fragmentation. Le COW a besoin de marge.
Correction : libérez un espace significatif (pas quelques gigaoctets, mais des points de pourcentage significatifs), ajoutez de la capacité vdev, ou migrez des workloads. Ensuite appliquez un plafond.
4) « Nous avons ajouté un special vdev et maintenant ‘no space’ arrive tôt »
Symptôme : le pool a beaucoup d’espace brut ; les workloads riches en métadonnées échouent ; les erreurs ressemblent à une exhaustion d’espace.
Cause racine : le special vdev est plein ou presque plein à cause de la politique de petits blocs/métadonnées.
Correction : surveillez et étendez le special vdev ; restreignez special_small_blocks aux datasets ciblés ; arrêtez d’envoyer des petits blocs à fort churn sur le special à moins de l’avoir dimensionné pour ça.
5) « Le système de fichiers invité du zvol dit qu’il a de l’espace, l’hôte dit ENOSPC »
Symptôme : les écritures VM échouent ; l’invité a de l’espace libre ; le pool hôte est quasi-plein ou thin-provisioned.
Cause racine : thin provisioning + COW + snapshots + manque de marge ; ou un schéma de refreservation du zvol qui affame le pool.
Correction : ajoutez de la marge, réduisez le churn de snapshots, assurez-vous que discard/TRIM est configuré bout en bout si approprié, et décidez si la refreservation est nécessaire.
6) « Nous avons activé la dédup pour économiser de l’espace ; maintenant on n’a plus d’espace »
Symptôme : les calculs de capacité empirent ; la pression mémoire augmente ; l’espace se comporte de façon imprévisible.
Cause racine : la dédup ajoute des métadonnées et un coût opérationnel ; sur beaucoup de charges c’est un piège sauf si c’est conçu pour.
Correction : n’activez pas la dédup à la légère. Si vous l’avez déjà fait, mesurez et envisagez de migrer les données vers un dataset/pool sans dédup plutôt que d’essayer de « la désactiver » comme solution miracle.
Listes de vérification / plan pas à pas
Checklist A: « Arrêter l’hémorragie » pendant un incident ENOSPC
- Confirmez l’étendue : pool vs dataset vs zvol. Lancez
zpool listetzfs list. - Gelez le churn : mettez en pause le job générant les écritures (ingest, compaction, backups, artefacts CI). Les incidents d’espace s’aggravent avec le churn.
- Trouvez la contrainte : quotas/réservations/snapshots/special vdev. Ne devinez pas.
- Récupérez de l’espace en sécurité : supprimez des snapshots selon la politique (les plus anciens d’abord), ou réduisez les réservations, ou migrez un dataset vers un autre pool.
- Vérifiez la récupération : revérifiez le
CAPdu pool, leavailabledu dataset, et le succès des écritures applicatives. - Documentez ce que vous avez changé : suppressions de snapshots, changements de propriétés, tout. Le vous du futur est un stakeholder.
Checklist B: « Ne plus laisser ça se reproduire »
- Fixez un plafond opérationnel pour l’utilisation du pool (généralement 80–85% selon la charge) et alertez avant d’y arriver.
- Surveillez la croissance des snapshots par dataset, pas seulement l’utilisation du pool.
- Utilisez des quotas intentionnellement pour les voisins bruyants et les jobs hors de contrôle.
- Utilisez les réservations avec parcimonie et seulement quand les garanties valent le coût pour le pool partagé.
- Pour la virtualisation : décidez d’une politique sur le thin provisioning ; si vous l’autorisez, imposez de la marge et une surveillance agressive.
- Reconsidérez la stratégie special vdev comme un plan de capacité, pas un réglage rapide.
- Effectuez des scrubs réguliers et traitez toute erreur de périphérique comme urgente. Les incidents d’espace et de fiabilité arrivent souvent en paquet.
FAQ
1) Pourquoi ZFS dit « no space left » avant que le pool n’atteigne 100% ?
Parce que ZFS garde un espace de réserve (slop space) et a besoin de marge allouable pour les métadonnées et l’engagement des TXG. Près de 100%, un système COW peut se piéger.
2) Pourquoi la suppression de fichiers n’a-t-elle pas libéré d’espace ?
Les snapshots (ou clones) référencent probablement encore ces blocs. Vérifiez zfs get usedbysnapshots et listez les snapshots par used.
3) Est-ce sûr de supprimer des snapshots pour libérer de l’espace ?
Généralement oui — si vous comprenez pourquoi ils existent (sauvegarde, réplication, points de rollback). La partie risquée est de supprimer les mauvais sans coordonner les exigences de rétention.
4) Quelle est la différence entre reservation et refreservation ?
reservation réserve de l’espace pour le dataset incluant les snapshots. refreservation réserve de l’espace pour les données référencées du dataset seulement, souvent utilisé pour la sécurité des zvols.
5) La fragmentation peut-elle à elle seule causer ENOSPC ?
Elle peut contribuer. Quand les metaslabs sont encombrés et fragmentés, l’allocation pour certaines tailles de bloc peut échouer même s’il reste de l’espace libre, surtout sous un churn élevé.
6) La compression aide-t-elle contre le « no space left » ?
La compression peut réduire l’espace alloué et retarder la douleur, mais ce n’est pas un sauvetage quand vous êtes déjà proche de la saturation et que le churn des snapshots épingle des blocs anciens.
7) Pourquoi le USED d’un snapshot semble petit alors que les snapshots retiennent beaucoup d’espace ?
Le USED d’un snapshot est « unique à ce snapshot » par rapport à l’état courant du dataset. Beaucoup de snapshots peuvent chacun sembler modestes tout en retenant collectivement un gros churn historique.
8) Dois-je juste ajouter un disque plus grand au pool ?
Ajoutez de la capacité correctement : les pools ZFS grandissent en ajoutant des vdevs, pas en remplaçant un seul disque (sauf si vous remplacez chaque disque d’un vdev et expand). Les correctifs de capacité sont excellents lorsqu’ils sont planifiés, pas en panique.
9) Un special vdev plein peut-il provoquer des échecs d’écriture sur tout le pool ?
Oui. Si les métadonnées (ou petits blocs dirigés vers le special) ne peuvent pas être allouées, les écritures normales peuvent échouer. La capacité spéciale n’est pas optionnelle si vous en dépendez.
10) Combien d’espace libre dois-je garder dans un pool ZFS ?
Assez pour ne pas jouer à la roulette de l’allocateur. Pour de nombreuses charges production, considérez 80–85% comme « plein ». Pour les pools VM avec écritures aléatoires, soyez encore plus conservateur.
Conclusion : prochaines étapes à réaliser aujourd’hui
Si vous êtes en plein incident : stoppez le churn, identifiez si les snapshots ou les réservations sont les véritables consommateurs d’espace, et libérez de l’espace d’une manière que vous pourrez expliquer dans un postmortem. La suppression aléatoire transforme un incident d’espace en un incident de perte de données.
Si vous n’êtes pas en incident (luxe rare) : fixez un plafond de capacité pour le pool, surveillez usedbysnapshots et les réservations, et répétez les étapes du « playbook de diagnostic rapide » pour ne pas les apprendre à 2h du matin. Le but n’est pas de faire arrêter ZFS de « mentir ». Le but est de parler la langue de ZFS avant qu’il ne se mette à crier.