À 92% plein, ZFS sourit poliment. À 98%, il commence à répondre aux courriels par « comme demandé dans ma dernière allocation ». À 100%, il ne négocie pas : votre renommage « routinier » devient un incident de production, et soudain tout le monde apprend ce que signifie ENOSPC.
Ce guide de terrain couvre le moment désagréable où un pool ZFS manque d’espace (ou où l’espace utilisable par ZFS est épuisé), ainsi que la marche disciplinée pour revenir à une marge saine. Il s’adresse aux personnes qui n’ont pas le temps d’admirer l’élégance du copy-on-write pendant que le pager hurle.
Ce qui casse en premier : la chaîne de défaillance quand ZFS se remplit
« Pool plein » n’est pas un état unique. C’est un ensemble de contraintes en cascade qui affectent différemment les charges. ZFS n’a pas seulement besoin d’octets pour vos données ; il a besoin d’espace de travail pour les métadonnées, la tenue de compte d’allocation, les réécritures copy-on-write et les commits du groupe transactionnel (TXG). Quand le pool approche de la saturation, les choix de l’allocation se dégradent, la fragmentation augmente, et la latence commence à se comporter comme si elle essayait de vous envoyer un message.
1) La première chose que vous ressentirez : la latence, pas forcément des erreurs
Quand l’espace libre se raréfie, ZFS dispose de moins de régions contiguës à allouer. Pour les écritures, cela signifie plus de recherches de metaslab, plus de fragmentation et des E/S dispersées. Même si le pool n’est pas « à 100% », la falaise de performance est réelle. Pour beaucoup de pools, la dégradation commence autour de 80–90% selon le recordsize, la charge et la géométrie des vdev. Ce n’est pas de la superstition ; c’est la physique de l’allocateur et les pénalités de seek déguisées en pile de stockage.
2) Ensuite l’application commence à échouer « mystérieusement »
Les applications échouent souvent de manière indirecte :
- Bases de données peuvent se bloquer sur fsync ou échouer lorsqu’elles ne peuvent pas créer de fichiers temporaires, de segments WAL ou de nouveaux extents d’espace de tables.
- Containers ne démarrent pas parce que les couches overlay ne peuvent pas écrire, les logs ne peuvent pas s’appender, ou le runtime ne peut pas créer d’état sous
/var/lib. - Services système échouent parce qu’ils ne peuvent pas écrire des pidfiles, journald ne peut pas persister, ou les gestionnaires de paquets ne peuvent pas déballer.
3) Supprimer des fichiers ne libère pas toujours de l’espace (et c’est la partie cruelle)
Les snapshots ZFS préservent les blocs référencés. Si vous supprimez un répertoire de 200 Gio mais que des snapshots référencent encore ces blocs, l’utilisation du pool ne bougera pas. Dans un incident « pool plein », c’est le moment où les équipes commencent à accuser ZFS. ZFS est innocent ; il est simplement cohérent.
4) Les bords vraiment tranchants : métadonnées, special vdevs et réservations
Certains incidents « pool plein » sont en réalité des incidents « le pool a de l’espace, mais vous ne pouvez pas l’utiliser » :
- Reservations (
reservation,refreservation) peuvent réserver de l’espace de sorte que vos suppressions n’aident pas le dataset qui vous importe. - Quotas (
quota,refquota) peuvent plafonner un dataset et provoquer ENOSPC même si le pool a de la marge. - Special vdev plein (si vous en utilisez un) peut causer des échecs d’allocation de métadonnées alors que les vdevs principaux ont de la marge.
- Slop space (espace réservé pool-wide) peut bloquer les allocations quand vous êtes proche du plein ; c’est une fonction de sécurité que vous maudirez jusqu’à ce qu’elle vous sauve.
5) Pourquoi « ajouter simplement un disque » n’est pas toujours un soulagement immédiat
Augmenter la capacité peut aider, mais cela ne rembobine pas la fragmentation, ne répare pas un special vdev plein, et ne retire pas les références des snapshots. De plus, le resilvering ou l’expansion ajoute de la charge au pire moment. Vous ajoutez de la capacité pour survivre ; vous devez quand même nettoyer et appliquer des politiques pour récupérer.
Une idée paraphrasée de Werner Vogels (fiabilité/ops) : « Tout échoue, tout le temps ; concevez et opérez en supposant que cela arrivera. » Cela s’applique à la saturation du stockage plus que quiconque ne veut l’admettre.
Faits et histoire intéressants (ceux qui changent les décisions)
- ZFS est né chez Sun au milieu des années 2000 avec le copy-on-write et les checksums de bout en bout comme points fondamentaux, pas comme ajouts.
- « Les snapshots sont bon marché » est vrai—jusqu’à ce que le pool soit plein et que « bon marché » devienne « pourquoi personne ne les a taillés ».
- ZFS utilise un modèle transactionnel (TXGs). Vos écritures ne sont pas « terminées » comme beaucoup le supposent avant le commit du TXG ; la pression s’y manifeste comme une latence de sync.
- La comptabilité d’espace est subtile :
USED,REFER,USEDDS, l’utilisation liée aux snapshots et les réservations disent des vérités différentes. - La règle des « 80% » n’a pas été inventée par ZFS, mais ZFS rend les conséquences des niveaux de remplissage élevés plus visibles parce que le comportement de l’allocateur se dégrade brutalement.
- Les special vdevs (popularisés dans OpenZFS) peuvent accélérer les métadonnées et les petits blocs, mais introduisent un nouveau mode d’échec « plein » si mal dimensionnés.
- Recordsize et volblocksize influencent la fragmentation et l’utilisabilité de l’espace libre. Les gros blocs peuvent être efficaces—jusqu’à ce qu’ils ne trouvent pas d’emplacements.
- La compression peut retarder la crise, mais elle peut aussi masquer la croissance jusqu’à franchir un seuil et soudain chaque écriture devient une lutte pour de l’espace.
- Copy-on-write signifie que « overwriter » nécessite de l’espace libre. Manquer d’espace libre peut casser des charges qui pensent « mettre à jour en place ».
Blague #1 : Un pool ZFS à 99% est comme une salle de réunion réservée « enchaînée » toute la journée : techniquement disponible, pratiquement inutilisable.
Playbook de diagnostic rapide (premier / second / troisième)
Si vous n’avez que cinq minutes avant qu’un VP n’apparaisse dans votre Slack, faites ceci. L’objectif est d’identifier lequel de ces éléments est votre goulot : capacité du pool, quota/réservation du dataset, snapshots, ou une contrainte special vdev/métadonnées.
Premier : confirmer la contrainte réelle
- Le pool est-il réellement plein ? Vérifiez la capacité et la santé avec
zpool list. - Le dataset est-il plafonné ? Vérifiez les quotas/refquotas pour le dataset affecté.
- L’espace est-il « coincé » dans des snapshots ? Regardez l’utilisation des snapshots et le churn récent.
- Des réservations accaparent-elles l’espace ? Cherchez les outliers
refreservationetreservation.
Second : localiser le plus gros consommateur en un minimum de temps
- Répartition par dataset : trouvez quel dataset consomme le pool.
- Répartition par snapshot : trouvez quel ensemble de snapshots épingle le plus d’espace.
- Perspective processus : trouvez quelle charge écrit actuellement et échoue.
Troisième : choisir l’action immédiate la plus sûre
- Soupape de sécurité immédiate : stoppez l’écrivain (pauser l’ingestion, désactiver le spam de logs, arrêter les jobs incontrôlés).
- Récupération à faible risque : supprimez des données jetables dont il n’y a pas besoin en snapshot ; taillez les anciens snapshots si la politique le permet.
- Correction structurelle : ajoutez de la capacité, ajustez quotas/réservations, ou corrigez la taille du special vdev—puis normalisez la politique de snapshots.
Ne commencez pas par « rm -rf » sur des chemins au hasard. Vous ne ferez que créer un second incident : « nous avons effacé la mauvaise chose et en plus nous n’avons pas libéré d’espace ».
Tâches pratiques de récupération (commandes, sorties, décisions)
Voici des tâches éprouvées à exécuter pendant un incident. Chaque tâche inclut une commande, une sortie réaliste, ce que cela signifie et la décision à prendre. Exécutez-les dans l’ordre si vous n’êtes pas sûr. Passez si vous connaissez déjà la forme du problème.
Task 1: Confirm pool capacity and health
cr0x@server:~$ zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 26.6T 640G - - 72% 97% 1.00x ONLINE -
Signification : Le pool tank est à 97% et fortement fragmenté. Vous êtes en zone dangereuse.
Décision : Traitez cela comme une panne en cours. Arrêtez les écrivains non essentiels. Prévoyez de récupérer de l’espace et/ou d’ajouter de la capacité.
Task 2: Look for pool-wide errors and slow devices
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Determine if the device needs to be replaced, and clear the errors
scan: scrub repaired 0B in 12:31:44 with 0 errors on Sun Dec 22 03:11:08 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 2 0 0
sdd ONLINE 0 1 0
errors: Permanent errors have been detected in the following files:
tank/data/postgres/base/16384/2609
Signification : Vous avez des erreurs de périphérique et un fichier référencé avec corruption. C’est séparé du « pool plein » mais peut devenir visible sous charge.
Décision : N’engagez pas de nettoyage agressif avant de snapshotter les datasets critiques (si possible) et de planifier la remédiation des disques défaillants. Si le pool est trop plein pour snapshotter, priorisez d’abord la libération sûre d’un peu d’espace, puis snapshottez.
Task 3: Identify which datasets consume the pool
cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint -S used tank
NAME USED AVAIL REFER MOUNTPOINT
tank 26.6T 512G 192K /tank
tank/data 19.8T 512G 19.8T /tank/data
tank/backups 4.9T 512G 3.1T /tank/backups
tank/vm 1.6T 512G 1.6T /tank/vm
tank/home 310G 512G 310G /tank/home
Signification : Le plus gros consommateur est tank/data, puis tank/backups.
Décision : Concentrez-vous d’abord sur les plus gros datasets. Libérer 50 Gio dans un pool de 27 To peut acheter des minutes, pas de la stabilité.
Task 4: Check whether snapshots are pinning space
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -S used tank/data | head
NAME USED REFER CREATION
tank/data@autosnap_2025-12-26_0000 620G 19.8T Fri Dec 26 00:00 2025
tank/data@autosnap_2025-12-25_0000 410G 19.6T Thu Dec 25 00:00 2025
tank/data@autosnap_2025-12-24_0000 395G 19.5T Wed Dec 24 00:00 2025
tank/data@autosnap_2025-12-23_0000 380G 19.2T Tue Dec 23 00:00 2025
Signification : Les snapshots consomment des centaines de Gio chacun en blocs « uniques » (colonne USED). Les suppressions dans tank/data ne libéreront pas ces blocs tant que les snapshots existent.
Décision : Si les snapshots ne sont pas requis pour conformité ou points de restauration, taillez-les. S’ils sont requis, ajoutez d’abord de la capacité ou déplacez des données hors du pool.
Task 5: Find the biggest snapshot consumers across the pool quickly
cr0x@server:~$ zfs list -t snapshot -o name,used -S used tank | head -n 10
NAME USED
tank/backups@weekly_2025-12-22 1.2T
tank/data@autosnap_2025-12-26_0000 620G
tank/data@autosnap_2025-12-25_0000 410G
tank/vm@hourly_2025-12-26_0900 210G
tank/vm@hourly_2025-12-26_0800 205G
tank/home@daily_2025-12-26 18.4G
tank/home@daily_2025-12-25 17.9G
Signification : Un snapshot de backup consomme 1.2 To ; c’est probablement une rétention devenue hors de contrôle ou un dataset de sauvegarde qui change trop.
Décision : Ciblez d’abord le(s) snapshot(s) à fort impact, mais vérifiez qu’ils sont sûrs à supprimer (politique de restauration, retenue légale, etc.).
Task 6: Verify quotas and refquotas on the failing dataset
cr0x@server:~$ zfs get -o name,property,value,source quota,refquota tank/data
NAME PROPERTY VALUE SOURCE
tank/data quota none default
tank/data refquota 20T local
Signification : tank/data est plafonné à 20 To de référence. Il peut atteindre ENOSPC même si le pool a de l’espace libre.
Décision : Si le dataset est celui qui échoue, envisagez d’augmenter temporairement refquota—après avoir confirmé que cela ne privera pas d’autres datasets critiques.
Task 7: Check reservations and refreservations (space hoarding)
cr0x@server:~$ zfs get -o name,property,value,source reservation,refreservation -r tank | egrep -v 'none|0B|default'
tank/vm refreservation 2T local
tank/db reservation 1T local
Signification : Deux datasets réservent 3 To. Cet espace est effectivement inaccessible pour les autres datasets.
Décision : En urgence, réduisez ou supprimez les réservations si vous pouvez tolérer le risque (les réservations existent pour une raison). Documentez et restaurez-les ensuite.
Task 8: Confirm whether deletions are stuck because a filesystem is “busy”
cr0x@server:~$ lsof +L1 | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
nginx 1421 root 9w REG 0,113 1048576 0 5329156 /tank/data/logs/access.log
postgres 2198 postgres 11w REG 0,113 1073741824 0 5329901 /tank/db/pg_wal/000000010000002A000000B3
Signification : Des fichiers ont été supprimés (NLINK=0) mais sont encore ouverts par des processus. L’espace ne sera pas libéré tant que ces descripteurs de fichiers ne seront pas fermés.
Décision : Redémarrez ou signalez proprement les services fautifs pour rouvrir les logs (ou faites une rotation adéquate). Cela peut récupérer de l’espace sans toucher aux snapshots.
Task 9: Identify what’s writing right now (and stop it with intent)
cr0x@server:~$ zpool iostat -v tank 2 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 26.6T 640G 420 3800 82M 610M
raidz2-0 26.6T 640G 420 3800 82M 610M
sda - - 70 640 14M 98M
sdb - - 66 640 13M 99M
sdc - - 73 940 14M 155M
sdd - - 71 580 14M 93M
---------- ----- ----- ----- ----- ----- -----
Signification : Les écritures sont lourdes et inégales sur sdc. Quelqu’un verse encore des données dans un pool presque plein.
Décision : Suspendez l’ingestion, désactivez la journalisation de debug, arrêtez les jobs batch, ou mettez le service impacté en mode lecture seule jusqu’à ce que la marge soit restaurée.
Task 10: Check if a special vdev is the real “full”
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 26.6T 640G - - 72% 97% 1.00x ONLINE -
raidz2-0 27.0T 26.1T 900G - - 70% 97% - ONLINE
special 200G 198G 2.0G - - 15% 99% - ONLINE
Signification : Le special vdev est à 99% de sa capacité. Si les métadonnées et les petits blocs y atterrissent, vous pouvez obtenir des échecs d’allocation même si ailleurs il reste un peu d’espace.
Décision : Considérez cela comme urgent. Vous devrez probablement ajouter de la capacité au special vdev (mirrorer un autre périphérique et l’attacher, ou reconstruire un special vdev plus grand) et/ou ajuster la stratégie special_small_blocks. Les solutions rapides sont limitées.
Task 11: Free space by destroying snapshots (safely and in the right order)
cr0x@server:~$ zfs destroy tank/data@autosnap_2025-12-23_0000
cr0x@server:~$ zfs destroy tank/data@autosnap_2025-12-24_0000
cr0x@server:~$ zfs destroy tank/data@autosnap_2025-12-25_0000
Signification : La destruction de snapshots est asynchrone ; vous ne verrez peut-être pas immédiatement la libération d’espace si le système est très occupé, mais la tendance sera bonne.
Décision : Supprimez d’abord les plus anciens quand la rétention le permet. Arrêtez-vous lorsque vous avez retrouvé une marge opérationnelle sûre (plus bas sur « combien »), puis corrigez la politique de snapshots pour éviter de refaire ça à 3h du matin.
Task 12: Confirm space reclaimed at the pool level
cr0x@server:~$ zpool list tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 27.2T 25.9T 1.30T - - 69% 95% 1.00x ONLINE -
Signification : Vous avez gagné ~660 Gio. Toujours serré, mais vous avez reculé du bord.
Décision : Continuez jusqu’à obtenir une marge opérationnelle (souvent 15–20% sur des pools chargés). Si vous ne pouvez pas l’atteindre par suppression, il vous faut une expansion de capacité ou une migration de données.
Task 13: Check if a dataset is still blocked by quota even after pool headroom improves
cr0x@server:~$ zfs list tank/data
NAME USED AVAIL REFER MOUNTPOINT
tank/data 19.9T 100G 19.9T /tank/data
Signification : Le pool a plus d’espace maintenant, mais le dataset n’a que 100 Gio disponibles—cohérent avec une limite refquota.
Décision : Augmentez refquota si c’est approprié, ou redistribuez les données vers un autre dataset avec plus de marge.
Task 14: Temporarily raise a refquota (controlled, logged, reversible)
cr0x@server:~$ sudo zfs set refquota=22T tank/data
cr0x@server:~$ zfs get -o name,property,value,source refquota tank/data
NAME PROPERTY VALUE SOURCE
tank/data refquota 22T local
Signification : Le dataset peut maintenant référencer jusqu’à 22 To, si le pool a l’espace.
Décision : Utilisez ceci comme palliatif. Ensuite implémentez planification de capacité et garde-fous pour éviter la culture du « on augmente encore ».
Task 15: Find top space consumers inside a dataset (when you need to delete something real)
cr0x@server:~$ sudo du -xhd1 /tank/backups | sort -h | tail -n 10
120G /tank/backups/tmp
540G /tank/backups/staging
1.8T /tank/backups/daily
2.4T /tank/backups/weekly
Signification : /tank/backups/weekly domine. C’est là que la purge sera efficace.
Décision : Supprimez d’abord dans la couche de rétention la moins critique business. Puis ajustez le job de sauvegarde pour qu’il ne crée pas une croissance non bornée (souvent c’est « conservé indéfiniment » par accident).
Task 16: Verify that snapshot retention tooling isn’t immediately recreating the problem
cr0x@server:~$ systemctl status zfs-auto-snapshot.timer
● zfs-auto-snapshot.timer - ZFS auto-snapshot timer
Loaded: loaded (/lib/systemd/system/zfs-auto-snapshot.timer; enabled; preset: enabled)
Active: active (waiting) since Thu 2025-12-26 09:05:12 UTC; 4h 12min ago
Trigger: Thu 2025-12-26 14:00:00 UTC; 42min left
Signification : Les snapshots automatisés sont planifiés et activés.
Décision : Ne désactivez pas cela à l’aveugle. Ajustez la rétention, excluez les datasets qui n’ont pas besoin de snapshots fréquents, et assurez-vous que la purge fonctionne. Si vous devez le mettre en pause pendant la récupération, mettez-vous un rappel pour le réactiver.
Task 17: Confirm pool feature flags and version context (helps when you call for backup)
cr0x@server:~$ zpool get all tank | egrep 'feature@|ashift|autoreplace|autoexpand'
tank ashift 12 local
tank autoexpand off default
tank autoreplace off default
tank feature@spacemap_histogram enabled local
tank feature@allocation_classes active local
Signification : Vous avez des classes d’allocation actives (commun avec special vdevs), ashift=12, et autoexpand désactivé.
Décision : Quand vous planifierez des changements de capacité, sachez si autoexpand est désactivé et si des classes d’allocation sont impliquées dans un comportement « espace disponible mais inutilisable ».
Trois mini-récits d’entreprise depuis le terrain
Mini-récit n°1 (mauvaise supposition) : « Supprimer les données libérera l’espace. »
Une société SaaS de taille moyenne exploitait un cluster analytique sur ZFS. L’on-call a reçu une alerte pour une panne d’API. Symptôme : écritures échouent, base de données se plaint de « no space left on device ». Le pool affichait 99% d’utilisation. L’on-call a fait ce que la plupart feraient sous stress : il a supprimé un répertoire d’anciens exports.
L’utilisation du pool n’a pas changé. Pas même un pour cent. L’on-call a supprimé davantage. Toujours rien. Maintenant ils avaient un service cassé et des données manquantes qu’un client pourrait demander plus tard. Le canal d’incident s’est rempli de conjectures confiantes, ce qui est toujours un mauvais signe.
La mauvaise supposition était simple : que les suppressions du système de fichiers libèrent immédiatement de l’espace. Sur ZFS avec des snapshots fréquents, ces blocs étaient encore référencés. Ils avaient une politique de snapshots qui prenait des snapshots horaires et les conservait plus longtemps que le cycle de vie des données. Idéal pour les points de restauration. Terrible pour un nettoyage d’urgence.
La récupération fut ennuyeuse mais stricte : identifier les plus gros consommateurs de snapshots, confirmer les exigences de rétention avec le propriétaire des données, détruire les plus anciens snapshots en premier, puis seulement supprimer plus de données filesystem. Ils ont retrouvé de la marge et restauré le service. Le postmortem a créé une règle : « Ne pas supprimer avant d’avoir pris en compte les snapshots. » Ils ont ajouté un tableau de bord montrant l’espace utilisé par snapshot par dataset, afin que le prochain on-call n’ait pas besoin d’un diplôme en théologie ZFS pour faire du triage.
Mini-récit n°2 (optimisation qui s’est retournée contre eux) : le special vdev sous-dimensionné
Une grande équipe plateforme interne a ajouté un special vdev pour accélérer les charges lourdes en métadonnées : des millions de petits fichiers, beaucoup d’opérations de répertoires et des couches de containers. Ça a très bien fonctionné. La latence a chuté. Tout le monde a célébré et est passé à autre chose.
Six mois plus tard, ils ont eu un incident « pool plein » alors que le pool avait encore de l’espace mesurable—des centaines de Gio. Pourtant, la création de nouveaux fichiers échouait sporadiquement. Les renommages se bloquaient. Certains datasets se comportaient normalement ; d’autres s’effondraient. Classique panne partielle : le pire genre.
Le special vdev était en cause. Il avait été dimensionné de manière optimiste, basé sur des estimations de métadonnées et un seuil de « petits blocs » qui capturait en réalité plus que prévu. Il est monté à 99% d’allocation, puis l’allocation pour les métadonnées est devenue un goulot. Les vdevs de données principaux allaient bien, ce qui rendait les symptômes paranormaux.
La correction n’a pas été une astuce CLI. C’était de la capacité : reconstruire le special vdev avec des périphériques plus grands et corriger la politique pour que special_small_blocks corresponde à la réalité. Ils ont aussi appris une vérité opérationnelle : les special vdevs sont puissants, mais ce n’est pas de l’infrastructure optionnelle. Vous les surveillez comme le pool lui-même, et vous planifiez leur croissance comme celle d’une base de données—parce que c’en est, effectivement.
Mini-récit n°3 (pratique ennuyeuse mais correcte) : quotas + rétention + headroom ont sauvé la situation
Une entreprise proche des finances (forte conformité, lourde bureaucratie) utilisait ZFS pour les services de fichiers et les backups. Ils n’étaient pas du type « move fast ». Ils avaient des quotas sur les datasets utilisateurs, des refquotas sur les applications bruyantes, des réservations pour la base de données et une politique de snapshots avec des niveaux de rétention explicites. La plupart des ingénieurs roulaient des yeux en privé.
Puis un outil fournisseur est devenu fou et a commencé à écrire des logs de debug à un rythme impressionnant. Le pool a commencé à grimper. Mais le rayon d’impact a été contenu : le dataset dudit outil a atteint son refquota et a commencé à échouer ses propres écritures sans consommer tout le pool. La réservation de la base de données a tenu. Les répertoires utilisateurs sont restés accessibles en écriture. L’incident s’est limité à « cet outil est cassé », pas « tout est cassé ».
Ils ont nettoyé en plein jour. Ils ont purgé le dataset fautif, corrigé la rotation des logs et ajusté les alertes sur l’espace disponible au niveau dataset, pas seulement sur la capacité du pool. Personne n’a eu à négocier la suppression de données critiques à 2 h du matin.
C’est la partie où l’équipe ennuyeuse gagne. Ils n’ont pas évité les incidents ; ils les ont rendus locaux. Les quotas et les politiques de rétention ne sont pas excitants, mais ce sont des armures opérationnelles.
Blague #2 : Le meilleur moment pour ajuster la rétention des snapshots était il y a six mois. Le deuxième meilleur moment, c’est avant que votre suppression « ne fasse rien » et que vous commenciez à marchander avec le système de fichiers.
Erreurs courantes : symptôme → cause racine → correctif
1) « Nous avons supprimé des données mais l’utilisation du pool n’a pas baissé »
Symptôme : rm se termine ; df et zpool list changent à peine.
Cause racine : Des snapshots référencent encore les blocs, ou des fichiers supprimés sont maintenus ouverts par des processus.
Correctif : Vérifiez l’utilisation des snapshots (zfs list -t snapshot) et les fichiers supprimés mais ouverts (lsof +L1). Détruisez les snapshots inutiles ; redémarrez les processus qui tiennent les fichiers supprimés ; puis vérifiez que l’espace libre du pool change.
2) « Le pool a de l’espace mais l’application dit ENOSPC »
Symptôme : Le pool affiche des centaines de Gio libres ; un dataset ou une application échoue toujours aux écritures.
Cause racine : Limite de refquota/quota sur le dataset, ou une réservation ailleurs empêche l’allocation, ou l’espace slop bloque les allocations.
Correctif : Inspectez quotas/réservations (zfs get quota,refquota,reservation,refreservation). Ajustez la contrainte délibérément, et conservez suffisamment de marge pool-wide pour éviter les collisions avec le slop space.
3) « Les opérations sur petits fichiers échouent en premier »
Symptôme : Création de petits fichiers échoue ; les grosses lectures fonctionnent encore ; certains datasets se comportent pire.
Cause racine : Special vdev plein (pression d’allocation pour les métadonnées) ou fragmentation sévère.
Correctif : Vérifiez l’allocation du special vdev (zpool list -v). Ajoutez de la capacité ou reconstruisez le special vdev plus grand ; reconsidérez special_small_blocks. Réduisez le niveau de remplissage et la fragmentation en restaurant de la marge.
4) « Tout est lent, même les lectures »
Symptôme : Latence élevée sur l’ensemble, près de 95–100% plein ; blocages liés au TXG ; complaints sur « le système est gelé ».
Cause racine : Contention d’allocation et fragmentation ; écritures sync lourdes ; goulots au niveau du périphérique amplifiés sous pression.
Correctif : Arrêtez les gros écrivains, récupérez de l’espace, et envisagez de réduire temporairement le débit d’écriture (throttling applicatif). Après récupération, appliquez une politique de headroom et surveillez la fragmentation.
5) « Nous avons détruit des snapshots mais l’espace libre ne revient pas »
Symptôme : La liste des snapshots diminue, mais l’espace libre du pool n’augmente pas comme prévu.
Cause racine : L’espace est retenu par d’autres snapshots/clones ; des écritures en cours consomment immédiatement l’espace récupéré ; ou des réservations/quotas brouillent les attentes.
Correctif : Vérifiez les clones (zfs get origin), identifiez les plus gros écrivains (zpool iostat), et recontrôlez les réservations. Mettez les écrivains en pause pendant le nettoyage pour réellement regagner de la marge.
6) « Nous avons ajouté de la capacité mais les performances n’ont pas amélioré »
Symptôme : Le pool n’est plus plein, mais la latence reste mauvaise.
Cause racine : La fragmentation persiste ; le special vdev est toujours contraint ; la charge est sync-heavy ; ou déséquilibre des périphériques.
Correctif : Restaurez une marge significative (pas seulement 1–2%). Vérifiez la fragmentation et l’utilisation du special vdev. Envisagez des changements au niveau de la charge (regroupement de logs, alignement recordsize) plutôt que d’espérer qu’une simple capacité résolve tout.
Listes de contrôle / plan étape par étape
Phase 0 : Stabiliser (arrêter l’hémorragie)
- Geler le plus gros écrivain. Pauser les pipelines d’ingestion, désactiver les logs debug incontrôlés, arrêter les jobs batch, ou réduire temporairement les écrivains.
- Confirmer que vous résolvez la bonne contrainte. Pool plein vs quota dataset vs special vdev plein vs fichiers supprimés encore ouverts.
- Communiquer un statut simple. « Nous sommes contraints en espace ; nous récupérons X ; ETA pour le retour des écritures : Y. » Restez factuel.
Phase 1 : Obtenir une marge immédiate (victoires rapides)
- Fermer les fichiers supprimés mais ouverts. Utilisez
lsof +L1, redémarrez proprement les services. - Tailler les pires snapshots en premier. Supprimez les snapshots les plus anciens et à fort
USEDqui ne sont pas requis. - Supprimer les données jetables non snapshotées. Répertoires temporaires, données de staging, caches, anciens artefacts de build—uniquement après vérification que les snapshots ne les épinglent pas.
- Visez un seuil de marge réel. Sur les pools chargés, 10% reste inconfortable. Visez 15–20% si possible.
Phase 2 : Restaurer les opérations normales (rendre les écritures sûres)
- Réactiver les écrivains progressivement. Surveillez
zpool iostatet les taux d’erreurs applicatives ; ne passez pas de zéro à pleine charge instantanément. - Revérifier quotas et réservations. Annulez les changements d’urgence avec précaution ; confirmez que les datasets ont des limites appropriées.
- Lancer un scrub si le matériel était suspect. Si vous avez vu des erreurs de périphérique, planifiez un scrub et remplacez le matériel si nécessaire.
Phase 3 : Corriger le système (pour que cela ne se reproduise pas)
- Mettre en place une rétention de snapshots explicable. Horaire pour un jour, quotidien pour une semaine, hebdo pour un mois—selon ce qui convient au business. Rendre cela explicite, automatisé et audité.
- Alertes au niveau dataset et pool. Pool à 80/85/90% avec urgence croissante ; dataset avail sous des seuils spécifiques à la charge.
- Planification de capacité basée sur le taux de croissance, pas sur les impressions. Suivez les changements hebdomadaires de
zfs list, les deltas de snapshots et le churn des sauvegardes. - Si vous utilisez un special vdev, dimensionnez-le comme si la production en dépendait. Parce que c’est le cas.
Prévention : rendre « pool plein » ennuyeux à nouveau
Politique de marge : choisissez un chiffre et appliquez-le
Si votre pool héberge des charges sensibles à la latence ou intensives en écriture, considérez 80–85% comme « jaune », 90% comme « rouge », et 95% comme « tout arrêter ». Ces seuils ne sont pas des jugements moraux ; ce sont des garde-fous opérationnels. La fragmentation et la pression de l’allocateur ne se soucient pas de votre feuille de route trimestrielle.
Conception des datasets : localiser la défaillance
Placez les charges bruyantes dans leurs propres datasets avec refquota. Placez les systèmes critiques (bases de données, images VM) dans des datasets avec des réservations délibérées seulement si vous avez vraiment besoin d’espace garanti. C’est ainsi que vous empêchez une rafale de logs d’affecter votre base de données.
Discipline des snapshots : la rétention est une fonctionnalité produit
Les snapshots ne sont pas des « sauvegardes gratuites ». Ce sont un système de rétention avec un coût qui explose lors d’un fort churn. Définissez :
- Ce que vous snapshottez (tout ne mérite pas la même politique)
- À quelle fréquence (niveaux horaire/quotidien/hebdo)
- Combien de temps vous les gardez (et qui approuve les augmentations)
- Comment vous les taillez (automatisé, vérifié, surveillé)
Special vdevs : surveillez-les comme un pool à part
Si vous avez adopté des special vdevs, ajoutez des alertes sur leur capacité et observez l’effet de special_small_blocks. Beaucoup d’équipes pensent aux special vdevs comme à un cache. Ce n’en est pas un. C’est une classe d’allocation qui peut devenir votre première limite stricte.
Hygiène opérationnelle qui paye
- Rotation des logs testée (pas seulement configurée). Inclure le cas « processus garde un ancien fd ».
- Sauvegardes qui n’explosent pas les snapshots. Certains workflows de backup provoquent un churn massif ; ajustez-les.
- Tableaux de bord qui montrent : capacité du pool, fragmentation, espace snapshot par dataset, et
availpar dataset. - Runbooks qui incluent les règles de rétention/conformité de votre organisation pour que l’on-call n’ait pas à négocier la politique en pleine urgence.
FAQ
1) Que se passe-t-il réellement quand ZFS est complètement plein ?
Les écritures commencent à échouer avec ENOSPC, mais toutes les écritures n’échouent pas de la même façon. Les allocations de métadonnées, les mises à jour copy-on-write et les écritures sync peuvent échouer ou se bloquer plus tôt que vous ne le pensez. La performance se dégrade généralement avant la panne dure.
2) Pourquoi ZFS devient lent quand il approche du plein même avant les erreurs ?
L’espace libre devient fragmenté. L’allocateur travaille plus dur pour trouver des blocs adaptés, et les E/S deviennent plus dispersées. Sur disques rotatifs, c’est particulièrement pénible ; sur SSD, cela augmente quand même l’amplification d’écriture et la latence.
3) Pourquoi supprimer des fichiers n’a-t-il pas libéré d’espace ?
Le plus souvent : des snapshots référencent encore ces blocs, ou les fichiers sont supprimés mais toujours ouverts par un processus. Utilisez zfs list -t snapshot et lsof +L1 pour confirmer.
4) Dois-je détruire des snapshots pendant une panne ?
Si les snapshots sont la raison pour laquelle vous ne pouvez pas libérer d’espace et que la politique de rétention le permet, oui—détruire des snapshots est souvent la manière la plus propre de récupérer de l’espace. Mais faites-le intentionnellement : identifiez les snapshots à fort USED, confirmez les exigences business, et supprimez les plus anciens en premier.
5) Est-il sûr d’augmenter quota/refquota pour corriger ENOSPC ?
C’est sûr mécaniquement, mais risqué opérationnellement. Vous déplacez la capacité consommable entre occupants du pool. Utilisez-le comme mesure temporaire et associez-le à une correction de capacité/rétention.
6) Combien d’espace libre devrais-je garder sur un pool ZFS ?
Pour des charges mixtes générales : visez 15–20% de marge. Pour des charges majoritairement append avec peu de churn, vous pouvez être un peu plus serré, mais vous achetez du risque et de la latence. Si vous ne pouvez pas maintenir la marge, il vous faut plus de disques ou moins de données—choisissez.
7) Puis-je « défragmenter » un pool ZFS après qu’il soit trop plein ?
Pas directement comme sur des systèmes de fichiers hérités. La méthode pratique est de restaurer de la marge et laisser les nouvelles écritures mieux se placer, ou migrer les données (send/receive) vers un pool neuf pour un vrai reset. Planifiez la capacité pour éviter les héroïsmes.
8) Que faire si le special vdev est plein mais que le pool ne l’est pas ?
Alors vous avez un goulot d’allocation de métadonnées. Vous devrez traiter la capacité du special vdev (souvent en le reconstruisant/étendant) et revoir quels blocs y sont envoyés. Ce n’est pas une situation où « supprimer quelques fichiers » suffit.
9) Les réservations aident-elles ou nuisent-elles dans les situations « pool plein » ?
Les deux. Les réservations protègent des datasets critiques contre des voisins bruyants, ce qui peut sauver la situation. Mais elles réduisent aussi la flexibilité en cas d’urgence. Utilisez-les parcimonieusement et seulement avec une propriété et une surveillance explicites.
10) Dois-je ajouter de la capacité d’abord ou supprimer d’abord ?
Si vous pouvez supprimer en toute sécurité et récupérer de manière significative et rapide, supprimez d’abord. Si les snapshots/la conformité empêchent la suppression, ou si la récupération est lente par rapport au coût de l’incident, ajoutez de la capacité d’abord. Souvent vous faites les deux : ajoutez pour survivre, puis supprimez pour restaurer des marges saines.
Prochaines étapes
Un pool ZFS qui se remplit n’est presque jamais une surprise ; c’est généralement une surprise seulement pour l’on-call qui n’a pas eu le bon signal assez tôt. Quand cela arrive, la meilleure décision est d’arrêter les conjectures. Confirmez la contrainte (pool vs dataset vs snapshots vs special vdev), récupérez de l’espace par les actions les moins irréversibles d’abord, et restaurez une marge à un niveau où ZFS peut allouer correctement.
Étapes pratiques que vous pouvez faire cette semaine :
- Ajouter des alertes sur la capacité du pool, le
availdes datasets, et l’espace utilisé par snapshot par dataset. - Rédiger et appliquer une politique de rétention qui correspond à la réalité business (pas à l’optimisme).
- Placer les charges bruyantes derrière des refquotas pour qu’elles puissent échouer de manière isolée.
- Si vous utilisez des special vdevs, surveillez-les et planifiez leur capacité comme une infrastructure de première classe, pas un accessoire.
- Organiser un game day : simulez un pool à 95%, parcourez le runbook et voyez où votre processus casse avant que la production ne le fasse.
Parce que la seule chose pire qu’un pool plein, c’est un pool plein durant une fenêtre de migration que vous aviez promise « à faible risque ».