Vous avez une pile de disques rotatifs, pas de budget pour des SSD, et des utilisateurs qui croient que « stockage » est un bouton à presser pour accélérer l’application.
Pendant ce temps, votre pool ZFS fait exactement ce que la physique permet : I/O aléatoire lent, débit séquentiel correct, et parfois du drame pendant les scrubs.
Ce guide de terrain explique comment rendre ZFS sur HDD étonnamment compétent—sans se mentir. Nous allons tuner ce qui est tunable, éviter les
pièges qui paraissent rapides jusqu’à ce qu’ils corrompent votre semaine, et construire un muscle de diagnostic qui marche à 3h du matin.
Le modèle mental : ce pour quoi les pools HDD sont adaptés
L’optimisation ZFS pour pools uniquement HDD est surtout l’art de ne pas demander aux disques ce qu’ils ne peuvent pas faire. Les disques rotatifs excellent en bande passante séquentielle et sont catastrophiques en IOPS aléatoires. ZFS est excellent pour la robustesse et la flexibilité, et plutôt bon pour les performances—tant que vous alignez votre charge avec la géométrie du pool et le comportement d’écriture/lecture de ZFS.
Une règle à graver dans votre runbook
Pour les pools HDD, vous ne « tunez pas pour les IOPS ». Vous réduisez l’I/O aléatoire, augmentez la séquentialité effective et prévenez l’amplification d’écriture évitable.
Si votre charge est vraiment composée de petites écritures aléatoires, l’optimisation la plus honnête est de changer la charge ou d’acheter des SSD. Tout le reste est une négociation avec la physique.
Pourquoi ZFS peut sembler lent sur des HDD (et pourquoi c’est souvent votre faute)
- Copy-on-write signifie que les réécritures deviennent de nouvelles écritures, avec mises à jour des métadonnées également. La fragmentation et les écritures dispersées apparaissent si vous faites beaucoup de mises à jour aléatoires.
- Transaction groups (TXGs) regroupent les changements et se flushent périodiquement. C’est bon pour le débit ; ça peut être mauvais pour la latence si vous ne comprenez pas le comportement sync.
- Vérification par somme (checksumming) ajoute du travail CPU, et force des lectures de bloc complet sur des écritures partielles dans certains cas (read-modify-write).
- RAIDZ est efficace en capacité, mais les petites écritures aléatoires peuvent déclencher un overhead de parité et des pénalités RMW.
Blague #1 : les HDD sont comme un manager qui lit ses e-mails—incroyables pour traiter un long fil, et catastrophiquement lents si vous l’interrompez toutes les 8 millisecondes.
« Vitesse » signifie plusieurs choses
La plupart des erreurs de tuning viennent d’optimiser le mauvais métrique :
- Débit (MB/s) pour les sauvegardes, médias, stockage d’objets, grosses lectures ETL.
- IOPS pour les charges riches en métadonnées, les dépôts mail, les VM avec écritures aléatoires.
- Latence pour les bases de données, écritures sync des VM, NFS avec sémantique sync.
Vous pouvez souvent améliorer le débit et aussi réduire la latence extrême en évitant les schémas pathologiques (petites écritures sync, recordsize mal dimensionné, RAIDZ trop large, scrubs agressifs en période de pointe). Mais vous ne pouvez pas faire en sorte que huit HDD se comportent comme huit SSD. La victoire consiste à les faire ressembler à un ensemble de huit disques bien alimentés plutôt qu’à un sac de recherches tristes.
Faits et historique qui comptent vraiment
L’optimisation du stockage est plus simple quand vous savez quelles idées « anciennes » ont encore cours dans le comportement moderne. Voici des faits courts et concrets qui continuent de payer :
- ZFS est né chez Sun Microsystems au milieu des années 2000 pour arrêter la corruption silencieuse et simplifier l’administration du stockage ; les performances ont été conçues pour être prévisibles sous contraintes de correction.
- Copy-on-write explique pourquoi ZFS peut toujours valider les données avec des checksums, mais cela signifie aussi que les réécritures aléatoires ont tendance à fragmenter avec le temps.
- RAIDZ a été conçu pour éviter le « write hole » que le RAID-5 classique peut subir en cas de perte de courant ; la cohérence de la parité fait partie du design, pas d’une rustine.
- L’ARC (Adaptive Replacement Cache) a évolué pour dépasser le LRU simpliste en équilibrant récence et fréquence ; sur les pools HDD, l’efficacité de l’ARC est souvent votre plus gros levier « gratuit » de performance.
- Les disques à secteur 4K ont changé la donne et ashift existe parce que l’OS ne peut pas toujours faire confiance à la taille de secteur annoncée par le disque ; le mauvais choix peut pénaliser les performances de façon permanente.
- La compression LZ4 est devenue le favori par défaut car elle est généralement plus rapide que les disques au débit typique des HDD ; elle augmente souvent la bande passante effective en écrivant moins.
- Le ZIL (intent log) n’est pas un cache d’écriture ; il sert à valider en toute sécurité la sémantique des écritures synchrones. Sans dispositif SLOG dédié, le ZIL vit sur les HDD et les écritures sync peuvent devenir le roi de la latence.
- Les scrubs ne sont pas optionnels dans la culture ZFS car les checksums ne détectent la corruption que quand vous lisez ; les scrubs forcent des lectures pour trouver et réparer les erreurs latentes.
- Les RAIDZ larges sont devenus populaires avec les gros disques pour la capacité, mais opérationnellement ils augmentent le temps de resilver et le rayon d’impact d’une mauvaise performance pendant une reconstruction—surtout sur des pools HDD occupés.
Une idée paraphrasée à garder : « L’espoir n’est pas une stratégie ; mesurez le système que vous avez, puis changez une chose à la fois » — idée paraphrasée de Gene Kranz (discipline des opérations de mission).
Décisions d’agencement du pool qui changent tout
Si vous optimisez un pool existant, certains choix d’agencement sont verrouillés. Mais vous devez quand même comprendre ce que vous avez construit, car beaucoup de problèmes de « tuning »
sont en réalité des problèmes du type « vous avez choisi RAIDZ2 pour une ferme de VM ».
Mirrors vs RAIDZ sur HDD
Si votre charge est sensible aux IOPS (VM, bases de données, tempêtes de métadonnées), les mirrors gagnent. Pas d’un peu : les mirrors vous donnent plus d’actionneurs indépendants (spindles) pour les lectures aléatoires et souvent un meilleur comportement en écriture. RAIDZ offre une efficacité de capacité et un excellent débit séquentiel, mais le paie via l’overhead de parité et le read-modify-write sur les petites écritures.
- Mirrors : meilleures IOPS de lecture aléatoire, écritures aléatoires correctes, resilver rapide (copie seulement des blocs alloués).
- RAIDZ2/3 : meilleur TB utilisable par disque, bon pour le streaming séquentiel, comportement en petites écritures plus complexe, resilver potentiellement lourd.
Ne pas aller trop large
« Des vdevs plus larges sont plus rapides » est vrai pour le débit séquentiel, et souvent faux pour la latence et le comportement de reconstruction. Un RAIDZ2 à 12 colonnes peut
streamer comme un champion, puis se transformer en bête fragile et occupée pendant un resilver alors que vos applications se battent pour des seeks.
Alignement recordsize et géométrie des vdevs
ZFS écrit des blocs de taille variable jusqu’à recordsize (datasets) ou volblocksize (zvols). Sur RAIDZ, chaque bloc est réparti sur les colonnes plus la parité. Si vos tailles de bloc ne s’accordent pas avec la géométrie RAIDZ, vous pouvez vous retrouver avec plus d’opérations IO que prévu, et plus d’écritures partielle-stripe.
Pour les pools uniquement HDD, l’objectif pragmatique est : écrire moins, des blocs plus gros et bien compressés ; éviter de les réécrire fréquemment ; garder les chemins de métadonnées et de petites écritures aléatoires sous contrôle.
ashift : on choisit une fois, on subit pour toujours
ashift définit l’exposant de la taille de secteur du pool. En pratique vous voulez presque toujours ashift=12 (4K). Parfois on choisit
ashift=13 (8K) pour certains disques avancés ou pour réduire l’overhead avec des périphériques à gros secteurs. Si vous avez choisi ashift=9 sur
des disques 4K, vous vous êtes acheté une douleur de read-modify-write permanente.
Si vous êtes coincé avec un mauvais ashift, la « correction » consiste à migrer vers un nouveau pool. ZFS est poli ; il ne réécrira pas les fondations de votre pool juste parce que vous le regrettez maintenant.
Tuning des datasets et zvols (recordsize, compression, sync)
La plupart des gains ZFS pour pools HDD proviennent de la définition des propriétés des datasets en fonction de la charge. ZFS permet de tuner par dataset, alors arrêtez de penser « pool-entier »
et commencez à penser « ce dataset contient des disques VM, celui-ci des sauvegardes, celui-là des logs ».
recordsize : arrêtez de faire faire du travail supplémentaire au disque
recordsize influence la manière dont ZFS découpe les données de fichier. Des records plus grands tendent à améliorer le débit séquentiel et l’efficacité de la compression.
Des records plus petits peuvent réduire l’amplification de lecture pour les lectures aléatoires, et réduire la taille de réécriture pour de petites mises à jour—parfois.
Valeurs par défaut opinionnées :
- Sauvegardes, médias, gros objets :
recordsize=1Mest généralement excellent sur des pools HDD. - Partages de fichiers généraux : le
128Kpar défaut est bien ; changez seulement avec des preuves. - Bases de données : souvent
16Kou32K, mais testez avec la taille de page de votre BD et le schéma d’accès. - Images VM en fichiers : dépend ; beaucoup tirent mieux parti de
64Kou128Kplus compression, mais les charges d’écriture aléatoire peuvent toujours souffrir sur RAIDZ.
volblocksize pour les zvols : définissez-le à la création, pas après
Si vous présentez iSCSI ou utilisez des zvols pour disques VM, volblocksize est votre taille de bloc. Elle ne peut pas être changée après la création du zvol.
Alignez-la sur la taille de page du système invité / BD quand c’est possible (souvent 8K ou 16K). Trop petit augmente les métadonnées et les ops IO ; trop grand augmente l’amplification d’écriture pour les petites mises à jour aléatoires.
Compression : le plus proche d’un repas gratuit
Sur HDD, la compression est généralement bénéfique car elle réduit l’I/O physique. lz4 est typiquement la bonne base. La mise en garde est le CPU : si vous manquez de CPU ou utilisez des algorithmes plus lourds, vous pouvez déplacer le goulot d’étranglement vers le processeur.
sync : où la latence va mourir
Les écritures synchrones exigent une confirmation de durabilité. Sans SSDs (pas de SLOG dédié), vos écritures sync sont engagées dans le ZIL sur pool, ce qui signifie des déplacements de tête et de la latence rotationnelle. Les applications qui font beaucoup de petites écritures sync feront paraître un pool HDD en panne.
Vérité dure : passer sync=disabled rendra les benchmarks beauoup plus rapides et les postmortems beaucoup plus coûteux.
atime : arrêtez d’écrire parce que quelqu’un a lu un fichier
Sur les pools HDD, atime=on peut créer un churn d’écritures inutile pour des datasets majoritairement en lecture. Désactivez-le pour la plupart des charges serveur sauf si vous avez une exigence concrète pour les temps d’accès.
xattr et dnodesize : les métadonnées comptent sur des spinners
Les charges lourdes en métadonnées peuvent être brutales sur HDD. Des réglages comme xattr=sa et dnodesize=auto peuvent réduire l’I/O de métadonnées pour certaines charges, mais ce n’est pas une magie universelle. La meilleure leçon : identifiez tôt les tempêtes de métadonnées et isolez-les dans des datasets que vous pouvez tuner.
ARC, pression mémoire, et pourquoi « plus de RAM » n’est pas un mythe
Si vous n’avez pas de SSD, la RAM est votre couche de performance. L’ARC est l’endroit où les lectures chaudes vont pour éviter les seeks disque. Sur les pools HDD, un ARC sain peut faire la différence entre « assez rapide » et « pourquoi ls est lent ».
Taille de l’ARC : ne pas affamer l’OS, ne pas affamer ZFS
La bonne taille d’ARC dépend de votre plateforme et de votre charge. La mauvaise taille est facile : trop grande et vous swappez ; trop petite et vous thrasherez les disques.
Le swap sur un système qui sert de l’I/O, c’est comme mettre du sable dans la boîte de vitesses parce que vous vouliez plus d’adhérence.
Savoir ce qui est dans l’ARC : données vs métadonnées
Les pools HDD tirent souvent un bénéfice disproportionné du cache de métadonnées (entrées de répertoire, blocs indirects, dnodes). Si les manques sur les métadonnées forcent des seeks disque, votre charge « lecture aléatoire » devient « lecture aléatoire de métadonnées » plus « lecture aléatoire de données ». C’est une mauvaise affaire que personne n’a demandée.
Note spéciale sur la déduplication
La déduplication sur des pools uniquement HDD est généralement un cauchemar de performance sauf si vous avez un cas très spécifique, mesuré et beaucoup de RAM pour les tables de déduplication. Si vous voulez économiser de l’espace, commencez par la compression. Si vous voulez quand même la dédup, apportez une calculatrice et un plan de rollback.
Prélecture, lectures séquentielles et charges de streaming
La prélecture est le mécanisme par lequel ZFS essaie d’être utile en lisant par anticipation quand il détecte un accès séquentiel. Sur les pools HDD, l’accès séquentiel est votre zone heureuse.
Quand la prélecture fonctionne, le débit augmente et la latence se lisse. Quand elle se trompe (fréquent avec certains patterns VM), elle peut gaspiller de la bande passante et évincer des entrées ARC utiles.
L’approche de tuning n’est pas « désactiver la prélecture parce que quelqu’un sur un forum l’a dit ». C’est : détecter si la charge est réellement séquentielle, puis tester. Si vous pouvez réorganiser l’I/O pour être plus séquentiel—blocs plus grands, moins de points sync, écritures groupées—faites cela avant de toucher aux réglages de prélecture.
Scrub et resilver : garantir la sécurité sans tuer les performances
Les scrubs et les resilvers sont les moments où votre pool cesse d’être un système de stockage et devient un projet de stockage. Les HDD ont une bande passante finie et des IOPS limités. Si la production est chargée et qu’un resilver commence, quelque chose va perdre. Votre travail est de décider quoi perd et de rendre cela prévisible.
Planifier les scrubs, c’est faire du tuning de performance
Les scrubs sont essentiels. Mais lancer un scrub à midi sur un serveur NFS chargé, c’est la manière d’apprendre ce que vos dirigeants disent quand ils découvrent la latence.
Planifiez les scrubs hors-pointe et surveillez leur durée dans le temps. L’augmentation de la durée est une odeur : fragmentation, croissance des données, disques défaillants, ou changement de charge.
Le comportement de resilver varie selon l’agencement
Les mirrors résilverent souvent plus vite car ils ne copient que les blocs alloués. Les resilvers RAIDZ se sont grandement améliorés au fil des ans (resilver séquentiel), mais ils stressent toujours le pool parce que la reconstruction nécessite des lectures depuis plusieurs disques et des écritures vers un seul, plus des mises à jour de métadonnées.
Blague #2 : un resilver pendant les heures de pointe est la chose la plus proche que le stockage ait d’un exercice au feu réel—sauf que le feu, c’est votre file de tickets.
Tâches pratiques : commandes, sorties et décisions (12+)
Voici des tâches opérationnelles réelles que vous pouvez lancer sur un hôte ZFS (Linux ou variantes illumos avec des outils similaires). Chaque tâche inclut ce que la sortie vous indique et quelle décision prendre. Utilisez un cahier. Ne « tunez » pas de mémoire.
Task 1: Identifier la santé du pool et les chemins lents évidents
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
status: Some supported features are not enabled on the pool.
action: Upgrade the pool to enable all features.
scan: scrub repaired 0B in 05:12:41 with 0 errors on Sun Dec 22 03:10:14 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d4 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d5 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d6 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d7 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d8 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d9 ONLINE 0 0 0
errors: No known data errors
Sens : la santé est correcte ; le scrub prend ~5 heures ; pas d’erreurs. « Features not enabled » n’est pas un incendie de performance en soi.
Décision : Si les scrubs durent plus longtemps chaque mois, vous devrez investiguer la fragmentation et les performances disque. Sinon, passez à autre chose.
Task 2: Voir la latence en temps réel et identifier si ce sont les lectures ou écritures qui posent problème
cr0x@server:~$ zpool iostat -v tank 2 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 48.2T 21.6T 120 980 12.8M 38.4M
raidz2-0 48.2T 21.6T 120 980 12.8M 38.4M
wwn-... - - 18 155 2.1M 6.2M
wwn-... - - 20 160 2.2M 6.4M
wwn-... - - 16 148 1.9M 6.0M
wwn-... - - 19 158 2.1M 6.3M
wwn-... - - 17 152 2.0M 6.1M
wwn-... - - 18 154 2.1M 6.1M
---------- ----- ----- ----- ----- ----- -----
Sens : les écritures dominent les opérations ; le débit est modéré. Si la charge est lente, elle est probablement liée à la latence/IOPS, pas au débit.
Décision : Ensuite, vérifiez les écritures sync et les patterns d’I/O par processus ; validez aussi que l’application n’effectue pas des tempêtes de petits fsync.
Task 3: Vérifier les propriétés des datasets qui décident le plus souvent des performances
cr0x@server:~$ zfs get -o name,property,value -r recordsize,compression,atime,sync tank
NAME PROPERTY VALUE
tank recordsize 128K
tank compression lz4
tank atime off
tank sync standard
tank/vm recordsize 128K
tank/vm compression lz4
tank/vm atime off
tank/vm sync standard
tank/backup recordsize 1M
tank/backup compression lz4
tank/backup atime off
tank/backup sync disabled
Sens : le dataset backup est configuré pour le débit mais a sync=disabled, ce qui est un risque de perte de données pour tout ce qui réclame de la durabilité.
Décision : Si ce dataset est vraiment un « staging de sauvegarde recréable », cela peut être acceptable ; sinon remettez-le à sync=standard et corrigez l’émetteur pour qu’il fasse du batching.
Task 4: Vérifier ashift (alignement secteur) sur chaque vdev
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n
45: vdev_tree:
62: ashift: 12
Sens : ashift=12 est bon pour les disques 4K modernes.
Décision : Si vous voyez ashift: 9 sur des disques 4K, planifiez une migration. Ne perdez pas de temps en micro-tuning.
Task 5: Mesurer le ratio de hits ARC et la pression mémoire
cr0x@server:~$ arcstat 1 5
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:10:01 980 62 6 40 4% 22 2% 10 1% 22G 24G
12:10:02 1012 74 7 51 5% 23 2% 12 1% 22G 24G
12:10:03 990 69 6 44 4% 25 3% 9 1% 22G 24G
12:10:04 1005 80 8 55 5% 25 2% 13 1% 22G 24G
12:10:05 970 58 5 37 4% 21 2% 9 1% 22G 24G
Sens : un taux de miss ~5–8% est correct ; l’ARC est proche de sa cible. Si miss% est élevé et que les disques sont occupés, vous faites plus d’I/O physique que nécessaire.
Décision : Si vous avez de la RAM libre, augmentez la taille max de l’ARC (spécifique à la plateforme). Si non, priorisez le cache de métadonnées via la séparation des charges ou réduisez le working set (snapshots, prolifération de datasets, etc.).
Task 6: Identifier si la charge est liée aux écritures sync
cr0x@server:~$ grep -E "zil|sync" /proc/spl/kstat/zfs/zil
5 1 0x01 107 4080 173968149 2130949321
zil_commit_count 4 84211
zil_commit_writer_count 4 84211
zil_itx_count 4 5128840
zil_itx_indirect_count 4 120
zil_itx_indirect_bytes 4 983040
Sens : un zil_commit_count élevé implique des transactions sync fréquentes.
Décision : Si les utilisateurs se plaignent de latence et que vous voyez beaucoup de commits ZIL, identifiez quel dataset reçoit des I/O sync et quel processus appelle fsync(). La correction se situe souvent au niveau appli/BD, pas ZFS.
Task 7: Trouver quels processus génèrent l’I/O
cr0x@server:~$ iotop -oPa
Total DISK READ: 12.31 M/s | Total DISK WRITE: 41.22 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8421 be/4 postgres 1.12 M/s 9.65 M/s 0.00 % 23.11 % postgres: wal writer
9102 be/4 qemu 4.88 M/s 18.33 M/s 0.00 % 31.74 % qemu-system-x86_64 -drive file=/tank/vm/vm01.img
2210 be/4 root 0.00 B/s 6.10 M/s 0.00 % 9.12 % rsync -a /staging/ /tank/backup/
Sens : PostgreSQL WAL et QEMU sont intensifs en écriture. WAL tend à être sensible au sync.
Décision : Pour la BD, considérez un tuning au niveau dataset (recordsize, placement du log) et des réglages BD (batching des commits). Pour les VM,
envisagez fortement des mirrors pour le dataset VM même si le reste du pool est en RAIDZ.
Task 8: Vérifier la fragmentation et la marge de capacité
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag,health tank
NAME SIZE ALLOC FREE CAP FRAG HEALTH
tank 69.8T 48.2T 21.6T 69% 38% ONLINE
Sens : 69% utilisé, 38% fragmenté. Ce n’est pas catastrophique, mais la tendance est vers « les écritures aléatoires vont empirer ».
Décision : Si la CAP dépasse ~80% sur des pools HDD, attendez-vous à un effet cliff de performance. Planifiez expansion, suppression ou migration avant d’y arriver.
Task 9: Vérifier le ratio de compression et si vous réduisez l’I/O
cr0x@server:~$ zfs get -o name,used,compressratio -r tank | head
NAME USED COMPRESSRATIO
tank 48.2T 1.52x
tank/vm 18.4T 1.18x
tank/backup 22.1T 1.94x
Sens : les sauvegardes se compressent bien (bon). Le dataset VM se compresse peu (normal pour des images OS déjà compressées).
Décision : Gardez lz4 sauf si le CPU est le goulot. Si la compression VM est proche de 1.00x et que le CPU chauffe, vous pouvez envisager de désactiver la compression uniquement pour ce dataset.
Task 10: Inspecter la propriété sync par dataset et corriger les raccourcis dangereux
cr0x@server:~$ zfs get -o name,property,value sync tank/vm tank/backup
NAME PROPERTY VALUE
tank/vm sync standard
tank/backup sync disabled
Sens : Backup est dangereux pour la sémantique sync. Certains outils de sauvegarde s’appuient sur fsync pour la cohérence.
Décision : Si vous ne pouvez pas le justifier dans une revue de risques, changez-le :
cr0x@server:~$ sudo zfs set sync=standard tank/backup
Task 11: Déterminer si vous faites du petit I/O aléatoire (le tueur de HDD)
cr0x@server:~$ sudo fio --name=randread --filename=/tank/testfile --size=4G --direct=1 --rw=randread --bs=4k --iodepth=32 --runtime=20 --time_based
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, ioengine=psync, iodepth=32
fio-3.33
randread: IOPS=420, BW=1.64MiB/s (1.72MB/s)(32.8MiB/20001msec)
lat (usec): min=210, max=185000, avg=76000.15, stdev=22100.42
Sens : les lectures aléatoires 4K sont brutales : quelques centaines d’IOPS et une latence de queue très mauvaise. C’est normal pour des HDD.
Décision : Si votre production ressemble à cela, vous avez besoin de changements architecturaux : mirrors, plus de spindles, plus de RAM/ARC, ou rendre l’I/O moins aléatoire (batching, blocs plus grands, couche de cache extérieure à ZFS).
Task 12: Déterminer la marge de débit séquentiel
cr0x@server:~$ sudo fio --name=seqread --filename=/tank/testfile --size=8G --direct=1 --rw=read --bs=1M --iodepth=8 --runtime=20 --time_based
seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, ioengine=psync, iodepth=8
fio-3.33
seqread: IOPS=620, BW=620MiB/s (650MB/s)(12.1GiB/20003msec)
lat (msec): min=2, max=52, avg=12.9, stdev=4.2
Sens : les lectures séquentielles sont fortes. Donc si votre application est lente, elle effectue probablement de l’I/O non séquentielle ou force la latence sync.
Décision : Tuner recordsize et les patterns de charge vers de l’I/O séquentielle quand c’est possible (écritures en flux, I/O plus grosses, moins de fsync).
Task 13: Vérifier la latence au niveau disque pour trouver un acteur lent
cr0x@server:~$ iostat -x 2 3
avg-cpu: %user %nice %system %iowait %steal %idle
6.12 0.00 2.20 18.40 0.00 73.28
Device r/s w/s r_await w_await aqu-sz %util
sda 9.20 45.10 12.40 18.70 1.22 78.0
sdb 10.10 44.90 13.20 19.10 1.24 79.4
sdc 9.40 45.20 12.10 17.90 1.18 77.9
sdd 8.90 44.70 61.30 88.10 5.90 99.8
Sens : un disque (sdd) a un await bien supérieur et est saturé à 99.8% util. Cela peut tirer tout le vdev vers le bas.
Décision : Récupérez les stats SMART et envisagez un remplacement proactif. Un « disque lent » est un vrai mode de défaillance, pas de la superstition.
Task 14: Vérifier SMART pour secteurs réalloués et erreurs en attente
cr0x@server:~$ sudo smartctl -a /dev/sdd | egrep -i "Reallocated_Sector_Ct|Current_Pending_Sector|Offline_Uncorrectable|SMART overall"
SMART overall-health self-assessment test result: PASSED
5 Reallocated_Sector_Ct 0x0033 098 098 010 Pre-fail Always 12
197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always 4
198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline 4
Sens : « PASSED » est un confort sans signification ; des secteurs en attente et des erreurs non récupérables sont mauvais. Ce disque provoquera des retries et de la latence.
Décision : Remplacez le disque, puis resilver hors-pointe, et surveillez les compteurs d’erreurs pendant l’opération.
Task 15: Vérifier si les snapshots font grossir les métadonnées et le working set
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation | head
NAME USED REFER CREATION
tank/vm@auto-2025-12-01 28G 18.4T Mon Dec 1 02:00 2025
tank/vm@auto-2025-12-02 31G 18.4T Tue Dec 2 02:00 2025
tank/vm@auto-2025-12-03 33G 18.4T Wed Dec 3 02:00 2025
Sens : de nombreux snapshots VM avec « USED » croissant indiquent du churn. Ce churn peut augmenter la fragmentation et la pression sur l’ARC.
Décision : Faites respecter la rétention. Pour des pools HDD, traitez la prolifération de snapshots comme un bug de performance, pas seulement un problème de capacité.
Playbook de diagnostic rapide
Quand quelqu’un dit « le stockage est lent », vous n’avez pas le temps pour un débat philosophique sur les patterns I/O. Vous avez besoin d’un entonnoir rapide pour localiser le goulot : disque, comportement ZFS, mémoire, ou sémantique applicative.
Premier : confirmez si c’est un problème de pool ou d’hôte
-
Vérifiez la santé du pool et l’activité courante.
Lancezzpool status. Si un scrub/resilver est actif, c’est votre manchette. S’il y a des erreurs de checksum, arrêtez le tuning et commencez la réponse à incident. -
Vérifiez l’iowait CPU et le swap.
Si l’hôte swappe ou si le CPU est saturé, les symptômes de stockage peuvent être secondaires. Un iowait élevé signifie souvent que les disques sont le goulot, mais cela peut aussi signifier des stalls d’écritures sync.
Deuxième : déterminez si vous êtes lié à la latence ou au débit
-
Lancez
zpool iostat -v 2.
Si les ops sont élevées et le débit faible, c’est lié aux IOPS/latence (douleur classique des HDD). -
Lancez
iostat -x 2.
Cherchez un disque unique avec un await/%util élevé. Un disque boiteux peut faire paraître un vdev comme une défaillance de conception.
Troisième : vérifiez les tempêtes d’écritures sync et de métadonnées
-
Regardez les comptes de commit ZIL et identifiez les principaux écrivains.
Si vous voyez une forte activité ZIL et un writer BD/WAL en tête, votre « lenteur de stockage » est la rencontre entre la sémantique sync et la latence HDD. -
Vérifiez les stats ARC.
Un mauvais hit ratio plus des disques occupés suggère un working set plus grand que la RAM ou une mauvaise localité ; vous aurez besoin de changements de charge ou de plus de mémoire. -
Vérifiez la fragmentation et la capacité.
Un CAP élevé et une FRAG qui augmente sont des prédicteurs classiques de « ça allait bien l’année dernière ».
Ce que vous décidez à partir du playbook
- Si un scrub/resilver est en cours : throttlez/planifiez ; communiquez ; ne « touchez pas » au tuning en pleine reconstruction sauf si vous aimez le chaos.
- Si un disque est lent : remplacez-le ; arrêtez de blâmer ZFS pour un HDD mourant.
- Si les écritures sync dominent : corrigez le batching applicatif ; isolez le dataset ; n’appliquez pas d’instinct
sync=disabled. - Si les misses ARC sont élevés : ajoutez de la RAM ou réduisez le working set ; séparez les datasets ; corrigez la rétention des snapshots.
- Si vous faites du 4K aléatoire sur RAIDZ : le « tuning » est de changer l’agencement (mirrors) ou d’ajuster vos attentes.
Erreurs courantes : symptôme → cause → correction
1) « Les écritures sont lentes et en pics ; les graphes ressemblent à une dent-de-scie »
Cause : flushs TXG + rafales d’écritures sync + latence HDD. Souvent aggravé par une appli qui fait des fsync fréquents.
Correction : Identifiez le dataset et le processus sync-heavy. Augmentez le batching applicatif ; ajustez les paramètres BD de commit ; isolez dans un dataset avec recordsize approprié ; gardez sync=standard sauf si vous acceptez formellement le risque de perte de données.
2) « Les lectures aléatoires sont terribles ; même un petit ls lag »
Cause : misses de métadonnées dans l’ARC, ou métadonnées fragmentées à cause du churn des snapshots et des petites mises à jour aléatoires.
Correction : Assurez suffisamment de RAM ; réduisez le nombre de snapshots ; envisagez xattr=sa pour les datasets concernés ; gardez la CAP sous ~80% ; séparez les charges riches en métadonnées dans des datasets distincts et revoyez le recordsize.
3) « Après avoir rempli le pool au-delà de 85%, tout est devenu plus lent »
Cause : maps d’espace, pression de l’allocateur, fragmentation ; les seeks HDD augmentent ; les metaslabs deviennent contraints.
Correction : Libérez de l’espace agressivement ; ajoutez de la capacité ; migrez vers un pool plus grand ; ne maintenez pas un état « presque plein » en permanence. Traitez la marge comme une exigence de performance.
4) « Les scrubs prennent une éternité maintenant, et les utilisateurs se plaignent pendant les scrubs »
Cause : dataset en croissance, fragmentation, un disque lent, ou scrubs planifiés pendant un pic IO.
Correction : Déplacez la fenêtre de scrub ; vérifiez la latence par disque ; remplacez les disques lents ; envisagez de réduire la largeur du pool dans les futurs designs ; surveillez la durée des scrubs.
5) « Nous avons désactivé la compression pour ‘réduire le CPU’ et les performances ont empiré »
Cause : vous avez augmenté l’I/O physique sur HDD ; les disques sont redevenus le goulot.
Correction : Utilisez compression=lz4 comme base ; mesurez le CPU. Si le CPU est vraiment le goulot, réduisez d’abord d’autres coûts CPU (chiffrement, checksums lourds) avant de renoncer à la réduction d’I/O.
6) « Le stockage VM sur RAIDZ est imprévisible sous charge »
Cause : l’I/O VM est souvent des petites écritures aléatoires + comportement semblable au sync des systèmes de fichiers invités et hyperviseurs. RAIDZ amplifie la douleur.
Correction : Utilisez des vdevs en mirror pour les VM ; tunez volblocksize ; réduisez la fréquence des fsyncs dans les invités quand c’est sûr ; envisagez un pool/dataset séparé pour les VMs avec des politiques plus strictes.
7) « Un client est lent, les autres vont bien »
Cause : problèmes réseau, sémantique sync côté client, ou une propriété dataset incorrecte pour ce client.
Correction : Vérifiez les propriétés du dataset pour ce partage/volume ; comparez avec un dataset connu bon ; vérifiez les options de montage client et le comportement applicatif.
Trois mini-histoires d’entreprise issues du terrain
Incident causé par une mauvaise hypothèse : « ZIL est un cache, non ? »
Une entreprise de taille moyenne faisait tourner une plateforme analytique interne sur un pool ZFS uniquement HDD. Tout allait « bien » jusqu’à ce qu’une nouvelle équipe déploie un service
qui écrivait de petits états avec une sémantique synchrone. La latence est passée de tolérable à absurde. L’ingénieur on-call a vu que la bande passante disque était basse et a conclu que le pool n’était pas exploité.
Ils ont basculé sync=disabled sur le dataset pour « tester ». Le service est devenu instantanément rapide. Les tickets ont cessé. Le changement est resté.
Personne ne l’a documenté. C’est comme ça que ça commence toujours.
Des semaines plus tard, un événement de coupure a mis le rack hors service. Les systèmes sont revenus. Le service aussi—sauf que certains états étaient incohérents, et il a fallu longtemps pour retrouver ce qui manquait parce que l’application croyait avoir des commits durables. La revue post-incident fut inconfortable à la manière très corporate : beaucoup d’« améliorations de processus », peu de nommage de la vraie erreur technique.
La mauvaise hypothèse n’était pas « sync=disabled est dangereux ». Tout le monde le sait. La mauvaise hypothèse était de croire que le ZIL se comporte comme un cache d’écriture que l’on peut ignorer.
En réalité, le ZIL existe pour rendre les sémantiques sync vraies. Sur HDD, cela signifie que la latence rotationnelle fait partie de votre SLA.
La correction a fini par être ennuyeuse : déplacer l’état sync-heavy vers un autre niveau de stockage (finalement des SSD), et en attendant réduire la fréquence des fsync en batchant les mises à jour et en utilisant une file. Ils ont aussi imposé une politique simple : tout changement de propriété dataset nécessite un ticket avec une déclaration de risque. Cela ne les a pas rendus plus rapides. Cela les a rendus moins fragiles.
Optimisation qui a mal tourné : « Réduisons recordsize pour tout »
Une autre organisation utilisait ZFS pour une charge mixte : partages de fichiers, cibles de sauvegarde, et quelques images VM. Ils ont remarqué un accès aléatoire lent pendant les pics et ont trouvé des conseils en ligne suggérant un recordsize plus petit pour « meilleure performance ». Sans profilage, ils ont mis recordsize=16K récursivement sur tout le pool.
L’effet immédiat sembla positif pour une charge : une petite base de données sur NFS a vu une latence de lecture aléatoire légèrement meilleure. Puis tout le reste a empiré. Les jobs de backup ont duré plus longtemps. Le CPU est monté. Les ops de métadonnées ont augmenté. L’efficacité ARC a chuté car le cache devait suivre beaucoup plus de blocs. Les scrubs ont pris plus de temps parce qu’il y avait plus de blocs à parcourir, et le pool a été occupé plus d’heures.
Le vrai kicker est arrivé un mois plus tard : la fragmentation a augmenté, la CAP a monté, et les utilisateurs ont commencé à subir des pauses intermittentes sur les transferts de gros fichiers. L’équipe de stockage s’est retrouvée dans la boucle classique : « tuner plus » pour réparer le tuning qui avait tout cassé. Ils ont failli désactiver la compression pour « sauver du CPU », ce qui aurait été la seconde blessure auto-infligée.
La récupération fut méthodique. Ils ont rétabli recordsize sur les datasets de backup et de partages à 128K et 1M où approprié, laissant le petit recordsize seulement pour le dataset BD. Ils ont séparé le stockage VM en son propre dataset puis en pool en miroir. Les performances se sont stabilisées.
Leçon : recordsize est un scalpels, pas un rouleau de peinture. Si vous l’appliquez pool-wide, vous obtiendrez des conséquences pool-wide.
Pratique ennuyeuse mais correcte qui a sauvé la mise : « suivre la durée des scrubs »
Une entreprise orientée finance (du genre qui adore les tableurs et déteste les surprises) exploitait un grand pool RAIDZ2 HDD pour des archives de conformité et du stockage interne. Ils n’avaient pas de SSD. Ils n’avaient pas d’illusions non plus sur la performance. Leur lead stockage a fait respecter deux habitudes : les scrubs étaient planifiés hors-pointe, et la durée des scrubs était tracée chaque mois sur un simple tableau de bord. Pas besoin de stack d’observabilité sophistiquée.
Sur plusieurs mois, la durée des scrubs a lentement augmenté. Pas de façon dramatique, mais suffisamment pour être évidente : la ligne de tendance était mauvaise. Personne ne rapportait encore de problèmes. C’est le point : vous voulez trouver les problèmes de stockage quand les utilisateurs dorment.
Ils ont investigué. La latence par disque a montré qu’un disque avait un await plus élevé mais pas d’erreurs dures. SMART montrait des secteurs en attente croissants. Il indiquait encore « PASSED » pour l’évaluation globale. Ils l’ont remplacé de manière proactive pendant une fenêtre planifiée, resilver silencieusement, et les temps de scrub sont revenus au baseline précédent.
Des semaines plus tard, un disque similaire dans un autre châssis est tombé complètement. Cette équipe a subi un incident bruyant et un long resilver sous charge. L’équipe finance-ish non. Leur pratique « ennuyeuse »—suivre la durée des scrubs et vérifier la latence par disque—a payé sans un héroïsme de minuit.
Listes de contrôle / plan étape par étape
Plan étape par étape pour tuner un pool existant uniquement HDD
-
Baseliner la charge.
Capturez :zpool iostat -v 2,iostat -x 2, les stats ARC, et les processus IO principaux. Sauvegardez ces données. -
Validez les éléments non négociables.
Santé du pool, ashift, aucun disque en échec, pas d’erreurs de lecture silencieuses. Si un disque est lent, réglez le matériel d’abord. -
Séparez les datasets par type de charge.
Au minimum :tank/vm,tank/db,tank/backup,tank/home. Le tuning se fait par dataset ; concevez votre namespace en conséquence. -
Définissez des propriétés dataset sûres et opinionnées.
- La plupart des datasets :
compression=lz4,atime=off - Backup/média :
recordsize=1M - Bases de données : commencez par
recordsize=16Kou32K(testez) - zvols VM : définissez
volblocksizecorrectement à la création
- La plupart des datasets :
-
Gérez les écritures sync avec de l’ingénierie, pas du déni.
Gardezsync=standard. Si la latence est inacceptable, la correction primaire est le batching applicatif ou déplacer le chemin sync-heavy vers un autre niveau (même si c’est « plus de RAM et moins de fsync »). -
Faites respecter la marge.
Définissez une politique interne : gardez les pools HDD sous ~80% CAP pour des performances cohérentes. Si vous ne pouvez pas, vous êtes en incident de capacité en attente. -
Gérez les snapshots comme s’ils coûtaient de l’argent (parce que c’est le cas).
La rétention pour le churn VM doit être courte. Les snapshots d’archive doivent être délibérés et limités. -
Planifiez les scrubs et surveillez leur tendance.
Scrub mensuel (fréquemment) hors-pointe. Suivez la durée. Investiguer les variations. -
Re-mesurez après chaque changement.
Une modification à la fois. Comparez au baseline. Gardez ce qui aide. Revenez en arrière ce qui ne marche pas.
Checklist : quand une équipe demande « Peut-on accélérer sans SSD ? »
- La charge est-elle principalement séquentielle ? Si oui, tunez recordsize et compression et vous pouvez gagner.
- Est-ce du petit I/O aléatoire ? Si oui, les mirrors et la RAM aident ; le RAIDZ ne deviendra pas magique.
- Est-ce lourd en écritures sync ? Si oui, corrigez la sémantique applicative ou acceptez la latence ; ne désactivez pas sync à la légère.
- Le pool est-il >80% plein ou fortement fragmenté ? Si oui, la gestion de capacité est du travail de performance.
- Un disque est-il lent ou en erreur ? Si oui, remplacez-le avant de toucher aux réglages.
Checklist : valeurs sûres par défaut pour pools uniquement HDD (par dataset)
- Partages généraux :
compression=lz4,atime=off,recordsize=128K - Sauvegardes :
compression=lz4,recordsize=1M,sync=standardsauf acceptation explicite du risque - Bases de données (fichiers) : commencez
recordsize=16Kou32K, benchmarkez, gardezsync=standard - VM : privilégiez des vdevs en mirror ; pour les zvols définissez
volblocksizecorrectement à la création
FAQ
1) Puis-je obtenir des performances « type SSD » avec ZFS uniquement sur HDD grâce au tuning ?
Non. Vous pouvez obtenir des performances de « baie HDD bien conçue ». C’est toujours utile. Le gain consiste à éliminer les I/O auto-infligées et aligner la charge sur le comportement séquentiel.
2) Dois-je utiliser RAIDZ ou des mirrors pour des pools uniquement HDD ?
Mirrors pour les charges sensibles aux IOPS (VM, bases de données, métadonnées lourdes). RAIDZ pour le stockage massif efficace en capacité et les I/O majoritairement séquentielles.
Si vous essayez de faire tourner des VM sur RAIDZ et que c’est mauvais, c’est parce que ça l’est.
3) compression=lz4 est-il sûr en production ?
Oui, et c’est généralement plus rapide sur des pools HDD. Cela réduit l’I/O physique. La principale raison de le désactiver est un CPU véritablement saturé pour ce dataset et un ratio de compression proche de 1.00x.
4) Quel recordsize devrais-je utiliser ?
128K par défaut pour usage général. 1M pour sauvegardes/médias/gros objets. Plus petit (16K/32K) pour bases de données ou charges avec petits I/O aléatoires, mais testez. Le recordsize est un réglage de forme d’I/O, pas un bouton « tout accélérer ».
5) Est-il acceptable de mettre sync=disabled pour améliorer les performances ?
Seulement si vous pouvez perdre des écritures récentes et avez explicitement accepté ce risque. Cela peut aussi casser des applications qui comptent sur fsync pour la cohérence. Pour la plupart des données en production : gardez sync=standard et corrigez la charge.
6) Dois-je tuner les paramètres du module ZFS pour obtenir de bonnes performances ?
En général non. Les gains les plus importants viennent de l’agencement, des propriétés dataset, du dimensionnement ARC et des pratiques opérationnelles. Le tuning kernel/module est du travail de dernière mile et facile à mal faire, surtout lors des mises à jour.
7) Pourquoi les performances chutent lorsque le pool se remplit ?
La pression de l’allocateur et la fragmentation augmentent, l’espace libre devient moins contigu, et ZFS a moins de bons choix. Les HDD payent le prix des seeks. Gardez une marge ; traitez-la comme partie du design.
8) À quelle fréquence devrais-je scrubber un pool uniquement HDD ?
Couramment mensuellement, hors-pointe. La vraie réponse dépend de la tolérance au risque, de la qualité des disques et des fenêtres de rebuild. Suivez la durée du scrub et les corrections d’erreurs ; les tendances importent plus que l’ordre du calendrier exact.
9) La dédup améliore-t-elle les performances ?
Rarement sur des pools uniquement HDD. Elle nuit souvent aux performances et à la mémoire. Si vous voulez des performances, utilisez la compression. Si vous voulez économiser de l’espace, mesurez et validez avant d’activer la dédup.
10) Quel est le plus grand gain unique sans SSD ?
Pour les charges en lecture : plus de RAM (ARC). Pour les charges mixtes : propriétés dataset correctes et éviter les tempêtes d’écritures sync. Pour VM-heavy : mirrors. Pour « tout est lent » : arrêtez de remplir le pool et remplacez les disques lents.
Étapes suivantes réalisables cette semaine
-
Exécutez le playbook de diagnostic rapide une fois pendant un pic.
Sauvegardez les sorties dezpool iostat,iostat -x, et des stats ARC. Les baselines vous arrêtent de deviner. -
Séparez les datasets par charge et définissez des propriétés sensées.
En particulier :recordsizepour les sauvegardes etatime=offpour la plupart des datasets serveur. -
Cherchez les tempêtes d’écritures sync.
Identifiez qui fait des fsync et pourquoi. Corrigez au niveau applicatif si possible. Ne « réglez » pas ça avecsync=disabled. -
Vérifiez un disque lent.
Utiliseziostat -xet SMART. Un seul acteur lent peut ressembler à une régression systémique. -
Faites respecter la marge et la rétention des snapshots.
Si votre pool tend à dépasser 80% CAP, traitez-le comme un incident de capacité. C’est moins coûteux que d’en faire un mystère de performance. -
Suivez la durée des scrubs.
C’est ennuyeux. Ça marche. C’est le genre de pratique dont on se moque jusqu’à ce qu’elle vous sauve.
La performance ZFS sur HDD est une discipline : bon agencement, sémantiques correctes, frontières de datasets sensées, et mesure implacable. Vous n’essayez pas de gagner les benchmarks. Vous essayez de gagner les mardis.