Quelqu’un finira par exécuter zfs destroy en vitesse. Pas par imprudence — parce que la prod est bruyante, les disques se remplissent à 2 h du matin et les noms de dataset ont l’air d’avoir été générés par un comité. ZFS est puissant, mais ce n’est pas un baby-sitter. Il supprimera exactement ce que vous avez demandé, pas ce que vous vouliez dire.
Voici le guide pratique pour éviter de tomber dans le cratère « mauvais dataset ». Nous passerons en revue les vrais garde-fous offerts par ZFS, les pièges absents, et les vérifications répétables qui rendent les suppressions ennuyeuses.
Ce que zfs destroy fait réellement (et ce qu’il refuse de faire)
zfs destroy supprime un dataset ZFS : un filesystem, un volume (zvol), un snapshot ou un bookmark. Cette suppression est pilotée par les métadonnées ; ZFS n’« efface » pas les blocs à l’ancienne. Il supprime simplement les références, et l’espace devient disponible une fois que rien d’autre ne référence ces blocs.
Cela paraît réversible. En grande partie, ce ne l’est pas. ZFS n’inclut pas de « corbeille » pour les datasets. Si vous n’avez pas pris de snapshot ou répliqué ailleurs, « annuler » est une machine à remonter le temps que vous avez oublié de construire.
Cibles de destruction et options à respecter
- Filesystem :
tank/app— a un mountpoint et peut contenir des enfants. - Snapshot :
tank/app@2025-12-26— enregistrement immuable ; sa suppression libère les blocs référencés que aucun autre snapshot/clone ne conserve. - Volume (zvol) :
tank/vm01— périphérique bloc sous/dev/zvol; souvent utilisé par des hyperviseurs. - Bookmark :
tank/app#bk1— pointeur d’optimisation send/receive ; minuscule, mais avec une signification de dépendance.
Ce sont les options où la sécurité existe ou disparaît :
-rdétruit les enfants (filesystems, snapshots sous un dataset) récursivement. Parfait quand c’est voulu. Catastrophique quand vous vous êtes trompé.-Rdétruit les dépendants, y compris les clones. C’est le bouton « je suis vraiment sérieux » — et aussi le bouton « je ne savais pas que des clones existaient ».-fforce le démontage lors de la destruction d’un filesystem (là où pris en charge). Si vous utilisez-fà la légère, vous êtes déjà hors du chemin heureux.-n(simulation) existe sur certaines plateformes/versions pour certaines opérations, mais ne comptez pas dessus universellement pour le comportement de destroy. Votre workflow sûr ne doit pas dépendre d’une option présente partout.
Voici la vérité inconfortable : zfs destroy n’est pas « dangereux » parce qu’il est imprévisible. Il est dangereux parce qu’il est parfaitement prévisible. Si votre sélection de dataset est bâclée, ZFS exécutera fidèlement votre négligence à la vitesse du réseau.
Une courte blague, parce qu’on en a tous besoin : ZFS n’a pas de « corbeille », il a du « regret de corbeille ».
Pourquoi « mauvais dataset » arrive en pratique
La plupart des incidents de destruction erronée ne sont pas une simple faute de frappe. Ce sont une chaîne de petites hypothèses :
- Vous supposez que le mountpoint équivaut à l’identité du dataset. Ce n’est pas le cas (les mountpoints peuvent être hérités, modifiés ou définis sur legacy).
- Vous supposez que « cet hôte » possède le dataset. Peut-être qu’il est importé ailleurs, répliqué, ou géré par du HA.
- Vous supposez que « personne ne l’utilise » parce que personne n’en a parlé. Pendant ce temps, un conteneur, un hyperviseur ou un agent de sauvegarde l’utilise en silence.
- Vous supposez que les enfants sont inoffensifs. Puis
-refface dix datasets dont vous aviez oublié l’existence. - Vous supposez que les snapshots sont des sauvegardes. Ils ne le sont pas, sauf si vous avez une seconde copie.
Garde-fous intégrés : clones, holds, montages occupés et permissions
ZFS fournit de vrais garde-fous. Ils ne sont pas toujours présents quand vous en avez besoin, et ils ne couvrent pas la catégorie « j’ai pointé sur la mauvaise chose ». Mais les comprendre change immédiatement votre posture de sécurité.
1) Les dépendances des clones bloquent la suppression de snapshots
Si un snapshot a un clone, ZFS ne vous laissera pas détruire le snapshot tant que vous n’avez pas traité la dépendance du clone (ou utilisé une opération de destruction dépendante). C’est l’une des rares fois où ZFS vous sauve de vous-même.
Mais c’est aussi un piège : vous pourriez penser que vous supprimez « un vieux snapshot » pour libérer de l’espace. ZFS refuse, vous ajoutez -R pour « faire fonctionner », et soudainement vous avez aussi détruit un clone que quelqu’un utilisait pour des tests, de l’analyse ou — mon favori personnel — des données « temporaires » devenues permanentes il y a trois trimestres.
2) Holds : la chose la plus proche d’un dispositif de sécurité
Un hold sur un snapshot empêche sa suppression jusqu’à libération explicite. Les holds sont simples, peu coûteux et extrêmement efficaces pour la sécurité. Ils sont aussi sous-utilisés parce qu’ils paraissent « en plus ». En production, « en plus » est tout l’intérêt.
3) Les datasets occupés et les montages peuvent résister à la destruction
Un filesystem monté et en cours d’utilisation peut échouer à se démonter proprement. Selon la plateforme et les options, ZFS peut refuser de le détruire, ou vous pouvez « corriger » avec -f et démonter quelque chose dont une application a encore besoin. Dans les deux cas, c’est instructif.
Si la destruction échoue parce que c’est occupé, ce n’est pas ZFS qui fait chier. C’est ZFS qui vous dit : « Hé, un processus est attaché ; vérifiez peut-être que vous n’êtes pas sur le point de supprimer une charge de travail en direct. » Écoutez.
4) Les permissions déléguées peuvent vous sauver de vous-même (et aussi de la rapidité)
ZFS prend en charge la délégation : vous pouvez autoriser un rôle à snapshotter mais pas à détruire ; ou détruire seulement dans un sous-arbre ; ou exiger des privilèges élevés pour les « couteaux tranchants ». Les organisations qui traitent zfs destroy comme rm -rf / — c’est-à-dire, tout le monde n’y a pas accès — ont généralement moins d’événements négatifs pour les carrières.
5) Nommage, ascendance et la « surprise des enfants »
Les datasets ZFS sont hiérarchiques. Les humains ne le sont pas. Sous pression, on voit tank/app sans remarquer tank/app/cache, tank/app/log, tank/app/old et tank/app/jenkins. Puis -r fait un balayage propre. ZFS fait exactement ce que vous avez demandé.
Faits et historique qui expliquent le comportement actuel
Quelques points de contexte qui rendent le comportement de destruction de ZFS moins mystérieux et plus prévisible :
- ZFS est né chez Sun Microsystems au milieu des années 2000, conçu pour rendre la gestion du stockage plus logicielle que rituelle.
- Copy-on-write est le mécanisme central : ZFS n’écrase jamais les blocs en place. Cela rend les snapshots peu coûteux et fait des suppressions principalement une affaire de comptage de références.
- Les snapshots ne sont pas un stockage séparé. Ce sont des vues à un instant ; l’espace n’est libéré que lorsque les blocs ne sont plus référencés par tous les snapshots/clones.
- Les clones ont été conçus pour un provisioning rapide (pensez dev/test, modèles de VM). La conséquence sécurité : des snapshots peuvent devenir « indestructibles » tant que vous n’avez pas traité les dépendants.
- Les premières versions de ZFS ont mis l’accent sur la clarté administrative via propriétés et héritage, ce qui est excellent — jusqu’à ce que des propriétés héritées fassent que deux datasets se ressemblent au premier coup d’œil.
- zfs destroy est rapide car centré sur les métadonnées. La suppression rapide est une fonctionnalité. Le coût de la sécurité est que vous devez vous ralentir.
- OpenZFS s’est développé sur plusieurs OS. Certaines options/comportements diffèrent subtilement selon la plateforme et la version ; les runbooks portables ne doivent pas supposer une capacité CLI exacte.
- La séparation « zpool/zfs » (commandes pool vs dataset) reflète l’architecture : les opérations de pool sont plutôt physiques, les opérations de dataset plutôt logiques. Les incidents « mauvais dataset » surviennent généralement à la couche logique.
L’ingénierie de fiabilité a une vision du monde pour ça. Voici une idée paraphrasée souvent attribuée à James Hamilton (AWS) : Opérez en supposant que l’échec est normal ; construisez des systèmes et des processus qui le tolèrent.
Cela inclut l’échec de l’opérateur.
Feuille de diagnostic rapide (quand il vous faut de l’espace maintenant)
Voici la séquence « je suis en astreinte, le pool est à 95 %, et quelqu’un va proposer de supprimer quelque chose ». L’objectif : trouver le vrai consommateur d’espace et confirmer l’identité du dataset avant toute action destructive.
Première étape : confirmer l’état du pool et la pression réelle
- Vérifier l’état du pool (erreurs, dégradé, resilvering peuvent fausser les attentes).
- Vérifier usage logique vs physique (compression, snapshots, vdevs spéciaux peuvent changer l’histoire).
- Identifier les datasets principaux par espace utilisé et séparer « données actives » de « données retenues par snapshots ».
Deuxième étape : localiser « espace retenu par les snapshots » vs « espace utilisé par la tête actuelle »
- Utiliser
usedbysnapshotsau niveau du dataset. - Lister les snapshots triés par used si disponible.
- Vérifier la présence de clones/holds qui bloqueront la suppression ou la rendront risquée.
Troisième étape : valider que le dataset est bien celui que vous voulez
- Confirmer le mountpoint et où il est monté (y compris les montages legacy).
- Confirmer que ce n’est pas un zvol qui alimente une VM (chercher des consommateurs).
- Confirmer que ce n’est pas une cible de réplication en cours (détruire une cible de receive en cours est un moyen simple de s’attirer une longue nuit).
Si vous ne suivez qu’une règle : ne décidez jamais « quoi détruire » uniquement à partir d’un chemin comme /srv/app. Décidez à partir du nom du dataset et des propriétés, puis mappez sur les chemins.
Tâches pratiques : commandes, sorties et décisions
Voici des tâches réelles que vous pouvez exécuter en production. Chacune inclut une sortie d’exemple et la décision à en tirer. Adaptez les noms de pool/dataset à votre environnement.
Task 1: Verify the pool is healthy before you touch anything
cr0x@server:~$ zpool status -x
all pools are healthy
Ce que cela signifie : Pas de défaillance connue au niveau du pool pour l’instant.
Décision : Poursuivre le diagnostic normalement. Si ceci montre degraded/faulted, arrêtez et gérez le matériel/resilvering d’abord ; supprimer des données pendant un événement de pool défaillant transforme un problème « serré » en « irrécupérable ».
Task 2: Get a fast “what is using space” view across datasets
cr0x@server:~$ zfs list -o name,used,available,refer,mountpoint -S used tank
NAME USED AVAIL REFER MOUNTPOINT
tank/backup 7.12T 1.80T 7.12T /tank/backup
tank/app 1.04T 1.80T 120G /srv/app
tank/app/log 320G 1.80T 320G /srv/app/log
tank/app/cache 210G 1.80T 210G /srv/app/cache
tank/home 96G 1.80T 96G /home
Ce que cela signifie : USED inclut snapshots/enfants ; REFER est la tête actuelle du dataset (excluant les enfants).
Décision : Si USED est énorme mais REFER petit, les snapshots/enfants sont probablement le problème. Ne détruisez pas « le dataset » pour résoudre une « surcharge de snapshots ».
Task 3: Separate snapshot-held space from live data
cr0x@server:~$ zfs get -o name,property,value -H used,usedbysnapshots,usedbychildren tank/app
tank/app used 1.04T
tank/app usedbysnapshots 880G
tank/app usedbychildren 40G
Ce que cela signifie : La plupart de l’espace est retenu par des snapshots (usedbysnapshots), pas par le filesystem actif.
Décision : Passez en revue la politique de snapshots et supprimez des snapshots spécifiques (avec précaution) plutôt que de détruire le dataset. Si vous détruisez le dataset, vous pouvez supprimer des données actives ainsi que ses descendants. L’excès est toujours fatal.
Task 4: List snapshots and see what you’re actually about to touch
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -S used tank/app
NAME USED REFER CREATION
tank/app@hourly-2025-12-26 22G 120G Fri Dec 26 02:00 2025
tank/app@hourly-2025-12-25 21G 118G Thu Dec 25 23:00 2025
tank/app@daily-2025-12-20 19G 110G Sat Dec 20 01:00 2025
Ce que cela signifie : Le USED d’un snapshot est l’espace unique détenu par ce snapshot.
Décision : Supprimez les snapshots conformément à une politique de rétention connue. Si vous ne pouvez pas expliquer pourquoi un snapshot existe, supposez qu’il existe pour une raison et trouvez cette raison avant de le supprimer.
Task 5: Check for snapshot holds (your “do not delete” tag)
cr0x@server:~$ zfs holds tank/app@daily-2025-12-20
NAME TAG TIMESTAMP
tank/app@daily-2025-12-20 legal Wed Dec 24 10:13 2025
Ce que cela signifie : Un hold nommé legal empêche la destruction de ce snapshot.
Décision : Arrêtez. C’est de la gouvernance ou de l’intention opérationnelle rendue explicite. Trouvez le propriétaire et obtenez l’approbation avant de relâcher le hold.
Task 6: Check for clones that make snapshot deletion risky
cr0x@server:~$ zfs get -H -o value clones tank/app@daily-2025-12-20
tank/devclone
Ce que cela signifie : Le snapshot a un clone dépendant tank/devclone.
Décision : N’utilisez pas -R comme réflexe. Confirmez si tank/devclone est utilisé, et si vous pouvez le promouvoir ou le snapshotter avant toute chaîne de suppression.
Task 7: Confirm dataset identity and ancestry before any destroy
cr0x@server:~$ zfs list -r -o name,mountpoint,canmount,readonly tank/app
NAME MOUNTPOINT CANMOUNT RDONLY
tank/app /srv/app on off
tank/app/cache /srv/app/cache on off
tank/app/log /srv/app/log on off
Ce que cela signifie : C’est le sous-arbre exact que vous affecterez avec -r. Pas d’improvisation.
Décision : Si vous voulez supprimer seulement les logs, ciblez tank/app/log, pas tank/app. Si vous vouliez supprimer toute l’app, assurez-vous que chaque enfant est attendu.
Task 8: Map a mountpoint back to a dataset (stop deleting by path)
cr0x@server:~$ findmnt -T /srv/app -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET SOURCE FSTYPE OPTIONS
/srv/app tank/app zfs rw,xattr,noacl
Ce que cela signifie : Le filesystem monté sur /srv/app est tank/app.
Décision : Utilisez le nom du dataset dans toutes les commandes et approbations. Si SOURCE n’est pas ce que vous attendiez, arrêtez et enquêtez. Les chemins mentent ; les noms ZFS ne mentent pas.
Task 9: Check if a dataset is in active use (before forcing unmount)
cr0x@server:~$ lsof +f -- /srv/app | head
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1421 root cwd DIR 0,105 4096 2 /srv/app
java 1880 app txt REG 0,105 12345678 7 /srv/app/bin/app.jar
Ce que cela signifie : Des processus utilisent des fichiers sous ce montage.
Décision : Ne détruisez pas et ne forcez pas le démontage. Coordonnez l’arrêt de l’application, ou ciblez des snapshots plutôt que le filesystem actif.
Task 10: Confirm whether you’re dealing with a zvol (VM risk)
cr0x@server:~$ zfs list -t volume -o name,used,volsize,readonly tank | head
NAME USED VOLSIZE RDONLY
tank/vm01 220G 256G off
tank/vm02 180G 256G off
Ce que cela signifie : Ces datasets sont des périphériques bloc. Les détruire équivaut à une amputation instantanée de VM.
Décision : Exigez une vérification explicite de la propriété VM avant toute destruction. Traitez les volumes comme plus risqués que les filesystems car le consommateur est souvent un hyperviseur, pas un humain.
Task 11: Check dataset properties that change how “obvious” it is
cr0x@server:~$ zfs get -o name,property,value -H mountpoint,canmount,origin,receive_resume_token tank/app
tank/app mountpoint /srv/app
tank/app canmount on
tank/app origin -
tank/app receive_resume_token -
Ce que cela signifie : Pas un clone (origin est -), et pas en cours de reprise d’un receive.
Décision : Si origin est défini, comprenez la lignée des clones avant suppression. Si receive_resume_token est présent, la réplication est/était en cours — la destruction peut compliquer la récupération ou les envois incrémentaux futurs.
Task 12: Use zfs destroy on a snapshot (narrowly), not a dataset (broadly)
cr0x@server:~$ zfs destroy tank/app@hourly-2025-12-25
Ce que cela signifie : Snapshot supprimé (si aucun hold/clone ne le bloque). L’absence de sortie en cas de succès est typique.
Décision : Préférez supprimer des snapshots spécifiques pour libérer de l’espace plutôt que de supprimer des datasets entiers. Vous taillez, vous ne décapitez pas.
Task 13: Verify that deletion actually returned space (and whether it could)
cr0x@server:~$ zfs get -o name,property,value -H usedbysnapshots tank/app
tank/app usedbysnapshots 840G
Ce que cela signifie : L’espace retenu par les snapshots est passé de 880G à 840G dans notre exemple précédent, donc l’espace se libère comme prévu.
Décision : Si l’espace ne diminue pas, vous avez probablement supprimé des snapshots qui ne tenaient pas beaucoup de données uniques, ou des clones/autres snapshots référencent encore les blocs. Continuez le diagnostic plutôt que de « tenter d’autres destructions ».
Task 14: Check which snapshots are actually holding space (dataset-level view)
cr0x@server:~$ zfs list -t snapshot -o name,used -S used -r tank/app | head
NAME USED
tank/app@hourly-2025-12-26 22G
tank/app@hourly-2025-12-24 22G
tank/app@hourly-2025-12-23 21G
tank/app@daily-2025-12-20 19G
Ce que cela signifie : Les plus grands deltas uniques de snapshot sont en haut.
Décision : Supprimez par ordre décroissant de used seulement si cela correspond à la politique et aux besoins métier. Si un snapshot est volumineux, c’est peut-être parce qu’il a capturé un déploiement majeur.
Task 15: Find “hidden” datasets via legacy mounts
cr0x@server:~$ zfs get -o name,property,value -H mountpoint tank | grep legacy
tank/oldapps mountpoint legacy
Ce que cela signifie : Le mountpoint de ce dataset n’est pas géré par ZFS ; il est probablement monté via /etc/fstab ou une unité de service.
Décision : Ne supposez pas qu’il est inutilisé parce qu’il n’est pas monté où vous l’attendez. Vérifiez les montages système et les consommateurs avant toute suppression.
Task 16: Confirm the exact dataset string you’re about to destroy (human-proofing)
cr0x@server:~$ zfs list -H -o name tank/app
tank/app
Ce que cela signifie : Le dataset existe et vous avez le nom canonique sans bruit de formatage.
Décision : Copiez-collez cette sortie exacte dans votre enregistrement de changement et dans votre ligne de commande. Taper à la main est la façon de créer des réalités alternatives.
Deuxième petite blague (puis on retourne au travail) : La seule chose plus rapide que zfs destroy est la réunion qu’on vous planifie après.
Trois mini-récits d’entreprise depuis les tranchées de la suppression
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
L’équipe avait un modèle standard : les fichiers applicatifs vivaient sous /srv/app, les logs sous /srv/app/log, le cache sous /srv/app/cache. Quelqu’un est passé en astreinte et a hérité du modèle mental : « Si le chemin existe, c’est le dataset. » Cette hypothèse a tenu des années — jusqu’à une migration.
Pendant la migration, un ingénieur stockage a déplacé tank/app/log sur un pool séparé pour isolation I/O et a utilisé un bind mount pour préserver les chemins. Le nom du dataset ZFS a changé ; le mountpoint non. L’astreinte a reçu une alerte pour espace faible et a suivi un ancien runbook : « destroy old logs dataset ». Ils ont exécuté zfs destroy -r tank/app, croyant que cela n’affecterait que le sous-arbre de logs qu’ils visaient.
Cela a fait exactement ce que la commande dit. tank/app était le dataset applicatif live, et -r a parcouru les enfants. Le dataset de logs bind-monté a survécu (pool différent), mais les binaires applicatifs, configs et fichiers clients téléchargés ont disparu. Le monitoring a montré l’app qui bascule ; le load balancer l’a retirée ; incident déclaré.
Le postmortem n’était pas sur une « erreur d’opérateur ». Il portait sur une organisation qui a laissé un runbook basé sur un chemin diverger de la réalité des datasets. La correction a été ennuyeuse : chaque étape destructive mentionnant un chemin devait inclure la vérification du nom du dataset via findmnt et zfs list. Ils ont aussi imposé « pas de -r sans lister les enfants dans le ticket ». Le prochain astreint a détesté ça — jusqu’au prochain quasi-accident.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une équipe plateforme voulait des environnements CI plus rapides. Les clones étaient la solution évidente : prendre un snapshot golden d’un dataset de cache de build, cloner par job, détruire à la fin. C’était élégant. C’était aussi une fuite de stockage lente déguisée en succès.
Le design initial clonait depuis tank/ci/cache@golden et définissait un TTL court. Mais le job de nettoyage dépendait du hook « job finished » du scheduler CI. Quand le scheduler a eu un mauvais jour (partition réseau, redémarrage du control-plane), le nettoyage n’a pas tourné. Les clones se sont accumulés. Tout le monde avait encore un CI rapide, donc personne n’a regardé la croissance du stockage.
Puis le pool a atteint une haute utilisation et les performances se sont dégradées. L’allocation a commencé à se fragmenter ; les écritures synchrones ont ralenti ; des pics de latence sont apparus sur des workloads non liés. L’astreinte a trouvé le plus gros consommateur : tank/ci/cache. Ils ont essayé de supprimer d’anciens snapshots pour libérer de l’espace. ZFS a refusé. Le snapshot avait des clones. « Réglez ça » est devenu « utilisez -R ».
-R a libéré de l’espace. Il a aussi supprimé des clones actifs appartenant à des jobs en cours, déclenchant des échecs de build et une cascade de tentatives. Le pool a récupéré ; CI est devenu une tempête ; le control plane est tombé à nouveau. L’optimisation s’était transformée en boucle de rétroaction.
La correction à long terme n’était pas « ne pas utiliser les clones ». C’était « traiter les cycles de vie des clones comme des données de production ». Ils ont ajouté des holds pour le snapshot golden, un reconciler périodique qui détruit les clones expirés basé sur une propriété ZFS, et une garde : personne ne pouvait exécuter zfs destroy -R dans les namespaces CI sans une seconde approbation.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Un service financier stockait des factures sur ZFS. L’équipe a fait une chose peu sexy mais correcte : utiliser des holds sur les snapshots de conformité et les répliquer hors hôte. Chaque snapshot de fin de mois recevait un hold et une référence de ticket. C’était bureaucratique. Ça a aussi fonctionné.
Un trimestre, un déploiement a accidentellement écrit un flot de mauvaises données — doublons, PDFs corrompus, tout. La réponse initiale s’est concentrée sur un rollback de code, mais les données étaient déjà polluées. Quelqu’un a proposé : « Détruire le dataset et restaurer depuis la sauvegarde. » C’est le genre de proposition qui sonne décisive jusqu’à ce qu’on réalise que « sauvegarde » est un processus, pas un substantif.
Au lieu de cela, ils ont utilisé le snapshot mensuel retenu comme ancre de confiance. Ils ont créé un clone du snapshot pour analyse forensique tout en conservant l’original immuable, puis utilisé la réplication pour restaurer un dataset propre dans un nouveau namespace. L’ancien dataset pollué est resté suffisamment longtemps pour extraire ce qui était encore utile, puis il a été détruit délibérément avec une validation.
Pas d’héroïsme. Pas de panique -R. La clé était que le mécanisme de sécurité « ennuyeux » — holds + réplication hors hôte — permettait à l’équipe d’aller lentement même lorsque l’incident était rapide.
Erreurs courantes : symptôme → cause racine → correctif
1) « J’ai supprimé des snapshots mais le pool n’a pas libéré d’espace »
Symptôme : Vous détruisez plusieurs snapshots ; zpool list change à peine.
Cause racine : Les blocs sont encore référencés par d’autres snapshots, des clones ou la tête du dataset ; les snapshots supprimés ne détenaient pas de données uniques.
Correctif : Inspectez le USED des snapshots et les dépendances de clone. Utilisez zfs list -t snapshot -o name,used -S used et vérifiez zfs get clones sur les snapshots volumineux/anciens. Supprimez les snapshots à espace unique élevé correspondant à la politique de rétention, pas des snapshots au hasard.
2) « Destroy a échoué : dataset occupé »
Symptôme : Erreurs cannot unmount / dataset is busy.
Cause racine : Des processus gardent des fichiers ouverts ; exports NFS ; runtimes de conteneurs ; dépendances systemd de montage.
Correctif : Identifiez les consommateurs avec lsof/fuser, arrêtez proprement les services, puis détruisez. Si la tentation est -f, considérez-le comme une demande de changement, pas un réflexe shell.
3) « J’ai détruit un dataset mais le répertoire existe encore »
Symptôme : Le chemin est toujours là ; vous supposez que la destruction n’a pas fonctionné.
Cause racine : Le répertoire mountpoint est juste un répertoire ; le dataset fournissait un filesystem monté dessus. Après unmount/destroy, le répertoire sous-jacent reste.
Correctif : Vérifiez avec findmnt -T et zfs list. Supprimez ou réutilisez le répertoire intentionnellement ; ne le prenez pas comme preuve d’échec.
4) « J’ai détruit un snapshot et maintenant les incréments de réplication ont cassé »
Symptôme : Le prochain send incrémental échoue car le snapshot de base attendu manque.
Cause racine : L’outil de réplication dépendait d’une chaîne de noms de snapshots spécifique ; la suppression a brisé la chaîne.
Correctif : Alignez la rétention des snapshots avec les exigences de réplication. Préservez les snapshots ancre de réplication via des holds ou des classes de rétention distinctes. Ne supprimez pas des snapshots « vieux au hasard » sur des datasets répliqués.
5) « J’ai lancé destroy -r et j’ai supprimé plus que prévu »
Symptôme : Datasets inattendus disparus ; mountpoints envolés ; services en panne.
Cause racine : Vous n’avez pas inventorié les enfants, ou vous avez supposé que les enfants n’étaient que des répertoires. Ils étaient des datasets.
Correctif : Exécutez toujours zfs list -r et collez la sortie dans le dossier de changement avant d’utiliser -r. Si le sous-arbre vous surprend, arrêtez et redéfinissez la portée.
6) « Destroy a réussi, mais les utilisateurs voient encore des données »
Symptôme : Après la destruction d’un dataset, le chemin sert encore du contenu.
Cause racine : Vous avez détruit un dataset ayant le même mountpoint qu’un autre dataset, ou vous avez un automounter/remount, ou un autre hôte le sert (NFS/SMB/cluster).
Correctif : Confirmez les sources de montage réelles (findmnt), confirmez les exports, et vérifiez que vous êtes sur l’hôte qui sert le chemin. Les noms ZFS sont par hôte ; l’expérience utilisateur peut provenir d’ailleurs.
Checklists / plan étape par étape pour une destruction sûre
Checklist A: Avant de détruire quoi que ce soit (vérifications d’identité)
- Obtenez le nom du dataset depuis le système, pas depuis votre tête. Utilisez
zfs list -H -o nameou le mappingfindmnt. - Confirmez le sous-arbre. Lancez
zfs list -ret examinez les enfants. - Confirmez le comportement de montage. Vérifiez
mountpoint,canmount,readonlyet l’étatlegacy. - Confirmez les consommateurs. Utilisez
lsofsur les mountpoints ; pour les zvols confirmez la propriété VM/hyperviseur. - Vérifiez les holds et les clones de snapshots. Les holds signifient « stop ». Les clones signifient « comprendre les dépendants ».
- Confirmez l’état de réplication/receive. Cherchez des receive resume tokens ou des plannings de réplication connus ; ne cassez pas des chaînes incrémentales à la légère.
Checklist B: Si l’objectif est « libérer de l’espace », préférez ces actions dans l’ordre
- Supprimez les déchets évidents à l’intérieur du filesystem (nettoyage au niveau application) si c’est sûr.
- Supprimez les snapshots qui correspondent à une politique documentée, en commençant par ceux avec le plus d’
USEDunique. - Déplacez les données (répliquez, archivez) puis détruisez un dataset désormais vide.
- Détruisez un dataset entier seulement quand le dataset est vraiment mis hors service, pas simplement « gros ».
Checklist C: Si vous devez détruire un dataset en production
- Prenez un snapshot final (et répliquez-le hors hôte si possible). Si le dataset est déjà corrompu (au niveau app), snapshottez quand même pour un rollback forensique.
- Mettez un hold court sur ce snapshot final afin que personne ne le supprime « utilement » durant la fenêtre de changement.
- Désactivez automounters/services qui pourraient remonter ou recréer le chemin du dataset.
- Détruisez la cible la plus étroite. N’utilisez pas
-rà moins d’avoir l’intention de supprimer tout le sous-arbre. N’utilisez pas-Rà moins d’avoir l’intention de tuer les clones. - Vérifiez la suppression avec
zfs list, vérifiez les montages et le comportement applicatif. - Surveillez l’espace du pool et les erreurs pendant au moins un intervalle de monitoring après le changement.
Garde-fous d’automatisation qui ne vous ralentissent pas
Les pratiques de sécurité humaines sont nécessaires. Elles ne sont pas suffisantes. Les suppressions en production finissent par arriver via l’automatisation : pipelines de déprovisionnement, nettoyage de locataires, teardown CI, réinitialisations d’environnements. Vous avez besoin de rails qui supposent que l’opérateur est fatigué et que le script est littéral.
1) Exiger un motif de allowlist de dataset explicite
L’automatisation ne devrait détruire que des datasets dans un namespace connu, comme tank/ci/* ou tank/ephemeral/*. Si un nom de dataset sort de ce préfixe, le script doit refuser.
2) Imposer « lister le sous-arbre avant destroy récursif »
Avant tout -r, les scripts doivent imprimer la sortie de zfs list -r et exiger une confirmation humaine en mode interactif, ou stocker la sortie comme artefact en mode non interactif. Cela vous donne une preuve forensique ultérieure et empêche les effacements silencieux.
3) Utiliser les holds comme frontière de politique
L’automatisation devrait refuser de détruire tout snapshot avec des holds, et être prudente avec les datasets contenant des snapshots retenus. Les holds sont un « panneau stop » intégré et peu coûteux. Traitez-les comme tels.
4) Taguer les datasets avec des propriétés et prendre des décisions à partir des propriétés
Définissez une propriété personnalisée comme com.example:purpose=ci ou com.example:ttl=2025-12-27T00:00Z (la nomenclature varie selon l’organisation). Ensuite votre job de nettoyage ne détruira que les datasets dont les propriétés indiquent qu’ils sont jetables. Les noms sont utiles. Les propriétés sont applicables.
5) Limiter qui peut détruire en premier lieu
Utilisez la délégation pour que la plupart des rôles puissent snapshotter/cloner mais pas détruire hors de leur sous-arbre. Il ne s’agit pas de confiance ; il s’agit de réduire le rayon d’explosion. En pratique, cela force aussi les opérations de destruction dans des workflows révisables.
FAQ
1) ZFS a-t-il une restauration intégrée pour les datasets détruits ?
Non. Si vous avez détruit un dataset et que vous n’avez ni snapshots ni copie répliquée, la récupération n’est pas quelque chose sur laquelle vous devriez compter. Traitez la destruction comme permanente.
2) Supprimer un snapshot est-il « sûr » comparé à supprimer un dataset ?
Plus sûr, oui, parce que c’est plus ciblé. Toujours pas automatiquement sûr : les snapshots peuvent être des ancres de réplication, des artefacts de conformité ou nécessaires pour un rollback. Vérifiez les holds, les dépendances de clone et votre flux de sauvegarde/réplication.
3) Quelle est la différence entre -r et -R sur zfs destroy ?
-r détruit les datasets enfants et les snapshots sous le dataset cible. -R détruit les dépendants, y compris les clones, et implique une récursion beaucoup plus destructive. Utilisez -R uniquement lorsque vous avez explicitement énuméré ce qu’il emportera avec lui.
4) Pourquoi ZFS refuse-t-il parfois de supprimer un snapshot ?
Les raisons courantes sont : le snapshot a un hold, le snapshot a des clones dépendants, ou vous n’avez pas la permission. ZFS refuse car la suppression violerait des règles de dépendance ou de politique.
5) Puis-je me fier aux mountpoints pour identifier les datasets ?
Pas de manière fiable. Les mountpoints peuvent être hérités, modifiés, définis sur legacy ou masqués par d’autres montages. Mappez toujours les chemins aux datasets avec des outils d’inspection de montage, puis opérez sur les noms de dataset.
6) Si je détruis un dataset, ses snapshots disparaissent-ils aussi ?
Oui, si vous détruisez le dataset (et surtout en récursion), vous supprimez normalement le dataset et ses snapshots dans ce périmètre. Si vous voulez garder un point de récupération, prenez et répliquez un snapshot ailleurs d’abord — puis détruisez.
7) Pourquoi la suppression d’un gros snapshot n’a-t-elle pas libéré autant d’espace que sa taille ?
La « taille » d’un snapshot n’est pas un nombre simple. La valeur USED d’un snapshot est l’espace unique détenu par ce snapshot, pas la vue totale des données. Si d’autres snapshots ou clones référencent les mêmes blocs, l’espace ne sera pas libéré tant que toutes les références n’auront pas disparu.
8) Est-il acceptable d’utiliser zfs destroy -f pour forcer ?
Seulement lorsque vous avez confirmé que le dataset doit mourir et que vous avez identifié pourquoi il est occupé. Forcer le démontage peut casser des applications en cours et peut masquer une erreur de cible. Traitez -f comme une étape d’escalade avec vérification explicite.
9) Quel est le modèle opérationnel le plus sûr pour la mise hors service d’un dataset ?
Snapshot → répliquer (ou autrement assurer une copie hors hôte) → retenir temporairement le snapshot final → désactiver services/montages → détruire de manière ciblée → vérifier. Si vous ne pouvez pas répliquer, au moins snapshottez et retenez pendant une fenêtre définie.
10) Comment empêcher un script de détruire le mauvais dataset ?
Utilisez une allowlist stricte de namespace, validez l’existence et le sous-arbre du dataset, refusez les cibles avec holds, et exigez des propriétés qui marquent le dataset comme jetable. Rendez le script pénible de la bonne manière.
Prochaines étapes que vous pouvez implémenter cette semaine
Si vous exécutez ZFS en production, vous pouvez rendre les destructions de mauvais dataset plus rares sans ralentir l’équipe.
- Mettez à jour vos runbooks : chaque étape destructive doit inclure « mapper le chemin au dataset » et des vérifications « lister le sous-arbre ».
- Adoptez les holds de snapshot pour les snapshots « à conserver » : conformité, fin de mois, pré-migration, pré-upgrade. Les holds sont une assurance peu coûteuse.
- Établissez une convention de nommage + namespace : les datasets jetables vivent sous un préfixe que l’automatisation peut appliquer.
- Restreignez les privilèges de destruction : la plupart des humains n’en ont pas besoin. Déléguez largement snapshot/clone ; restreignez la destruction.
- Pratiquez la feuille de diagnostic d’espace : apprenez à l’astreinte à distinguer
referdeusedbysnapshotspour qu’elle arrête de supprimer des « gros datasets » pour résoudre des « gros snapshots ». - Rendez
-Rsocialement coûteux : exigez l’énumération des dépendances de clone et une seconde paire d’yeux. Cela prévient à lui seul un nombre déprimant d’incidents évitables.
ZFS est un scalpel. Il peut aussi être une tronçonneuse, mais seulement si vous insistez pour le tenir par le mauvais bout. Les garde-fous existent ; votre travail est de les utiliser volontairement.