volblocksize ZFS : le réglage de stockage VM qui détermine les IOPS et la latence

Cet article vous a aidé ?

Si vous exécutez des machines virtuelles sur ZFS, vous avez probablement déjà ajusté recordsize sur des datasets, débattu des SLOGs en chat, et plaint au moins une fois des « voisins bruyants ». Et puis un jour vous découvrez volblocksize sur les zvols et réalisez que vous rouliez avec le frein à main serré — discrètement, à coût élevé, et en regardant vos graphiques d’IOPS avec incompréhension.

volblocksize est un de ces réglages qui semble trop petit pour importer, comme l’onglet « Avancé » que personne ne clique. Mais il décide comment ZFS découpe les E/S du disque virtuel de la VM, ce qui détermine l’amplification d’écriture, qui détermine la latence, et qui décide si votre base de données pense qu’aujourd’hui est un bon jour pour expirer. Ce n’est pas de la théorie ; cela apparaît comme des pics de latence au 99e percentile en production au pire moment possible.

Ce qu’est réellement volblocksize (et ce que ce n’est pas)

Un zvol est ZFS qui fait semblant d’être un périphérique bloc. Vous le créez avec zfs create -V ..., et il apparaît comme quelque chose du type /dev/zvol/pool/vm-101-disk-0. Votre hyperviseur le formate ensuite (ou le passe brut), et le système invité croit que c’est un disque.

volblocksize est la taille de bloc interne que ZFS utilise pour les données de ce zvol. Quand l’invité écrit des morceaux de 4K, 8K, 64K ou 1M, ZFS doit finalement mapper ces écritures dans ses propres blocs. Sur un dataset, ce réglage est habituellement recordsize. Sur un zvol, le réglage analogue est volblocksize.

Il est tentant de le traiter comme « mettez-le à 4K pour les VM » ou « mettez-le à 128K parce que ZFS aime les gros blocs ». Les deux sont des demi-vérités qui vieillissent mal. La bonne réponse dépend du type d’E/S (aléatoire vs séquentiel), du système de fichiers invité, de la taille des pages de la base de données, du comportement des écritures synchrones et du profil de latence de votre matériel.

Ce que ce n’est pas :

  • Ce n’est pas la taille de bloc du système de fichiers invité. Votre invité peut utiliser des blocs 4K sur un zvol avec volblocksize à 16K ; cela fonctionnera toujours, mais peut coûter cher.
  • Ce n’est pas le ashift du pool. L’alignement compte, mais ashift concerne la taille de secteur physique que ZFS suppose pour les vdevs.
  • Ce n’est pas un interrupteur magique d’IOPS. C’est un bouton de compromis entre l’efficacité des IOPS, le surcoût de métadonnées, le ratio de compression et le risque de pics de latence.

Première blague, parce qu’on l’a méritée : Changer volblocksize après avoir écrit des données sur le zvol, c’est comme essayer de changer la taille d’une pizza après l’avoir mangée — il existe des méthodes, mais aucune ne ressemble vraiment à un « redimensionnement ».

Pourquoi il décide des IOPS et de la latence

Les IOPS et la latence ne sont pas seulement « la vitesse des disques ». Dans le stockage VM, ils concernent aussi combien d’opérations vous forcez la pile de stockage à effectuer pour chaque opération invitée. volblocksize change ce facteur multiplicateur.

La véritable amplification d’écriture que vous pouvez raisonner

Supposons que votre invité effectue une écriture aléatoire de 4K. Ce que fait ZFS dépend de volblocksize :

  • Si volblocksize=4K, ZFS peut mettre à jour un seul bloc de 4K (plus les métadonnées). C’est le mapping « littéral ».
  • Si volblocksize=16K, ZFS doit mettre à jour un bloc de 16K. Si seulement 4K a changé, ZFS écrit quand même un nouveau bloc de 16K (copy-on-write), ce qui implique un comportement de lecture-modification-écriture au niveau logique : il faut assembler le nouveau contenu de 16K puis l’écrire. Selon le cache et la façon dont ce bloc est constitué, vous brûlez de la bande passante et augmentez le risque de latence.
  • Si volblocksize=128K, vous réécrivez potentiellement 128K pour un changement de 4K — encore une fois, au moins logiquement. Si les données sont compressibles et que l’écriture couvre surtout des zéros, vous pouvez avoir de la chance. Mais « peut-être que les données se compressent » n’est pas une stratégie.

Inversez maintenant la perspective pour les E/S séquentielles. Si votre VM lit/écrit en flux (sauvegarde, réplication de logs, grosses copies de fichiers) : les gros blocs peuvent réduire les frais généraux et améliorer le débit parce que ZFS effectue moins d’opérations d’E/S par mégaoctet.

La latence est un jeu de queue

La latence moyenne vous enjolive. La latence en queue vous humilie. Les gros blocs augmentent la quantité de travail par modification logique et élargissent la distribution de latence quand le système est sous pression : plus d’octets à déplacer, plus de temps coincé derrière d’autres écritures, plus de temps en attente des commits txg, et plus d’opportunités de collision avec des contraintes d’écritures synchrones.

En production, les pires moments sont prévisibles : orages de snapshots, fenêtres de sauvegarde, scrubs, resilvers, et ce job trimestriel dont personne n’a informé le SRE. Un choix de volblocksize qui est « correct dans un benchmark » peut devenir votre générateur d’incidents au 99,9e percentile quand le pool est rempli à 70 % et fragmenté.

Les métadonnées et le CPU ne sont pas gratuits non plus

Des blocs plus petits signifient plus de blocs, donc plus de métadonnées : plus de pointeurs de blocs, plus de blocs indirects, plus d’opérations de checksumming, plus de décisions de compression, plus de travail pour l’ARC. Vous pouvez tout à fait créer un système « rapide en IOPS » mais lié au CPU dans la couche de stockage, surtout avec chiffrement ou compression lourde.

Faits intéressants et contexte historique

Les ingénieurs stockage adorent le folklore ; voici les éléments concrets qui comptent vraiment :

  1. ZFS est né à l’époque des disques tournants, où le débit séquentiel importait et le coût des seeks était brutal. Les gros blocs avaient beaucoup de sens.
  2. Le copy-on-write est la superpuissance et le percepteur d’impôts de ZFS. Il permet les snapshots et la cohérence, mais signifie aussi que « de petits changements peuvent réécrire de gros blocs » si vous choisissez de gros blocs.
  3. Les zvols ont été conçus pour fournir des périphériques bloc pour iSCSI, disques VM et usages type swap — des workloads qui ne se comportent pas comme du trafic NAS de gros fichiers.
  4. Les secteurs 4K ont gagné l’industrie, mais la transition a été chaotique (512e, Advanced Format). Le désalignement peut couper silencieusement les performances en deux.
  5. Les hyperviseurs VM ont changé le jeu des motifs d’E/S. Thin provisioning, snapshot à l’hyperviseur et rafales d’écritures aléatoires sont devenus la norme.
  6. Les tailles de pages de base de données sont souvent 8K ou 16K (varie selon le moteur et la config). Quand la taille de bloc du stockage lutte avec la page DB, vous payez deux fois : une fois en I/O, une fois en comportement WAL/redo.
  7. Les SSD ont rendu l’E/S aléatoire bon marché par rapport aux HDD, mais pas gratuite. La latence est plus faible, mais l’amplification d’écriture (dans ZFS et dans le FTL du SSD) compte toujours.
  8. NVMe a tellement réduit la latence que le « coût logiciel » est devenu visible. Soudain, les choix de taille de bloc apparaissent comme temps CPU et contention de verrous, pas seulement attente disque.
  9. La compression ZFS est devenue courante parce que les CPU sont rapides et le stockage reste cher. La compression interagit fortement avec la taille de bloc : les blocs plus grands compressent souvent mieux, mais peuvent amplifier les petites écritures.

Un modèle mental utilisable à 3 h du matin

Pensez à volblocksize comme à « l’unité minimale de réécriture » du zvol à l’intérieur de ZFS. L’invité peut écrire 4K, mais si ZFS stocke les données en morceaux de 64K, ZFS est responsable de produire une nouvelle version de 64K de ce morceau à chaque modification.

Il y a trois grandes conséquences :

  • IOPS d’écriture aléatoire : un volblocksize plus petit aide généralement, car vous réécrivez moins d’octets par écriture.
  • Débit séquentiel : un volblocksize plus grand peut aider, car vous amortissez le travail de métadonnées et de checksums.
  • Latence en queue : les petits blocs ont tendance à être plus prévisibles sous charges mixtes, tandis que les gros peuvent provoquer de forts pics lorsque vous combinez écritures synchrones + pression sur le pool.

Deuxième et dernière blague : L’optimisation du stockage, c’est comme faire un expresso : une mouture trop fine et tout se bloque ; trop grossière et ça goûte le regret.

Écritures synchrones, SLOGs et pourquoi volblocksize change la douleur

Les workloads VM génèrent souvent des écritures synchrones même si vous ne les avez pas demandées. Les bases de données appellent fsync. Les systèmes de fichiers journalisés valident. Les hyperviseurs peuvent émettre des flushes. Et si vous exportez le zvol via iSCSI/NFS (ou utilisez certains réglages hyperviseur), « sync » peut devenir le comportement par défaut.

Sur ZFS, les écritures sync doivent être rendues durables avant que le système ne les accuse réception. Sans SLOG séparé, cela signifie que les vdevs du pool principal doivent engager assez d’enregistrements intent log sur un stockage stable rapidement. Avec un bon SLOG, vous pouvez accuser réception vite et vider vers le pool plus tard lors des commits txg.

Où intervient volblocksize ? De deux manières :

  1. Combien de données une « petite » écriture devient à l’intérieur de ZFS avant d’être commitée. Les gros blocs peuvent augmenter la quantité de travail nécessaire pour représenter ce changement de façon sûre.
  2. Combien de fragmentation et de churn vous créez dans le pool principal, ce qui affecte le temps de commit txg et donc la latence sync en période de congestion.

Un schéma de douleur classique : tout va bien jusqu’à la fenêtre de sauvegarde. Le pool est occupé par de grosses lectures/écritures séquentielles, les temps de commit txg montent, et soudain la latence fsync de votre base de données passe de « correcte » à « l’application est tombée ». Si votre volblocksize est trop grand pour vos disques VM à écritures aléatoires, vous avez augmenté la quantité de travail par fsync sous pression.

Nuance importante : la performance des écritures synchrones ne dépend pas seulement de volblocksize. Elle dépend aussi de la propriété sync, de logbias, de la qualité du SLOG, de la topologie du pool (mirrors vs RAIDZ) et du fait que votre stockage soit saturé. Mais volblocksize est l’un des rares leviers qui change la granularité fondamentale du changement.

Compression, sommes de contrôle et le coût CPU caché

La compression sur les zvols n’est pas automatiquement mauvaise. Mais elle n’est pas gratuite non plus.

Les blocs plus grands compressent souvent mieux parce que les compresseurs voient plus de motifs répétitifs. Cela peut réduire les écritures physiques, ce qui compense le coût de réécriture de blocs logiques plus gros. Dans le monde réel, le mix fait la différence :

  • Disques OS : souvent bien compressibles (texte, binaires, beaucoup de zéros). La compression peut aider, et le choix de taille de bloc peut être moins pénalisant.
  • Bases de données avec pages déjà compressées : peuvent mal se compresser. Alors les blocs plus grands signifient juste plus d’octets réécrits sans économie.
  • Invités chiffrés : si l’invité chiffre le système de fichiers, la compression peut devenir inefficace au niveau ZFS. Ne comptez pas sur la compression pour vous sauver d’une mauvaise taille de bloc lorsque les données sont à haute entropie.

Le checksumming et (optionnellement) le chiffrement augmentent aussi avec le « nombre de blocs » et les « octets traités ». Des blocs trop petits peuvent devenir gourmands en CPU à fort IOPS ; des blocs trop grands peuvent devenir coûteux en latence sur des workloads d’écriture aléatoire. Vous cherchez le coude dans la courbe pour votre environnement.

ashift, tailles de secteurs et alignement

Si volblocksize est l’unité logique de réécriture du zvol, ashift est l’hypothèse d’alignement physique du pool. La plupart des pools modernes devraient être au moins ashift=12 (4K). Certains environnements préfèrent ashift=13 (8K) pour certains périphériques.

Le désalignement est le tueur silencieux : si ZFS pense que le secteur du périphérique est plus petit que la réalité, il peut faire des lectures-modifications-écritures au niveau du disque. C’est le type de bogue de performance qui ressemble à des « pics de latence aléatoire » et survit des mois de réunions.

Règle empirique qui vous trahit rarement : assurez-vous que ashift du pool est correct lors de sa création. Vous ne pouvez pas le changer plus tard sans reconstruire. Ensuite choisissez volblocksize comme multiple de la taille de secteur (4K, 8K, 16K…). La plupart des déploiements traitent 4K ou 8K comme la base sûre pour les disques VM qui font des écritures aléatoires.

Choisir volblocksize pour de vrais workloads VM

Parlons choix, pas d’idéologie.

Points de départ courants

  • Disques OS généralistes pour VM : volblocksize=8K est un compromis courant : pas trop coûteux en métadonnées, pas trop sujet à l’amplification. 4K peut être excellent pour des workloads sensibles à la latence et mixtes, mais peut coûter plus en métadonnées et CPU à grande échelle.
  • Disques VM pour bases de données (fortes écritures aléatoires) : commencez à 8K ou 16K selon la taille de page DB et les I/O observées. Si vous ne savez pas, 8K est souvent un défaut plus sûr que 128K.
  • Volumes à forte séquentialité (cibles de sauvegarde dans des VM, média, grands objets) : 64K ou 128K peuvent être sensés si le workload est vraiment séquentiel et n’effectue pas beaucoup de petites réécritures aléatoires.

Voici la partie que beaucoup sautent : vous ne choisissez pas volblocksize par rapport à ce que « ZFS aime », vous le choisissez par rapport à ce que votre invité fait réellement. Si votre invité effectue des écritures aléatoires 4K toute la journée, lui donner une unité de réécriture de 128K revient à s’abonner à du travail et de la variance inutiles.

Mirrors vs RAIDZ changent l’enjeu

Sur des mirrors, les écritures aléatoires sont généralement plus tolérantes. Sur RAIDZ, les petites écritures aléatoires sont plus coûteuses en raison de la parité et des patterns lecture-modification-écriture au niveau vdev. Un volblocksize trop petit sur RAIDZ peut être pénalisant. Un volblocksize trop grand peut être pénalisant d’une autre manière (amplification). La valeur « correcte » est plus sensible sur RAIDZ.

Si vous exécutez du stockage VM sur RAIDZ et que vous tenez à la latence, vous jouez déjà en mode difficile. Ça peut fonctionner, mais le tuning et la marge de capacité doivent être disciplinés et ennuyeusement rigoureux.

Trois mini-histoires du monde de l’entreprise

1) Incident causé par une mauvaise hypothèse : « ZFS s’en occupera »

Ils migraient une plateforme SaaS interne chargée d’un SAN legacy vers un cluster de virtualisation sur ZFS. L’équipe fit le sensé : vdevs en miroir, RAM décente, SSDs et un SLOG séparé parce que la base de données fsyncait agressivement. Le pilote avait l’air bien. Le basculement fut planifié, le risque évalué, le ticket approuvé, le café prêt.

L’hypothèse était simple : « ZFS est intelligent ; il s’adaptera. » Les zvols furent créés avec un volblocksize large parce que quelqu’un se souvenait que ZFS aime les gros blocs pour le débit, et la diapositive du vendeur montrait un graphique récompensant les performances séquentielles. Personne ne cartographia le profil I/O réel de la VM à la taille de bloc.

L’incident n’arriva pas tout de suite. Il attendit que le système tourne assez longtemps pour accumuler snapshots, churn et vraie fragmentation. Puis, pendant une fenêtre de batch routinière, la latence de commit de la base de données commença à osciller. Les threads applicatifs s’empilèrent. Les retries se multiplièrent. Le pool n’était pas « down », il était simplement lent d’une façon qui rend tout le reste cassé.

On-call regarda le CPU (ok), le réseau (ok), le SLOG (ok), et vit encore des pics de latence d’écritures sync. La percée survint quand quelqu’un exécuta rapidement zfs get volblocksize et le comparât aux tailles d’I/O de l’invité via iostat et un court fio. Les invités faisaient beaucoup d’écritures 8K et 16K ; l’unité de réécriture du zvol était beaucoup plus grande. Sous charge, chaque petit commit tirait un train de réécriture de bloc plus grand derrière lui.

La correction n’était pas un interrupteur. Ils durent créer de nouveaux zvols avec un volblocksize plus raisonnable et migrer les disques en live quand possible, hors ligne quand ce n’était pas possible. Ce fut une longue nuit, mais utile : ZFS est intelligent, oui. Il n’est pas clairvoyant.

2) Une optimisation qui se retourna contre eux : « On passe tout en 4K »

Autre endroit, autre problème. Ils avaient une flotte sensible à la latence de petites VM (workers CI, agents de build, petites bases de test) et voulaient plus d’IOPS. Quelqu’un lut que 4K volblocksize améliore les écritures aléatoires, et ils décidèrent d’uniformiser sur ce réglage. Pas d’exceptions. La cohérence rassure, surtout en entreprise où chaque exception devient une réunion.

La première semaine fut excellente. Les benchmarks s’améliorèrent. Les tableaux de bord souriaient. On se félicitait discrètement sur Slack avec l’emoji que l’on utilise quand on ne veut pas que la direction remarque qu’on a changé quelque chose d’important.

Puis la réalité arriva sous forme de pression CPU et mémoire. Les nœuds de stockage passèrent plus de temps sur les métadonnées et le checksumming. L’ARC churn augmenta car le working set contenait beaucoup plus de blocs et de pointeurs. Certains hôtes atteignirent un plafond où le pool n’était pas saturé en bande passante, mais les complétions d’I/O ralentissaient parce que le chemin logiciel faisait plus par opération.

Ce ne fut pas un échec catastrophique ; ce fut un échec bureaucratique. Les développeurs se plaignirent que les builds étaient « parfois lents ». Les jobs de tests devinrent instables. Les SREs passèrent des jours à chasser des « voisins bruyants » fantômes avant de remarquer le fil commun : un bloc uniforme 4K sur des workloads comprenant beaucoup d’I/O séquentiel et de gros fichiers.

La politique finale devint plus mature : 4K pour des zvols prouvés charges aléatoires ; 8K ou 16K pour disques OS généralistes ; plus grand seulement pour volumes séquentiels avérés. La leçon n’était pas « 4K est mauvais ». La leçon était que le dogme coûte cher.

3) La pratique ennuyeuse mais correcte qui sauva la situation : « Profils standards + voie de migration »

Cette équipe avait déjà été brûlée, alors elle fit quelque chose d’impopulaire : elle créa un petit ensemble de profils de stockage et en fit les valeurs par défaut. Chaque profil avait un volblocksize documenté, un réglage de compression, des attentes sur sync/logbias et une courte justification. Rien d’exotique. Juste des décisions écrites avant l’incident.

Lorsqu’une nouvelle VM était provisionnée, le demandeur choisissait « général », « db-heavy » ou « séquentiel ». S’il ne choisissait pas, ça tombait sur général. Cela évita beaucoup d’inadéquations accidentelles.

Mais le vrai gain fut la voie de migration. Ils acceptèrent que volblocksize est effectivement immutable pour les données existantes et construisirent une habileté opérationnelle pour déplacer les disques : créer un nouveau zvol avec le volblocksize désiré, répliquer ou copier bloc par bloc, basculer, valider, supprimer l’ancien. Ils s’entraînèrent en heures normales, pas en improvisation désespérée pendant une crise.

Ainsi, quand une VM appliance vendor arriva avec un workload sync lourd et commença à faire souffrir le cluster, la correction n’était pas une guerre de salle. Ce fut une migration planifiée utilisant un profil « latence-sync » connu et une coupure contrôlée. Le jour fut sauvé par l’ennui : une procédure répétable et la discipline de l’appliquer.

Tâches pratiques : commandes, sorties et interprétation

Voici les tâches que je lance réellement quand quelqu’un dit « le stockage VM est lent » et que l’unique indice est une capture d’écran d’un graphique rouge. Les commandes supposent un hôte Linux avec les utilitaires ZFS. Adaptez les noms de pool et de zvol au besoin.

Task 1: Identifier si le disque VM est un zvol et trouver son nom ZFS

cr0x@server:~$ ls -l /dev/zvol/tank/vm-101-disk-0
lrwxrwxrwx 1 root root 13 Dec 24 10:20 /dev/zvol/tank/vm-101-disk-0 -> ../../zd0

Interprétation : Vous avez affaire à un zvol. Bien — volblocksize s’applique. Le périphérique backing ici est /dev/zd0.

Task 2: Vérifier volblocksize (et quelques propriétés liées)

cr0x@server:~$ zfs get -H -o property,value volblocksize,compression,logbias,sync,refreservation tank/vm-101-disk-0
volblocksize	128K
compression	lz4
logbias	latency
sync	standard
refreservation	none

Interprétation : 128K est grand pour un disque VM général sauf si vous savez qu’il est surtout séquentiel. La compression est activée (lz4), ce qui peut aider ou pas. sync=standard signifie que les flushs de l’invité comptent.

Task 3: Confirmer l’ashift du pool (base d’alignement)

cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
52:        vdev_tree:
76:            ashift: 12
104:            ashift: 12

Interprétation : ashift=12 (secteurs 4K) est une base saine. Si vous voyez ashift=9 sur des disques modernes, vous avez probablement trouvé un problème fondamental.

Task 4: Surveiller rapidement la taille des I/O côté hôte et la latence

cr0x@server:~$ iostat -x 1 5 /dev/zd0
Linux 6.8.0 (server) 	12/24/2025 	_x86_64_	(32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           6.12    0.00    3.45    8.90    0.00   81.53

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz  aqu-sz  %util
zd0              5.00     80.0     0.00   0.00    4.20    16.0   220.00   3520.0    10.00   4.35   18.50    16.0    3.20  92.00

Interprétation : Taille moyenne de requête d’écriture ~16K, pas 128K. Si volblocksize est 128K, attendez-vous à un churn supplémentaire sur les petites écritures, surtout sous pression sync. w_await à ~18ms suggère déjà une douleur en latence.

Task 5: Observer la latence côté ZFS avec zpool iostat par vdev

cr0x@server:~$ zpool iostat -v tank 1 3
               capacity     operations     bandwidth
pool         alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank         4.2T  3.1T     80    900   12.0M  55.0M
  mirror     2.1T  1.6T     40    450    6.0M  27.5M
    nvme0n1      -      -     20    230    3.0M  14.0M
    nvme1n1      -      -     20    220    3.0M  13.5M
  mirror     2.1T  1.6T     40    450    6.0M  27.5M
    nvme2n1      -      -     20    225    3.0M  13.8M
    nvme3n1      -      -     20    225    3.0M  13.7M

Interprétation : Le débit n’est pas énorme, mais les opérations le sont. C’est typique d’un I/O aléatoire en petits blocs. Si vous voyez beaucoup d’ops et des Mo/s modérés, les choix de taille de bloc comptent.

Task 6: Vérifier la pression txg sync (les commits prennent-ils trop de temps?)

cr0x@server:~$ cat /proc/spl/kstat/zfs/txgs
1 0x01 137 14592 0 0 0 0 0 0 0
2 0x01 137 14593 0 0 0 0 0 0 0

Interprétation : Les champs exacts varient selon la plateforme, mais le but est de repérer des retards txg et si vous accumulez des données dirty plus vite que vous ne pouvez les sync. Si les temps de sync txg augmentent pendant vos pics de latence, vous êtes en territoire « pression de commit ».

Task 7: Vérifier si un zvol utilise efficacement un log séparé (SLOG)

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
config:

	NAME        STATE     READ WRITE CKSUM
	tank        ONLINE       0     0     0
	  mirror    ONLINE       0     0     0
	    nvme0n1 ONLINE       0     0     0
	    nvme1n1 ONLINE       0     0     0
	  mirror    ONLINE       0     0     0
	    nvme2n1 ONLINE       0     0     0
	    nvme3n1 ONLINE       0     0     0
	log
	  mirror    ONLINE       0     0     0
	    nvme4n1 ONLINE       0     0     0
	    nvme5n1 ONLINE       0     0     0

errors: No known data errors

Interprétation : Il y a un SLOG en miroir. Parfait. Mais rappelez-vous : un SLOG aide seulement pour les écritures synchrones. Si votre workload est asynchrone ou si le goulot est ailleurs, il ne vous sauvera pas.

Task 8: Vérifier si l’invité force le sync (ou si vous l’avez forcé par erreur)

cr0x@server:~$ zfs get -H -o property,value sync tank/vm-101-disk-0
sync	standard

Interprétation : standard signifie que ZFS respecte les sémantiques O_SYNC/fsync/flush. Si quelqu’un a mis sync=always, la latence augmentera dramatiquement pour beaucoup de workloads VM. Si quelqu’un a mis sync=disabled, la latence sera superbe jusqu’à ce que vous perdiez l’alimentation et que vous viviez une journée très éducative.

Task 9: Créer un zvol test avec un volblocksize candidat (ne changez pas la prod aveuglément)

cr0x@server:~$ sudo zfs create -V 50G -b 8K -o compression=lz4 tank/test-vm-disk-8k
cr0x@server:~$ zfs get -H -o property,value volblocksize tank/test-vm-disk-8k
volblocksize	8K

Interprétation : Utilisez un zvol test pour bencher avec fio depuis une VM ou l’hôte. Choisir avec des données bat les choix basés sur l’opinion.

Task 10: Lancer un test fio d’écriture aléatoire contre le zvol (côté hôte)

cr0x@server:~$ sudo fio --name=randwrite --filename=/dev/zvol/tank/test-vm-disk-8k \
  --direct=1 --ioengine=libaio --rw=randwrite --bs=8k --iodepth=32 --numjobs=1 --time_based --runtime=20 --group_reporting
randwrite: (g=0): rw=randwrite, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=libaio, iodepth=32
fio-3.36
randwrite: write: IOPS=42.1k, BW=329MiB/s (345MB/s)(6580MiB/20001msec)
  lat (usec): min=70, max=22450, avg=610.42, stdev=380.12
  clat percentiles (usec):
   |  1.00th=[  150], 10.00th=[  250], 50.00th=[  520], 90.00th=[  980], 99.00th=[ 1800], 99.90th=[ 4200]
  cpu          : usr=3.20%, sys=11.40%, ctx=820k, majf=0, minf=12

Interprétation : Regardez les percentiles, pas seulement les IOPS. Si votre 99.9e percentile est moche, votre application finira par se plaindre.

Task 11: Comparer avec un zvol test volblocksize plus grand (même job fio)

cr0x@server:~$ sudo zfs create -V 50G -b 128K -o compression=lz4 tank/test-vm-disk-128k
cr0x@server:~$ sudo fio --name=randwrite --filename=/dev/zvol/tank/test-vm-disk-128k \
  --direct=1 --ioengine=libaio --rw=randwrite --bs=8k --iodepth=32 --numjobs=1 --time_based --runtime=20 --group_reporting
randwrite: write: IOPS=18.7k, BW=146MiB/s (153MB/s)(2920MiB/20001msec)
  lat (usec): min=95, max=78410, avg=1380.12, stdev=1220.55
  clat percentiles (usec):
   |  1.00th=[  220], 10.00th=[  420], 50.00th=[ 1100], 90.00th=[ 2600], 99.00th=[ 6800], 99.90th=[ 32000]

Interprétation : Même workload 8K, bien pire en queue de latence et moins d’IOPS. Voilà la taxe d’amplification qui se manifeste.

Task 12: Vérifier l’utilisation logique/physique d’un zvol (effets compression et padding)

cr0x@server:~$ zfs list -o name,volsize,used,refer,compressratio tank/test-vm-disk-8k tank/test-vm-disk-128k
NAME                   VOLSIZE  USED  REFER  COMPRESSRATIO
tank/test-vm-disk-8k      50G  3.2G  3.2G             1.45x
tank/test-vm-disk-128k    50G  6.8G  6.8G             1.10x

Interprétation : Les gros blocs n’ont pas compressé mieux ici ; ils ont utilisé plus d’espace. Les workloads réels varient, mais c’est exactement pourquoi vous mesurez.

Task 13: Confirmer si un zvol est sparse (thin) et si refreservation le masque

cr0x@server:~$ zfs get -H -o property,value volsize,volmode,refreservation tank/vm-101-disk-0
volsize	500G
volmode	default
refreservation	none

Interprétation : Le comportement de thin provisioning dépend de l’environnement. Refreservation peut éviter les surprises « out of space » au prix d’une efficacité en capacité.

Task 14: Mesurer la distribution réelle des tailles d’I/O invité (échantillonnage côté hôte)

cr0x@server:~$ sudo blktrace -d /dev/zd0 -w 10 -o - | blkparse -i - | head -n 12
  8,16    3        1     0.000000000  1234  Q  WS 12345678 + 16 [qemu-kvm]
  8,16    3        2     0.000010000  1234  G  WS 12345678 + 16 [qemu-kvm]
  8,16    3        3     0.000020000  1234  P  WS 12345678 + 16 [qemu-kvm]
  8,16    3        4     0.000080000  1234  C  WS 12345678 + 16 [0]
  8,16    3        5     0.000100000  1234  Q  WS 12345710 + 32 [qemu-kvm]
  8,16    3        6     0.000120000  1234  C  WS 12345710 + 32 [0]

Interprétation : Les « + 16 » secteurs ici correspondent à 8K (16 × 512B secteurs) dans un schéma d’affichage en secteurs 512. Cela vous dit ce que la VM émet réellement, pas ce que vous souhaiteriez qu’elle émette.

Task 15: Planifier une migration sûre vers un nouveau volblocksize (create + copy)

cr0x@server:~$ sudo zfs create -V 500G -b 8K -o compression=lz4 tank/vm-101-disk-0-new
cr0x@server:~$ sudo dd if=/dev/zvol/tank/vm-101-disk-0 of=/dev/zvol/tank/vm-101-disk-0-new bs=16M status=progress conv=fsync
104857600000 bytes (105 GB, 98 GiB) copied, 310 s, 338 MB/s

Interprétation : C’est l’instrument brutal. Il nécessite une fenêtre d’arrêt (ou au moins une stratégie snapshot cohérente côté hyperviseur). Mais c’est fiable et garde la sémantique du périphérique bloc simple.

Mode opératoire de diagnostic rapide

Quand la latence est élevée et que les gens crient, vous avez besoin d’une courte séquence qui réduit vite l’espace de recherche. Voici la mienne pour des VMs backed par zvol ZFS.

Première étape : le pool est-il réellement saturé ou juste « lent » ?

  • Exécutez zpool iostat -v 1 et regardez ops et bande passante.
  • Vérifiez si un vdev est plus chaud que les autres (un déséquilibre peut ressembler à une « latence aléatoire »).
  • Cherchez une forte utilisation avec un débit modeste : cela indique souvent de petits I/O aléatoires ou de la pression sync.

Deuxième étape : est-ce une douleur d’écriture sync ?

  • Vérifiez zfs get sync,logbias sur le zvol.
  • Vérifiez si un SLOG existe et est sain (zpool status).
  • Corrélez les pics de latence avec la pression de commit txg (kstats spécifiques plateforme ; sur Linux, les kstats SPL et les logs système aident).

Troisième étape : volblocksize correspond-il au profil I/O ?

  • Vérifiez zfs get volblocksize pour le zvol affecté.
  • Échantillonnez les tailles I/O réelles avec iostat -x sur le périphérique zvol et, si besoin, blktrace.
  • Si les écritures invitées sont majoritairement 4K–16K et que volblocksize est 64K–128K, supposez une amplification jusqu’à preuve du contraire.

Quatrième étape : êtes-vous contraint par la capacité/fragmentation ?

  • Vérifiez le remplissage du pool : zfs list / zpool list. Une haute utilisation augmente la fragmentation et ralentit les allocations.
  • Vérifiez le travail en arrière-plan : scrub/resilver dans zpool status.
  • Le nombre de snapshots : beaucoup de snapshots peut augmenter les métadonnées et ralentir certaines opérations.

Cinquième étape : valider avec un micro-benchmark contrôlé

  • Créez un zvol test avec des valeurs de volblocksize candidates.
  • Lancez fio avec des tailles de bloc correspondant au workload VM et mesurez la latence en queue.
  • Ne faites pas de bench sur un pool déjà en feu à moins d’aimer les tickets auto-infligés.

Erreurs fréquentes, symptômes et corrections

Mistake 1: Utiliser volblocksize 128K pour des disques VM à écritures aléatoires

Symptômes : Excellent débit séquentiel, mais les bases de données expirent sous charge mixte ; latence 99e/99.9e élevée ; IOPS inférieurs aux attentes ; « tout va bien jusqu’aux sauvegardes ».

Correction : Migrer vers un nouveau zvol avec volblocksize=8K ou 16K (selon le workload). Valider avec fio et métriques applicatives réelles. Ne vous attendez pas à ce qu’un changement de propriété in-place re-bloque les données existantes.

Mistake 2: Mettre volblocksize 4K partout sans vérifier le coût CPU/métadonnées

Symptômes : Les IOPS aléatoires s’améliorent, mais les hôtes de stockage montrent plus d’USAGE CPU système ; churn ARC ; variabilité sur les workloads séquentiels ; « c’est rapide mais incohérent ».

Correction : Utiliser des profils. Garder 4K pour les disques vraiment orientés écriture aléatoire ; utiliser 8K/16K pour usage général ; utiliser plus grand uniquement pour volumes séquentiels connus.

Mistake 3: Confondre le tuning dataset (recordsize) avec le tuning zvol (volblocksize)

Symptômes : Quelqu’un définit recordsize=16K sur le dataset parent et s’attend à un changement de performance sur un disque VM. Rien ne change ; la faute circule.

Correction : Les zvols ne sont pas des fichiers dans un dataset de la même manière. Utilisez zfs get volblocksize sur le zvol lui-même.

Mistake 4: Utiliser sync=disabled pour « résoudre la latence »

Symptômes : Les graphiques de latence ont l’air magnifiques ; la direction est contente ; puis un arrêt non propre arrive et la base de données doit être réparée ou perd des transactions récentes.

Correction : Gardez sync=standard pour la cohérence. Si vous avez besoin de performance sync, corrigez le SLOG, la topologie du pool et l’alignement du volblocksize selon le workload.

Mistake 5: Ignorer le remplissage et la fragmentation du pool

Symptômes : Le même workload devient plus lent au fil des mois ; la latence d’écriture augmente progressivement ; les allocations deviennent coûteuses ; « ZFS était rapide au début ».

Correction : Maintenez une marge de capacité. Planifiez les expansions avant la panique. Traitez les snapshots et la rétention comme un budget, pas comme un hobby.

Mistake 6: Benchmarker avec la mauvaise taille de bloc et appeler ça une « preuve »

Symptômes : fio montre des Mo/s énormes avec des écritures séquentielles 1M, mais la production est lente ; les disputes s’ensuivent.

Correction : Benchmarquez avec les tailles de bloc et les sémantiques sync que votre workload VM utilise. Incluez les percentiles de latence et des patterns mixtes lecture/écriture.

Checklists / plan pas à pas

Checklist A: Provisionnement d’un nouveau disque VM (zvol-backed)

  1. Classer le workload : général OS, DB-heavy écritures aléatoires, ou séquentiel-heavy.
  2. Choisir un profil :
    • Général : volblocksize=8K (souvent) avec compression=lz4
    • DB-heavy : 8K ou 16K ; testez si possible
    • Séquentiel-heavy : 64K ou 128K si vraiment séquentiel
  3. Créer explicitement le zvol (ne vous fiez pas à des valeurs par défaut non auditées).
  4. Confirmer les propriétés avec zfs get.
  5. Documenter le choix dans les métadonnées/ticket de la VM pour que le futur vous n’ait pas à l’archéologiser.

Checklist B: Quand vous suspectez que volblocksize est erroné

  1. Confirmer le volblocksize actuel du zvol.
  2. Mesurer les tailles I/O réelles (iostat -x, optionnellement blktrace).
  3. Créer un zvol test avec un volblocksize candidat plus petit/plus grand.
  4. Benchmarker avec fio en utilisant des tailles de bloc correspondant au workload.
  5. Faire un plan de migration :
    • Fenêtre d’indisponibilité ou outil de migration live
    • Méthode de copie bloc (dd, réplication côté hyperviseur, ou copie au niveau invité)
    • Étapes de validation (check filesystem, vérifications applicatives, tests de performance)
  6. Basculez, surveillez la latence en queue, puis décommissionnez l’ancien zvol.

Checklist C: « Garde-fous ennuyeux » pour le stockage VM sur ZFS

  1. Garder l’utilisation du pool raisonnable ; éviter de vivre près du plein.
  2. Planifier les scrubs, mais ne pas les chevaucher avec vos fenêtres d’écriture les plus lourdes sauf si vous voulez tester la résilience.
  3. Suivre les percentiles de latence, pas seulement les moyennes.
  4. Standardiser un petit ensemble de profils volblocksize et les appliquer.
  5. Pratiquer la procédure de migration avant d’en avoir besoin.

FAQ

1) Quelle est la valeur par défaut de volblocksize, et puis-je lui faire confiance ?

Les valeurs par défaut varient selon la plateforme et la version, et elles peuvent ne pas être alignées sur votre workload VM. Traitez les valeurs par défaut comme « sûres pour quelqu’un », pas « optimales pour vous ». Vérifiez toujours ce que votre environnement utilise et bencher par rapport à votre profil I/O.

2) Puis-je changer volblocksize sur un zvol existant sans migration ?

Vous pouvez souvent changer la valeur de propriété, mais cela ne réécrira pas rétroactivement les blocs existants dans la nouvelle taille de façon propre et instantanée. En pratique, si vous avez besoin du comportement performant d’un volblocksize différent, planifiez la création d’un nouveau zvol et la migration des données.

3) 4K est-il toujours le meilleur choix pour les disques VM ?

Non. 4K peut être excellent pour des workloads fortement écriture aléatoire et pour une latence prévisible, mais il augmente le coût des métadonnées et le travail CPU. Pour des disques VM polyvalents, 8K ou 16K est souvent un meilleur compromis.

4) Comment volblocksize se rapporte-t-il à la taille de bloc du système de fichiers invité ?

Ce sont des couches indépendantes. La taille de bloc invitée influence les I/O qu’elle émet. Le volblocksize du zvol influence la façon dont ZFS stocke et réécrit ces écritures. Quand elles sont mal appairées (par ex. invité écrit des pages 8K, zvol utilise 128K), vous pouvez créer de l’amplification et de la variance de latence.

5) Si j’ai un pool NVMe rapide, volblocksize compte-t-il encore ?

Oui. NVMe rend la latence suffisamment basse pour que le surcoût logiciel et l’amplification deviennent visibles. Vous pouvez tout à fait gaspiller les performances NVMe avec un mauvais choix de taille de bloc, surtout pour des workloads sync ou écriture aléatoire.

6) La compression change-t-elle la recommandation de volblocksize ?

Elle peut. La compression peut réduire les écritures physiques et parfois adoucir le coût des gros blocs, mais c’est dépendant du workload. Les données chiffrées ou déjà compressées n’en bénéficieront souvent pas. Mesurez compressratio et percentiles de latence sous charge réaliste.

7) RAIDZ — cela me pousse-t-il vers des volblocksize plus grands ou plus petits ?

RAIDZ rend les petites écritures aléatoires plus coûteuses à cause de la parité et du comportement lecture-modification-écriture. Ça ne signifie pas « toujours utiliser des blocs énormes », mais cela signifie que vous devez être particulièrement prudent et tester. Les mirrors sont généralement plus tolérants pour l’I/O aléatoire VM.

8) Un SLOG peut-il corriger un mauvais choix de volblocksize ?

Un bon SLOG peut améliorer dramatiquement la latence des écritures synchrones, mais il n’éliminera pas l’amplification ni les effets de fragmentation d’un volblocksize inadapté. Pensez au SLOG comme « m’aide à accuser réception rapidement » plutôt que « corrige les écritures inefficaces ».

9) Mon hyperviseur utilise QCOW2 ou un autre format d’image — volblocksize compte-t-il toujours ?

Oui, mais la pile devient plus complexe. Les formats d’image peuvent introduire leur propre granularité d’allocation et surcoût métadonnées, qui peuvent se cumuler avec le comportement ZFS. Si vous tenez à une latence prévisible, les volumes bruts sont plus simples à raisonner, et volblocksize devient un levier plus clair.

10) Quelle est la valeur volblocksize la plus sûre si je dois standardiser ?

Si vous devez choisir une seule valeur pour des disques VM généraux sans connaître les workloads, 8K est un choix commun « le moins mauvais » dans de nombreux environnements. Mais la réponse honnête est : standardisez des profils, pas une valeur unique.

Conclusion

volblocksize n’est pas glamour, et c’est précisément pour ça qu’il mord. C’est une décision bas-niveau qui façonne silencieusement combien de travail votre pile de stockage fait par écriture VM, la stabilité de votre latence sous pression, et la vitesse à laquelle votre « pool rapide » devient une excuse.

L’approche opérationnelle mature est aussi la plus simple : mesurez les tailles I/O réelles, choisissez un petit ensemble de profils sensés, benchmarquez la latence en queue, et acceptez que changer volblocksize soit généralement un projet de migration — pas un interrupteur. Faites cela, et vous obtiendrez ce que ZFS sait faire de mieux : cohérence prédictible, performances robustes, et un comportement de stockage que vous pouvez expliquer à un autre humain pendant un incident.

← Précédent
Le northbridge disparu : comment l’intégration a reconfiguré les PC
Suivant →
Proxmox « dpkg was interrupted » : réparer les paquets cassés sans réinstaller

Laisser un commentaire