ZFS est un système de fichiers qui dit la vérité — sauf quand il ne le fait pas. Pas parce qu’il est bogué, mais parce que ZFS fait de la comptabilité au travers d’un arbre généalogique : datasets, snapshots, clones, holds, reservations, vdevs spéciaux et métadonnées que vous ne pensiez pas facturables. Si vous avez déjà fixé USED qui ne correspondait pas à la réalité, vous avez rencontré les « mensonges d’espace utilisé ». Ils ne sont pas malveillants. Ils sont juste ZFS qui est techniquement correct d’une manière qui toutefois vous fera recevoir un page à 02:00.
refquota est le quota qui attache la facture au dataset qui a créé l’utilisation, au lieu de la socialiser entre descendants et historique de snapshots partagés. Si vous utilisez ZFS en multi-tenant (stockage de VM, répertoires utilisateurs, caches CI, conteneurs, cibles de sauvegarde), refquota est le seul quota qui empêche de façon cohérente les disputes « ce n’est pas moi qui ai utilisé cet espace » de devenir un incident de stockage.
Le problème : les « mensonges d’espace utilisé » dans ZFS
Nommons précisément l’ennemi. Quand les gens se plaignent que ZFS ment sur l’espace utilisé, ils veulent généralement dire une des choses suivantes :
- Un dataset affiche une faible utilisation, mais le pool est plein.
- Un locataire affirme n’avoir utilisé que 200G, mais le pool a perdu 2T.
- Supprimer des fichiers ne libère pas d’espace (parce que les snapshots conservent les blocs vivants).
- Les clones semblent ne rien utiliser jusqu’à divergence, puis la facture arrive quelque part d’où on ne l’attendait pas.
- Un quota sur un dataset parent semble correct, et pourtant des enfants peuvent encore provoquer l’épuisement du pool via l’historique de snapshots partagé.
Voici l’essentiel : ZFS fait de la comptabilité au niveau des blocs. Les blocs peuvent être référencés par plusieurs entités en même temps : un système de fichiers actif, un snapshot, un clone, un snapshot d’un clone, etc. « Used » n’est pas seulement « fichiers actuellement visibles ». C’est « blocs actuellement référencés par quelque chose ». Et ce « quelque chose » peut être historique.
Les quotas classiques (quota) concernent l’espace total utilisé par un dataset et ses descendants. Ils sont parfaits pour « ce sous-arbre ne doit pas dépasser X ». Mais ils ne sont pas conçus pour facturer équitablement seulement les changements propres à ce dataset lorsque snapshots/clones/descendants partagent des blocs. C’est ce à quoi sert refquota : il plafonne l’espace référencé par ce dataset seul.
Phrase résumée à apporter en réunion : quota est un budget familial ; refquota est une limite de crédit personnelle.
Première plaisanterie (les ingénieurs stockage méritent au moins une) : la comptabilité ZFS ressemble à une note de frais d’entreprise — tout est détaillé, rien n’est simple, et d’une manière ou d’une autre les « coûts partagés » finissent toujours sur la mauvaise carte.
Pourquoi les équipes d’exploitation se brûlent
Les échecs opérationnels liés à l’espace ZFS ne viennent que rarement d’une méconnaissance des commandes. Ils viennent d’une hypothèse sur un modèle qui n’est pas vrai :
- Supposer que supprimer un fichier libère immédiatement de l’espace.
- Supposer que le
USEDd’un dataset est ce qu’il « coûte » au pool maintenant. - Supposer que les quotas se comportent comme les quotas projet d’ext4 ou d’XFS.
- Supposer que les snapshots sont « gratuits jusqu’à ce qu’ils grossissent ». Ils sont gratuits jusqu’à ce que vous modifiiez des blocs.
refquota ne rend pas magiquement gratuits les snapshots. Ce qu’il fait, c’est empêcher un dataset d’élargir son empreinte référencée au-delà d’un plafond — même quand l’utilisation est difficile à raisonner à cause d’un historique partagé. Il force une réponse à une question simple : « Quelle taille ce dataset est-il autorisé à atteindre, quoi qu’il arrive ? »
Faits intéressants et contexte historique
Quelques points de contexte importants quand vous concevez une politique et prédisez le comportement :
- L’unité de base de la comptabilité de ZFS est le pointeur de bloc, pas le nom de fichier. C’est pourquoi les snapshots peuvent conserver de l’espace sans « avoir des fichiers » que vous pouvez voir.
- Les snapshots ne sont pas des copies. Ce sont des références immuables à des blocs existants, et ils ne « coûtent » de l’espace que lorsque le dataset actif diverge.
- Les clones sont des snapshots réinscriptibles. Ils partagent des blocs avec leur origine, et votre facture d’espace dépend de la mesure de leur divergence par rapport à l’histoire partagée.
- « USED » est une métrique en couches. Elle inclut l’espace référencé par le dataset plus l’espace référencé pour le compte des enfants et des snapshots selon la propriété que vous lisez (
used,refer,usedby*). - ZFS distingue espace « logique » et « physique ». La compression et les copies signifient que la taille logique du dataset peut différer fortement de l’allocation du pool.
- Les quotas ZFS ont évolué pour s’adapter au modèle dataset, pas aux quotas POSIX utilisateur. Ils sont d’abord des limites par dataset ; les quotas utilisateur/groupe sont une couche additionnelle.
- « Référencé » est ce qui ressemble le plus à une empreinte facturable. Il approxime ce qui serait libéré si vous détruisiez le dataset (sans compter les blocs encore nécessaires ailleurs).
- Les réservations peuvent provoquer des ENOSPC « mystérieux ». Un pool avec beaucoup d’espace libre brut peut tout de même refuser des écritures si l’espace libre n’est pas disponible pour ce dataset à cause de réservations et de quotas.
- Historiquement, de nombreux incidents ZFS proviennent des politiques de rétention de snapshots, pas des débits d’écriture. Les gens dimensionnent pour le churn quotidien et oublient que « hebdomadaire pendant un an » se cumule.
Famille de quotas : quota, refquota, reservation, refreservation
ZFS vous donne quatre boutons qui se ressemblent jusqu’à ce qu’ils ruinent votre week-end. Définissons-les avec l’intention opérationnelle.
quota
quota limite la quantité d’espace qu’un dataset et tous ses descendants peuvent utiliser. Si vous mettez quota=1T sur tank/prod, alors tank/prod plus tank/prod/db plus tank/prod/vm ne peuvent collectivement pas dépasser 1T.
À utiliser quand vous voulez un plafond dur pour un sous-arbre.
refquota
refquota limite la quantité d’espace qu’un dataset lui-même peut référencer (son « empreinte personnelle »), en excluant les descendants. C’est celui qui se comporte comme « plafonner ce dataset, indépendamment de ce que font ses enfants ».
À utiliser pour des datasets de locataires, disques de VM, racines de conteneurs, répertoires personnels — tout ce dont vous avez besoin de limites par entité qui ne deviennent pas des discussions sur des snapshots partagés.
reservation
reservation garantit de l’espace à un dataset et à tous ses descendants. C’est la promesse « ce sous-arbre aura toujours au moins X disponible ».
À utiliser avec précaution. Les réservations sont une arme grossière. Elles permettent d’assurer « cette charge ne manquera jamais d’espace parce qu’un autre locataire a un pic ». Elles sont aussi la manière de créer un pool qui semble à moitié vide et qui renvoie pourtant ENOSPC aux workloads non réservés.
refreservation
refreservation garantit de l’espace au dataset lui-même (sans les descendants). C’est la version personnelle de reservation, tout comme refquota est la version personnelle de quota.
À utiliser quand un dataset a besoin d’espace garanti pour des pics, croissance de métadonnées ou rafales transactionnelles (bases de données, WAL, certains modèles de VM). Mais traitez-la comme une allocation budgétaire : elle réduit l’espace « libre pour tous » du pool.
Un modèle mental rapide
Si vous ne retenez qu’une chose :
quota: plafonner la famille.refquota: plafonner la personne.reservation: garantir la famille.refreservation: garantir la personne.
Comment refquota fonctionne réellement (et ce qu’il ne fait pas)
Référencé vs utilisé : la facture que vous pouvez défendre
La propriété qui importe ici est referenced (souvent affichée REFER dans zfs list). C’est la quantité d’espace qui serait libérée si le dataset était détruit, en excluant les blocs référencés ailleurs (comme par des snapshots en dehors de l’ensemble de snapshots du dataset, ou par des clones dépendants).
refquota est appliqué par rapport à l’espace référencé du dataset. Quand vous définissez refquota, ZFS stoppe les écritures lorsque l’espace référencé de ce dataset dépasserait le plafond.
Cela a deux conséquences opérationnelles :
- Il fournit un « panneau stop » par dataset qui ne s’étend pas simplement parce qu’un dataset enfant existe.
- Il n’empêche pas le pool de se remplir à cause de snapshots détenus ailleurs ou d’autres datasets. Ce n’est pas un garde-fou au niveau du pool ; c’est un outil d’équité.
Ce que refquota ne résout pas
refquota ne vous protégera pas contre :
- La fragmentation du pool et l’espace de marge. ZFS a besoin d’une marge de manœuvre. Si vous maintenez des pools très chargés, vous subirez des effondrements de performance et des comportements ENOSPC bizarres indépendamment des quotas.
- Les explosions de rétention de snapshots. Un dataset peut rester sous son refquota tandis que de vieux snapshots conservent d’énormes quantités d’espace référencé au niveau du pool. Le dataset ne peut pas « voir » cette facture comme sa propre utilisation référencée si les blocs sont attribués ailleurs.
- Les cibles de réplication sans politique cohérente. Un flux send/receive peut recréer des snapshots et des holds qui rendent l’utilisation différente de la source si vous ne faites pas attention à la rétention et aux holds.
Deuxième plaisanterie, car vous en aurez besoin quand le graphe s’envole : un refquota est comme un limiteur de vitesse sur un camion — il ne vous empêchera pas de conduire dans une rivière, mais il vous empêchera de blâmer le moteur ensuite.
Pourquoi le quota seul mène aux « mensonges d’espace utilisé »
Avec les snapshots et les clones, « qui a utilisé l’espace » devient une question de propriété de blocs. Le quota standard est basé sur le sous-arbre et interagit avec les descendants. Si votre structure de locataires est « un dataset parent avec beaucoup d’enfants », un quota sur le parent peut être correct tout en étant opérationnellement inutile pour la facturation et le contrôle du rayon d’impact.
Voici le schéma qui mord :
- Vous mettez
quotasurtank/tenants. - Chaque locataire est un dataset enfant
tank/tenants/acme,tank/tenants/zephyr, etc. - Des snapshots sont pris au niveau du parent ou répliqués d’une manière qui traverse les attentes.
- Un locataire churn des données ; les snapshots retiennent de vieux blocs.
- Le pool se remplit, mais le dataset du locataire paraît « petit » selon la métrique que vous regardez.
refquota sur chaque dataset de locataire change la conversation de « l’espace est compliqué » en « vous avez atteint votre limite ; nettoyez ou achetez plus ».
Trois mini-histoires du monde de l’entreprise
1) Incident causé par une mauvaise hypothèse : « Supprimer des fichiers libère de l’espace »
Le contexte était familier : un pool ZFS partagé derrière une flotte de runners CI et quelques caches de build éphémères. Chaque équipe avait un dataset, et l’équipe plateforme avait mis quota sur un dataset parent. Des snapshots étaient pris chaque heure parce que quelqu’un avait autrefois perdu un cache et déclaré la guerre à la perte de données.
L’incident a commencé par une petite alerte : l’espace libre du pool diminuait plus vite que d’habitude. Puis c’est devenu une vraie alerte : latences d’écriture en hausse, files d’attente, jobs qui échouent avec ENOSPC. Quand le canal d’incident s’est allumé, la première réponse a été la classique : « Supprimez juste les vieux fichiers des caches. » Les équipes ont obtempéré. Elles ont supprimé des dizaines ou centaines de gigaoctets. Le pool n’a pas bougé.
Pourquoi ? Les snapshots. Les snapshots retenaient les anciens blocs, donc supprimer les fichiers actifs ne les libérait pas. Mais la plus grande erreur opérationnelle était l’hypothèse que les quotas dataset empêcheraient qu’une seule équipe cause un impact large. Ils ne l’ont pas fait, parce que le quota était sur le sous-arbre et que la comptabilité regardée était USED de zfs list, pas une vue consciente des snapshots.
La correction n’a pas été héroïque. Elle a été ennuyeuse et correcte : refquota par équipe, plus une politique de rétention qui correspondait au cas d’usage réel (snapshots horaires pour 24 heures, quotidiens pour une semaine, pas « horaires pour toujours »). Après cela, quand une équipe atteignait le mur, c’était leur mur. Le pool a cessé d’être une tragédie partagée.
2) Optimisation qui s’est retournée contre eux : « Clones pour économiser de l’espace »
Une équipe de virtualisation voulait un provisionnement plus rapide pour des VM de dev. Ils avaient des images gold. Ils ont découvert le clonage ZFS et ont décidé de l’utiliser partout : créer un snapshot du modèle, le cloner pour chaque nouvelle VM, et apprécier la création instantanée et le partage des blocs.
Pendant un moment c’était beau. La croissance du stockage ralentissait, les temps de déploiement s’amélioraient, et le tableau de bord semblait sain. Puis est arrivé le jour des patchs. Tout le monde a appliqué des mises à jour OS sur des dizaines de VM, et soudain les écritures ont explosé. La divergence par rapport à l’image de base signifiait que chaque VM commençait à allouer ses propres blocs. C’est normal, mais la surprise est venue du côté humain : les équipes supposaient « le template est partagé donc c’est pratiquement gratuit », et personne n’avait mis de refquotas par VM.
Le pool a atteint un seuil où la performance s’est dégradée. Pas juste « plus lente », mais « les opérations de métadonnées semblent réalisées par pigeon voyageur ». L’équipe a tenté d’atténuer en supprimant des VM, mais certains blocs lourds étaient toujours référencés à cause des clones et des relations de snapshots. L’espace n’est pas revenu là où ils l’attendaient. Ils regardaient les mauvaises métriques.
La récupération a impliqué deux changements de politique : (1) placer chaque dataset de VM sous un refquota aligné sur sa taille prévue, et (2) considérer les clones comme un outil de provisionnement, pas une stratégie de cycle de vie — promouvoir les clones au besoin, et ne pas conserver une chaîne de dépendances complexe sur des mois de snapshots. L’optimisation n’était pas mauvaise ; elle manquait juste de garde-fous.
3) Pratique ennuyeuse mais correcte qui a sauvé la mise : « Budgets d’espace + audits hebdomadaires »
Un autre atelier gérait un appliance ZFS multi-tenant pour des équipes internes. Ce n’était pas spectaculaire : plannings de snapshots prévisibles, nommage cohérent, et un petit rituel hebdomadaire. Chaque lundi matin, un ingénieur passait 15 minutes à revoir un rapport court : top datasets par usedbysnapshots, top par referenced, datasets proches de refquota, et pools sous 20% libre.
Cette pratique semblait inutile — jusqu’à ce qu’une intégration fournisseur commence à déverser de gros logs compressibles dans le mauvais dataset. La compression rendait la taille logique terrifiante, mais l’allocation physique restait modérée… jusqu’à ce que ce ne soit plus le cas. La rotation des logs churnait des blocs quotidiennement, et les snapshots gardaient les anciennes versions. La courbe du pool a commencé à monter.
L’audit du lundi l’a détecté avant que cela ne devienne un incident. L’équipe a vu un dataset avec une augmentation rapide de usedbysnapshots et un refquota qui approchait. Ils ont corrigé le chemin d’ingestion, expiré les snapshots pour ce dataset seulement, et augmenté légèrement le refquota pour éviter de casser une pipeline critique pendant que les changements étaient déployés.
Pas de page nocturne. Pas d’extension d’urgence. Juste une petite habitude ennuyeuse et des quotas qui signifiaient ce qu’ils pensaient signifier. En production, l’ennuyeux est une fonctionnalité.
Tâches pratiques : commandes et interprétations
Ce sont des tâches que vous pouvez exécuter aujourd’hui. Chacune inclut quoi chercher et comment interpréter les résultats. Les exemples supposent un pool nommé tank.
Tâche 1 : Voir la table de vérité : used vs referenced
cr0x@server:~$ zfs list -o name,used,refer,usedbysnapshots,usedbychildren,usedbydataset -r tank
NAME USED REFER USEDBYSNAPSHOTS USEDBYCHILDREN USEDBYDATASET
tank 3.12T 192K 0B 3.12T 192K
tank/tenants 3.12T 128K 540G 2.58T 128K
tank/tenants/acme 900G 600G 220G 80G 600G
tank/tenants/zephyr 780G 760G 10G 10G 760G
Interprétation : USED inclut snapshots et enfants. REFER est l’« empreinte personnelle » du dataset. Si un locataire dit « je n’ai que 600G », vérifiez si des snapshots retiennent 220G et si des enfants utilisent plus.
Tâche 2 : Vérifier quotas et refquotas en une seule commande
cr0x@server:~$ zfs get -o name,property,value,source quota,refquota,reservation,refreservation tank/tenants/acme
NAME PROPERTY VALUE SOURCE
tank/tenants/acme quota none default
tank/tenants/acme refquota 700G local
tank/tenants/acme reservation none default
tank/tenants/acme refreservation none default
Interprétation : Si refquota est défini et quota ne l’est pas, vous appliquez des limites par dataset sans restreindre les descendants. C’est souvent correct pour des datasets de locataires qui ne doivent pas être pénalisés pour des enfants (ou qui n’en utilisent pas).
Tâche 3 : Définir un refquota en toute sécurité (et le confirmer)
cr0x@server:~$ sudo zfs set refquota=500G tank/tenants/acme
cr0x@server:~$ zfs get -o name,property,value refquota tank/tenants/acme
NAME PROPERTY VALUE SOURCE
tank/tenants/acme refquota 500G local
Interprétation : Les écritures vers tank/tenants/acme échoueront une fois que REFER atteindra ~500G (sujets à recordsize, métadonnées, marge). C’est le « panneau stop ».
Tâche 4 : Simuler « pourquoi ma suppression n’a pas libéré d’espace ? »
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation tank/tenants/acme
NAME USED REFER CREATION
tank/tenants/acme@hourly-2025... 24G 600G Mon Dec 23 01:00 2025
tank/tenants/acme@hourly-2025... 30G 620G Mon Dec 23 02:00 2025
Interprétation : USED du snapshot correspond à combien d’espace unique ce snapshot maintient en vie comparé à l’état courant du dataset. Si le USED du snapshot est important, les suppressions ne libèreront pas grand-chose tant que les snapshots n’expirent pas ou ne sont pas détruits.
Tâche 5 : Trouver les datasets proches de leur refquota
cr0x@server:~$ zfs list -o name,refer,refquota -r tank/tenants
NAME REFER REFQUOTA
tank/tenants 128K none
tank/tenants/acme 498G 500G
tank/tenants/zephyr 310G 800G
Interprétation : acme est sur le point d’atteindre un arrêt dur. Vous devriez vous attendre à des erreurs applicatives bientôt (échecs d’écriture), pas à un avertissement doux.
Tâche 6 : Diagnostiquer ENOSPC : vérifier l’état du pool et l’espace libre
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,health
NAME SIZE ALLOC FREE CAP HEALTH
tank 7.25T 6.60T 660G 90% ONLINE
Interprétation : 90% de capacité est une zone dangereuse pour beaucoup de pools, surtout avec des vdevs HDD. Même si des quotas sont en place, le pool peut encore se remplir à cause de snapshots, réservations ou autres datasets.
Tâche 7 : Afficher l’espace retenu par les snapshots (la taxe silencieuse)
cr0x@server:~$ zfs list -o name,usedbysnapshots -r tank/tenants | sort -h -k2
tank/tenants 540G
tank/tenants/acme 220G
tank/tenants/zephyr 10G
Interprétation : Si usedbysnapshots domine, votre « problème d’espace » est en réalité un problème de rétention. Refquota ne supprimera pas cela ; il empêche juste un locataire d’agrandir indéfiniment son empreinte référencée.
Tâche 8 : Identifier les holds qui empêchent la suppression d’un snapshot
cr0x@server:~$ zfs holds -r tank/tenants/acme@hourly-2025-12-23-0200
NAME TAG TIMESTAMP
tank/tenants/acme@hourly-2025-12-23-0200 keep Tue Dec 23 02:10 2025
Interprétation : Un tag de hold (ici keep) bloque la suppression. Si l’espace ne se libère pas après « nous avons supprimé des vieux snapshots », vérifiez les holds avant de supposer que ZFS est hanté.
Tâche 9 : Libérer un hold et détruire le snapshot
cr0x@server:~$ sudo zfs release keep tank/tenants/acme@hourly-2025-12-23-0200
cr0x@server:~$ sudo zfs destroy tank/tenants/acme@hourly-2025-12-23-0200
Interprétation : L’espace ne reviendra pas nécessairement instantanément si d’autres snapshots/clones référencent les mêmes blocs, mais vous avez retiré un ancrage.
Tâche 10 : Auditer les relations de clones (espace partagé dans la lignée)
cr0x@server:~$ zfs get -o name,property,value origin tank/vm/dev-42
NAME PROPERTY VALUE
tank/vm/dev-42 origin tank/vm/template@golden-2025-12-01
Interprétation : Si un dataset est un clone, détruire le snapshot origine peut être bloqué, et la comptabilité peut vous surprendre. L’espace peut être « possédé » par une chaîne de dépendances.
Tâche 11 : Promouvoir un clone pour briser la dépendance à l’origine
cr0x@server:~$ sudo zfs promote tank/vm/dev-42
cr0x@server:~$ zfs get -o name,property,value origin tank/vm/dev-42
NAME PROPERTY VALUE
tank/vm/dev-42 origin -
Interprétation : La promotion inverse la dépendance pour que le clone devienne indépendant dans la lignée. C’est une décision de cycle de vie ; cela peut simplifier le nettoyage et rendre le comportement futur de l’espace plus prévisible.
Tâche 12 : Confirmer ce qu’un dataset libérerait (le rapport défensif)
cr0x@server:~$ zfs get -o name,property,value referenced,logicalreferenced,used,logicalused tank/tenants/acme
NAME PROPERTY VALUE
tank/tenants/acme referenced 498G
tank/tenants/acme logicalreferenced 1.02T
tank/tenants/acme used 900G
tank/tenants/acme logicalused 1.60T
Interprétation : La compression (et parfois les copies) rend les tailles logiques plus grandes que le physique. Si vous facturez en interne, décidez si vous facturez sur le physique (referenced) ou le logique (logicalreferenced). Pour la planification de capacité, le physique compte ; pour « combien de données avez-vous », le logique peut correspondre à l’attente utilisateur.
Tâche 13 : Surveiller l’application de la règle (repérer un hit refquota)
cr0x@server:~$ zfs get -o name,property,value refquota,refer tank/tenants/acme
NAME PROPERTY VALUE
tank/tenants/acme refquota 500G
tank/tenants/acme refer 498G
cr0x@server:~$ sudo -u acmeuser dd if=/dev/zero of=/tank/tenants/acme/bigfile bs=1M count=4096
dd: failed to open '/tank/tenants/acme/bigfile': Disc quota exceeded
Interprétation : L’erreur sera souvent « Disc quota exceeded » (EDQUOT), pas « No space left on device ». C’est une bonne chose : cela indique une politique dataset, pas un épuisement du pool.
Tâche 14 : Trouver les datasets avec des réservations qui peuvent causer des ENOSPC surprises
cr0x@server:~$ zfs get -o name,property,value -r tank reservation,refreservation | egrep -v ' none$'
tank/prod reservation 2T
tank/prod/db refreservation 300G
Interprétation : Les réservations découpent de l’espace. Si un pool semble avoir de l’espace libre mais que des workloads échouent, les réservations sont l’un des premiers suspects.
Guide de diagnostic rapide
Ceci est l’ordre qui trouve rapidement le goulot d’étranglement lors d’incidents réels. Ne commencez pas par débattre des ratios de compression. Commencez par localiser la frontière d’application et le consommateur réel.
1) Déterminer le mode d’échec : EDQUOT vs ENOSPC
Vérifiez les logs applicatifs et les messages du noyau. Si vous voyez « Disc quota exceeded », vous touchez quota ou refquota (ou des quotas utilisateur/groupe). Si vous voyez « No space left on device », le pool est probablement à court d’espace allouable, ou contraint par des reservations/marge.
cr0x@server:~$ dmesg | tail -n 20
...
2) Vérifier d’abord la capacité et la santé du pool (toujours)
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,health
NAME SIZE ALLOC FREE CAP HEALTH
tank 7.25T 6.60T 660G 90% ONLINE
cr0x@server:~$ zpool status -x
all pools are healthy
Décision : Si CAP est élevé (souvent >80–85% sur des pools HDD, parfois moins selon la charge), traitez-le comme un incident de capacité même si « libre » existe. ZFS a besoin de marge pour l’allocation et la performance.
3) Identifier si les snapshots sont le vrai consommateur
cr0x@server:~$ zfs list -o name,usedbysnapshots -r tank | sort -h -k2 | tail -n 10
tank/tenants 540G
tank/backup 1.40T
Décision : Si les snapshots dominent, allez directement aux politiques de rétention, aux holds et au comportement de réplication. Ne perdez pas de temps à supprimer des fichiers actifs.
4) Si l’erreur est EDQUOT, vérifiez refquota/refer d’abord
cr0x@server:~$ zfs get -o name,property,value refquota,quota,refer,used tank/tenants/acme
NAME PROPERTY VALUE
tank/tenants/acme refquota 500G
tank/tenants/acme quota none
tank/tenants/acme refer 498G
tank/tenants/acme used 900G
Décision : Si refer est proche du refquota, la correction est de supprimer/compacter des données dans ce dataset, expirer des snapshots qui gonflent les blocs référencés (rare mais possible selon les relations de clone), ou augmenter volontairement le refquota.
5) Si l’erreur est ENOSPC, vérifiez ensuite les réservations
cr0x@server:~$ zfs get -o name,property,value -r tank reservation,refreservation | egrep -v ' none$'
tank/prod reservation 2T
tank/prod/db refreservation 300G
Décision : Les réservations peuvent consommer l’espace allouable. Réduisez-les, ajoutez de la capacité, ou déplacez des workloads — de préférence pas pendant l’incident sauf si vous avez déjà choisi la douleur.
6) Trouver les principaux consommateurs d’espace par espace référencé
cr0x@server:~$ zfs list -o name,refer -r tank | sort -h -k2 | tail -n 15
tank/backup/job-17 1.10T
tank/tenants/acme 498G
tank/prod/db 420G
Décision : refer vous donne la vue « si je supprime ce dataset, que récupérerai-je ». C’est une manière rapide de classer les candidats au nettoyage.
Erreurs courantes, symptômes et corrections
Erreur 1 : Mettre quota quand vous vouliez refquota
Symptôme : Un dataset de locataire paraît limité, mais les datasets enfants peuvent encore croître d’une manière inattendue, ou la limite s’applique à tout le sous-arbre et provoque des conflits internes.
Correctif : Appliquez refquota à chaque dataset de locataire qui représente une unité facturable. Utilisez quota seulement quand vous voulez explicitement un plafond de sous-arbre.
cr0x@server:~$ sudo zfs set refquota=200G tank/tenants/zephyr
Erreur 2 : Lire USED comme « ce que coûte ce dataset »
Symptôme : Vous supprimez des données d’un dataset, mais son USED change à peine. Ou un dataset semble énorme parce qu’il a des enfants.
Correctif : Utilisez refer pour l’empreinte propre au dataset ; utilisez usedbysnapshots pour voir la taxe des snapshots ; utilisez usedbychildren pour comprendre la croissance du sous-arbre.
Erreur 3 : Ne pas tenir compte de la rétention de snapshots
Symptôme : Le pool se remplit lentement mais sûrement, même si les datasets actifs sont stables et que le « nettoyage » n’aide pas.
Correctif : Inspectez l’usage des snapshots et la rétention. Supprimez les holds. Ajustez les plannings. Considérez des politiques par dataset plutôt que d’un plan global unique.
Erreur 4 : Chaînes de dépendance de clones qui empêchent le nettoyage
Symptôme : Vous ne pouvez pas détruire un vieux snapshot parce que « le snapshot a des clones dépendants », ou l’espace ne revient pas après la suppression de la « grosse chose ».
Correctif : Auditez les relations origin. Promouvez les clones quand c’est approprié. Ne gardez pas des clones liés à un snapshot modèle vieux de plusieurs mois à moins d’aimer l’archéologie.
Erreur 5 : Confondre tailles physiques et logiques
Symptôme : Les utilisateurs déclarent « je n’ai que 300G de données », mais vous voyez 800G logique. Ou la finance veut de la charge interne et les chiffres ne correspondent pas aux attentes.
Correctif : Décidez quelle métrique sert de politique : physique (referenced) pour la capacité, logique (logicalreferenced) pour le volume perçu. Communiquez-le. Documentez-le dans les runbooks.
Erreur 6 : Surutiliser les réservations
Symptôme : Le pool a de l’espace libre mais des écritures échouent pour certains datasets ; ou une charge semble « immunisée » tandis que d’autres manquent.
Correctif : Auditez reservation/refreservation. Supprimez ou redimensionnez. Utilisez les réservations parcimonieusement et intentionnellement.
Erreur 7 : S’attendre à ce que refquota protège le pool
Symptôme : Tous les locataires ont un refquota, mais le pool se remplit toujours et l’équipe plateforme est surprise.
Correctif : Refquota est une application par dataset, pas un plan de capacité du pool. Vous avez toujours besoin d’objectifs de marge de pool, de contrôles de rétention de snapshots et de surveillance des consommateurs globaux du pool comme les backups.
Listes de contrôle / plan étape par étape
Checklist A : Implémenter refquota pour les datasets multi-tenant
- Définir les unités facturables. Un dataset par locataire/VM/conteneur est idéal.
- Choisir une base de quota. Le physique (
refquotasur le référencé) est le plus simple à appliquer ; documentez-le. - Définir refquota sur chaque dataset de locataire.
- Optionnellement définir un quota parent comme disjoncteur. Utilisez
quotasur le parent pour plafonner le programme entier. - Construire une politique de snapshot par classe de données. Les caches CI ne sont pas des bases de données ; ne les traitez pas de la même façon.
- Surveiller les datasets proches des limites. Alertez sur
refer/refquota> 85–90% et sur le taux de croissance deusedbysnapshots.
cr0x@server:~$ sudo zfs set refquota=300G tank/tenants/acme
cr0x@server:~$ sudo zfs set refquota=500G tank/tenants/zephyr
cr0x@server:~$ sudo zfs set quota=5T tank/tenants
Checklist B : Réponse à incident pour « le pool se remplit »
- Vérifier la CAP et la santé du pool (
zpool list,zpool status). - Classer les datasets par
usedbysnapshotsetrefer. - Vérifier les holds sur les gros snapshots qui auraient dû expirer.
- Vérifier les dépendances de clones qui empêchent la suppression de snapshots.
- Ajuster la rétention et supprimer des snapshots de façon chirurgicale (pas globalement, sauf si vous êtes déjà en mode urgence).
- Si le pool est chaud (>85–90%), prévoir une marge immédiate : supprimer, déplacer ou étendre. Les quotas ne créeront pas d’espace.
Checklist C : Hygiène continue qui maintient refquota honnête
- Rapport hebdomadaire : top
usedbysnapshots, toprefer, proches derefquota. - Revue mensuelle des réservations.
- Revue du cycle de vie des snapshots après des changements produits majeurs (nouveaux logs, nouveaux artefacts de build, nouvelle réplication).
- Politique de cycle de vie des templates/clones : quand promouvoir, quand reconstruire depuis zéro.
FAQ
1) refquota est-il appliqué sur l’espace logique ou physique ?
refquota est appliqué sur l’espace référencé tel que ZFS le compte (effectivement l’allocation physique après compression). C’est pourquoi un dataset compressé peut stocker plus de données logiques que ce que le refquota pourrait suggérer.
2) Si je définis refquota, les snapshots peuvent-ils encore remplir mon pool ?
Oui. Les snapshots peuvent retenir de vieux blocs et consommer l’espace du pool. Refquota limite combien le dataset actif référence, pas combien de données historiques votre politique de snapshots retient à travers le pool.
3) Pourquoi un dataset affiche-t-il un REFER faible mais un USED élevé ?
Parce que USED peut inclure l’espace des snapshots et des datasets enfants. REFER est « ce que ce dataset lui-même référence ». Décomposez toujours avec usedbydataset, usedbysnapshots, et usedbychildren.
4) Quelle erreur verront les applications quand refquota est atteinte ?
Généralement Disc quota exceeded (EDQUOT). C’est un indice que vous avez atteint une limite de dataset, pas un épuisement du pool.
5) Dois-je utiliser quota ou refquota pour les répertoires personnels ?
Si chaque utilisateur a son propre dataset, utilisez refquota. Si les utilisateurs sont des répertoires dans un seul dataset, les quotas dataset n’aideront pas ; vous auriez besoin de quotas utilisateur/groupe (un mécanisme différent) ou de restructurer en datasets par utilisateur.
6) refquota inclut-il les descendants ?
Non. C’est le but. refquota limite le dataset lui-même, en excluant les enfants. Si vous voulez un plafond de sous-arbre, c’est quota.
7) Comment refquota interagit-il avec les réservations ?
Une reservation garantit la disponibilité d’espace ; un refquota plafonne la croissance. Vous pouvez avoir les deux : garantir 50G avec refreservation et plafonner à 500G avec refquota. Une mauvaise utilisation peut créer un comportement d’espace déroutant, donc surveillez les deux.
8) Pourquoi augmenter le refquota n’a-t-il pas réglé ENOSPC ?
Parce que ENOSPC est souvent un épuisement au niveau du pool (ou des contraintes de réservation), pas une limite de dataset. Si le pool est à haute capacité, augmenter un refquota change juste qui échouera ensuite.
9) Puis-je compter sur refquota pour la facturation interne ?
Vous pouvez vous en servir pour l’application et le contrôle du rayon d’impact. Pour la facturation, décidez si vous facturez sur l’espace référencé physique, l’espace logique, ou les limites provisionnées. Le plus défendable opérationnellement est de facturer sur des limites appliquées (refquota) plus des exceptions pour dépassement.
10) Quelle est la politique la plus simple qui fonctionne ?
Un dataset par locataire/workload, refquota par dataset, une cible de marge de pool conservative, et une politique de rétention de snapshots liée au besoin métier — pas à l’anxiété.
Conclusion
ZFS ne ment pas sur l’espace. Il rapporte l’espace comme un système de fichiers copy-on-write, avec snapshots et clones, doit le faire : en termes de blocs et de références, pas seulement de fichiers et de dossiers. Le problème est que les humains continuent de poser des questions à ZFS dans le mauvais dialecte, puis sont surpris par les réponses.
refquota est le quota qui traduit la réalité au niveau des blocs de ZFS en une frontière de politique que vous pouvez appliquer par dataset. Il n’empêchera pas votre pool de se remplir si votre politique de snapshots est imprudente, et il ne rendra pas les clones « gratuits pour toujours ». Mais il arrêtera la plus courante des disputes opérationnelles — « cet espace n’est pas à moi » — en rendant « à moi » une empreinte mesurable et applicable. En stockage multi-tenant, ce n’est pas seulement pratique. C’est la différence entre une plateforme stable et un rituel hebdomadaire de reproches sur le stockage.