ZFS iSCSI : configurer un ZVOL sans saccades sous charge

Cet article vous a aidé ?

Le pire type de problème de stockage est celui qui « fonctionne la plupart du temps ». La VM démarre. La base de données passe ses tests de fumée.
Puis quelqu’un lance une vraie charge à 10:14 un mardi et votre graphique de latence iSCSI devient une œuvre d’art moderne.
Le symptôme est une saccade : des arrêts périodiques, des latences en longue traîne, et des utilisateurs qui décrivent ça comme une « lenteur » comme si c’était une vraie métrique.

Si vous exportez des ZVOLs ZFS via iSCSI, vous tenez une chaîne de couteaux aiguisés : groupes de transactions ZFS, sémantique sync,
filetage iSCSI, multipath, et cache du système invité. Si vous vous trompez de géométrie, vous passerez la semaine à blâmer « le réseau »
pendant que ZFS attend poliment que votre petit SLOG cesse de fondre.

Modèle mental : d’où viennent les saccades

Les « saccades » sous charge sont rarement un problème de débit brut. C’est de la variance. Votre I/O médiane semble correcte, mais votre centile 99,9 devient moche :
l’invité se fige, le checkpoint de la base de données ralentit, l’hyperviseur panique et réessaye, et l’équipe applicative ouvre un ticket intitulé
« Le stockage se fige aléatoirement ».

Avec des ZVOLs ZFS exportés via iSCSI, les saccades proviennent généralement d’un des goulets d’étranglement suivants :

  • Gestion des écritures synchrones : le client émet une écriture avec sémantique FUA/flush ; ZFS doit l’engager de façon sûre. Sans SLOG approprié, il y a des pics de latence.
  • Comportement des transaction groups (TXG) : ZFS regroupe les écritures et les synchronise périodiquement. Des limites de données sales mal dimensionnées ou un pool lent peuvent faire exploser le temps de sync des TXG.
  • Inadéquation de la taille de bloc : le client effectue des écritures aléatoires 4K mais votre ZVOL a volblocksize 128K, forçant une amplification read-modify-write.
  • Mise en file et backpressure : la profondeur de file de l’initiateur iSCSI, la mise en file côté target et les files de vdev ZFS peuvent provoquer un comportement en « scie » de la latence.
  • Outliers de latence niveau physique : un SSD qui se meurt, un HBA qui se comporte mal, un chemin qui clignote en multipath. ZFS vous le dira, si vous posez les bonnes questions.

L’objectif pratique : une latence prévisible sous charge soutenue, même si le débit maximum baisse un peu.
En production, l’ennuyeux et constant l’emporte.

Faits et contexte intéressants (pour arrêter de répéter l’histoire)

  1. ZFS est né chez Sun au milieu des années 2000 comme une pile de stockage bout-en-bout : checksums, stockage poolé, snapshots et copy-on-write. Ce n’est pas « un système de fichiers sur RAID » autant que « du stockage qui refuse de mentir ».
  2. Le copy-on-write rend les snapshots peu coûteux, mais signifie aussi que les réécritures deviennent de nouvelles écritures, et la fragmentation existe. Les charges en bloc qui effectuent beaucoup de réécritures aléatoires peuvent « vieillir » un pool plus vite que prévu.
  3. Les ZVOLs ne sont pas des fichiers. Un ZVOL est un périphérique bloc géré par ZFS. Cette distinction compte : les ZVOLs n’ont pas de recordsize ; ils ont volblocksize, et il est plus difficile à modifier ensuite.
  4. iSCSI date des années 1990 comme « SCSI sur TCP ». Il a gagné parce qu’Ethernet a gagné, pas parce que c’est charmant. C’est aussi la raison pour laquelle « performance de stockage » peut être affectée par de petits comportements TCP.
  5. Le ZFS Intent Log (ZIL) existe pour rendre les écritures synchrones durables. Le périphérique de log séparé (SLOG) n’est pas un cache d’écriture ; c’est un point d’atterrissage à faible latence pour l’intention d’écriture synchrone.
  6. Les premières recommandations ZFS étaient façonnées par les disques spinning et les grosses écritures séquentielles. Les pools modernes SSD/NVMe déplacent le goulot d’étranglement du temps de seek vers l’amplification de latence et le comportement de mise en file.
  7. Les secteurs 4K ont tout changé (Advanced Format). Si vous alignez mal ashift, vous pouvez transformer involontairement une écriture 4K en un read-modify-write au niveau du disque. C’est une taxe que vous payez pour toujours.
  8. La compression faisait peur sur les charges bloc. LZ4 moderne est suffisamment rapide pour qu’activer la compression réduise souvent les I/O et améliore la latence, surtout pour les images VM et les bases de données avec motifs répétitifs.
  9. Le multipath servait à la redondance mais sert aujourd’hui aussi à la performance. Si vous le configurez mal, vous pouvez vous équilibrer vers la perte de paquets et les timeouts.

Choix d’architecture qui comptent (et ceux qui n’en valent pas la peine)

Choisir ZVOL vs dataset comme si vous le pensiez vraiment

Si le client attend un périphérique bloc (datastore hyperviseur, volume Windows, base de données clusterisée qui veut des disques bruts), utilisez un ZVOL.
Si vous pouvez présenter des fichiers (NFS, SMB) et que vous contrôlez les patterns d’I/O de l’application, un dataset est souvent plus simple à régler et à observer.
Mais cet article porte sur les ZVOLs, donc on reste sur ce front.

Volblocksize : la décision de « format » que vous ne refaites pas facilement

volblocksize est la taille de bloc interne que ZFS utilise pour le ZVOL. Elle influence fortement l’amplification d’écriture et le churn des métadonnées.
Les blocs plus petits conviennent mieux aux I/O aléatoires. Les blocs plus grands réduisent l’overhead des métadonnées pour les charges séquentielles mais pénalisent les petites écritures aléatoires.

  • Disques système/boot de VM : 8K ou 16K est un bon point de départ. 8K l’emporte souvent pour les lectures/écritures aléatoires mixtes. 16K peut réduire l’overhead si la charge n’est pas trop bavarde.
  • Bases de données (OLTP-ish) : 8K si la taille de page DB est 8K (fréquent). Alignez sur la réalité. Ne combattez pas la physique.
  • Grand séquentiel (cibles de sauvegarde, médias) : 64K–128K peuvent convenir, mais n’exportez pas ça comme datastore VM général à moins d’aimer les saccades mystères.

Vous pouvez changer volblocksize seulement en recréant le ZVOL et en migrant les données. Ce n’est pas un projet de week-end en production.

Ashift : décidez une fois, subissez toujours

ashift est l’exposant de la taille de secteur du pool (par ex., 12 signifie 2^12 = 4096 octets). Si vos dispositifs physiques sont 4K (ils le sont), mettez ashift=12.
Pour beaucoup de SSD, 12 est correct ; pour certains dispositifs 8K, vous pourriez vouloir 13.
L’essentiel : ne laissez pas « auto-detect » se tromper et ne le découvrir qu’après que votre pool contient des données.

Écritures synchrones : comprenez ce que le client vous demande de garantir

Les écritures synchrones sont la partie où le système de stockage promet que les données ne disparaîtront pas en cas de coupure de courant.
Sur iSCSI, les invités et les systèmes de fichiers émettent des flushs et des écritures FUA plus souvent qu’on ne le pense—surtout dans les stacks virtualisés.

ZFS peut gérer les écritures synchrones sans SLOG en écrivant sur le pool principal, mais la latence suivra la latence d’écriture stable du vdev le plus lent.
Si vous tenez à la latence en queue, vous voulez probablement un SLOG dédié sur SSD/NVMe bas-latence avec protection contre la perte de puissance (PLP).

SLOG : pas « plus de cache », mais « moins d’attente »

Un SLOG accélère l’accusé de réception des écritures synchrones en persistant rapidement l’intention. Plus tard, ZFS flushera le transaction group vers le pool principal.
Si votre charge sync est lourde, le SLOG doit :

  • Faible latence sous écritures soutenues (pas seulement « rapide en séquentiel »).
  • Protection contre la perte de puissance ou vous pouvez perdre des écritures synchrones reconnues.
  • Endurance suffisante pour le taux d’écriture (les écritures ZIL peuvent être brutales).

Blague n°1 : Acheter un « NVMe gaming » pour SLOG, c’est comme engager un sprinteur comme veilleur de nuit—rapide oui, mais endormi quand le courant coupe.

Compression : activez-la sauf raison forte contraire

Utilisez compression=lz4 pour la plupart des charges ZVOL. Même si les données ne compressent pas beaucoup, le surcoût est faible sur les CPU modernes.
Le gain est souvent moins d’octets écrits sur disque et moins d’I/O, ce qui peut réduire la latence.

Dedup : n’en faites pas

La dedup sur ZVOLs est un classique « ça avait l’air bien sur une slide ». Elle augmente la pression mémoire et peut amplifier la latence.
À moins d’avoir modélisé, mesuré et de pouvoir payer la RAM et la complexité opérationnelle, n’en mettez pas.

vdevs spéciaux et métadonnées : utiles, mais faciles à mal utiliser

Les dispositifs d’allocation spéciale peuvent accélérer les métadonnées et les petits blocs.
Sur des systèmes à forte utilisation de ZVOLs, cela peut aider certains patterns, mais c’est aussi un nouveau domaine de fiabilité.
Si vous perdez un special vdev qui contient des métadonnées, vous pouvez perdre le pool à moins qu’il soit correctement mirroré.
Traitez-le comme du stockage central, pas comme un « SSD bonus ».

Réseau : 10/25/40/100GbE ne corrige pas la latence

iSCSI est sensible à la perte, à la pression des buffers et à l’instabilité de chemins. Un lien rapide avec micro-burst peut toujours provoquer des saccades.
Votre objectif est une latence constante, pas seulement des gros chiffres dans iperf.

Construire une cible iSCSI ZVOL qui reste fluide

Hypothèses de base

Les exemples supposent un hôte ZFS basé sur Linux exécutant OpenZFS, exportant des LUNs bloc via iSCSI en utilisant LIO (targetcli).
Le client est un initiateur Linux, mais nous signalerons les particularités Windows/ESXi.
Adaptez à votre plateforme, mais gardez les principes : alignez la géométrie, respectez le sync, et mesurez la latence là où elle est créée.

Disposition du pool : les mirrors surpassent RAIDZ pour latence d’écriture aléatoire

Pour les charges VM/bloc iSCSI, les mirrors surpassent généralement RAIDZ en latence et en cohérence d’IOPS. RAIDZ peut convenir pour des charges essentiellement séquentielles
ou majoritairement en lecture, mais les petites écritures aléatoires doivent toucher la parité et peuvent amplifier la latence pendant les rebuilds.

Si vous devez utiliser RAIDZ pour des raisons de capacité, prévoyez des écritures sync plus lentes et des latences de queue plus prononcées pendant les scrubs/resilvers.
Vous pouvez quand même faire de la production dessus ; vous n’avez juste pas le droit d’être surpris.

Créer le ZVOL avec des propriétés sensées

Un bon ensemble de départ pour un ZVOL de type datastore VM général :

  • volblocksize=8K ou 16K selon la charge
  • compression=lz4
  • sync=standard (ne pas mettre disabled pour « corriger » la latence sauf si vous aimez la perte de données)
  • logbias=latency pour les charges sync-intensives (courant pour le stockage VM)

Décidez aussi si vous voulez la provisionnement fin (thin). Les ZVOLs peuvent être sparse (thin) ou thick. Le thin est pratique. Le thin permet aussi aux gens
de surpromettre jusqu’à ce que le pool atteigne 100% et que tout devienne un incident au ralenti.

Exportez-le via iSCSI avec une mise en file prévisible

Avec LIO, vous ferez typiquement :

  1. Créer un backstore pointant sur le ZVOL
  2. Créer un IQN de cible iSCSI et un portail
  3. Créer un mapping de LUN et des ACL
  4. Optionnellement régler sessions, recovery d’erreur et timeouts

Là où se cachent les saccades : un trop grand nombre d’I/O en attente peut faire paraître la latence comme un « gel périodique » quand la file se vide.
Un sous-réglage peut plafonner le débit, mais un sur-réglage provoque des pics de latence en queue et des timeouts. Vous voulez suffisamment de profondeur de file pour saturer les disques,
pas assez pour créer un embouteillage I/O.

Multipath côté client : ennuyeux, spécifique et obligatoire en production

Le multipath résout deux problèmes : basculement et distribution de charge. Il crée aussi un tout nouveau mode de défaillance : path flapping, où le client
bascule sans cesse entre les chemins et votre stockage semble « perdre des paquets » alors qu’en réalité il est yo-yo par la politique.

Utilisez deux chemins physiquement séparés si vous revendiquez la redondance. NICs séparées, switches séparés si possible, tout séparé. Sinon, c’est du théâtre.

Une citation qui appartient vraiment aux opérations

« L’espoir n’est pas une stratégie. » — idée paraphrasée souvent attribuée aux opérateurs et ingénieurs en fiabilité

Tâches pratiques : commandes, sorties et décisions

Vous ne pouvez pas régler ce que vous ne pouvez pas observer. Ci‑dessous des tâches pratiques à exécuter sur la cible ZFS/iSCSI et l’initiateur.
Chaque tâche inclut : la commande, ce que signifie une sortie typique, et la décision que vous en tirez.

Task 1: Verify pool health and obvious hardware errors

cr0x@server:~$ sudo zpool status -v tank
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 0 days 02:11:43 with 0 errors on Sun Dec 22 03:10:16 2025
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            nvme0n1 ONLINE       0     0     0
            nvme1n1 ONLINE       0     0     0
          mirror-1  ONLINE       0     0     0
            nvme2n1 ONLINE       0     0     0
            nvme3n1 ONLINE       0     0     0

errors: No known data errors

Signification : tout READ/WRITE/CKSUM non nul est un indice, pas une suggestion. Même un seul dispositif instable peut créer des « saccades »
à cause des réessais et des terminaisons d’I/O lentes.

Décision : si vous voyez des erreurs ou un vdev dégradé, arrêtez les réglages. Réparez le matériel d’abord. Travailler la performance sur un stockage cassé, c’est du cosplay.

Task 2: Confirm ashift and vdev topology (latency expectations start here)

cr0x@server:~$ sudo zdb -C tank | egrep -i 'ashift|vdev_tree|type'
        type: 'root'
        type: 'mirror'
            ashift: 12
        type: 'mirror'
            ashift: 12

Signification : ashift=12 indique un alignement secteur 4K. Si vous découvrez ashift=9/10/11 sur des dispositifs 4K, vous avez trouvé une taxe permanente.

Décision : ashift incorrect signifie généralement « reconstruire le pool correctement ». Si ce n’est pas possible immédiatement, documentez le risque et arrêtez de promettre une faible latence.

Task 3: Check how full the pool is (ZFS gets cranky when it’s crowded)

cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint tank
NAME   USED  AVAIL  REFER  MOUNTPOINT
tank   18.2T 2.11T  256K   /tank

Signification : avec seulement ~10% libre, la fragmentation et le comportement de l’allocateur peuvent augmenter la variance de latence, surtout sur les charges bloc.

Décision : visez à garder un espace libre significatif (souvent 20%+ pour les pools occupés). Si vous êtes juste, planifiez de la capacité avant de régler les profondeurs de file.

Task 4: Inspect the ZVOL properties (block size, compression, sync)

cr0x@server:~$ zfs get -o name,property,value -s local,default volblocksize,compression,sync,logbias,refreservation tank/zvol/vmstore
NAME              PROPERTY      VALUE
tank/zvol/vmstore  volblocksize  8K
tank/zvol/vmstore  compression   lz4
tank/zvol/vmstore  sync          standard
tank/zvol/vmstore  logbias       latency

Signification : c’est votre checklist de base « est-ce que c’est sensé ». sync=disabled sur un datastore VM est un drapeau rouge sauf décision de risque explicite.

Décision : si volblocksize est manifestement inadapté à la charge (par ex., 128K pour OLTP), planifiez une migration vers un nouveau ZVOL. Ne prétendez pas que c’est un réglage sysctl.

Task 5: Create a ZVOL correctly (example) and explain why

cr0x@server:~$ sudo zfs create -V 4T -b 8K -o compression=lz4 -o logbias=latency -o sync=standard tank/zvol/vmstore2

Signification : -V crée un volume bloc, -b définit volblocksize à la création, et nous choisissons explicitement un comportement favorable à la latence pour les écritures synchrones.

Décision : choisissez 8K quand vous ne savez pas et que vous attendez des écritures aléatoires. Choisissez 16K si votre charge est mixte mais pas très petite et que vous voulez un peu moins de métadonnées.

Task 6: Confirm the block device exists and see its sector sizes

cr0x@server:~$ ls -l /dev/zvol/tank/zvol/vmstore2
lrwxrwxrwx 1 root root 13 Dec 25 10:12 /dev/zvol/tank/zvol/vmstore2 -> ../../zd16
cr0x@server:~$ sudo blockdev --getss /dev/zd16
4096
cr0x@server:~$ sudo blockdev --getpbsz /dev/zd16
4096

Signification : le ZVOL présente ici des secteurs logiques 4K. C’est typiquement bon pour des invités modernes et s’aligne sur ashift=12.

Décision : si votre stack initiateur/OS attend 512 et que vous présentez du 4K, certains anciens invités se comportent mal. Pour les systèmes modernes, 4K est généralement correct et souvent meilleur.

Task 7: Verify whether you have a dedicated SLOG and whether it’s mirrored

cr0x@server:~$ sudo zpool status tank | sed -n '1,120p'
  pool: tank
 state: ONLINE
config:

        NAME           STATE     READ WRITE CKSUM
        tank           ONLINE       0     0     0
          mirror-0     ONLINE       0     0     0
            nvme0n1    ONLINE       0     0     0
            nvme1n1    ONLINE       0     0     0
          mirror-1     ONLINE       0     0     0
            nvme2n1    ONLINE       0     0     0
            nvme3n1    ONLINE       0     0     0
        logs
          mirror-2     ONLINE       0     0     0
            nvme4n1    ONLINE       0     0     0
            nvme5n1    ONLINE       0     0     0

Signification : un SLOG mirror réduit le risque qu’une défaillance d’un périphérique de log entraîne la perte d’écritures sync récentes (selon le mode de défaillance).

Décision : si vous exécutez beaucoup de sync iSCSI et que vous tenez à la correction, utilisez un SLOG mirroré avec PLP. Si vous ne pouvez pas, acceptez une latence sync plus élevée et ajustez vos attentes, pas la physique.

Task 8: Watch ZFS latency and queueing in real time

cr0x@server:~$ sudo zpool iostat -v tank 1
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        18.2T  2.11T    820   1900  91.3M  76.2M
  mirror      -      -     410    950  45.6M  38.1M
    nvme0n1    -      -     205    470  22.8M  19.3M
    nvme1n1    -      -     205    480  22.8M  18.8M
  mirror      -      -     410    950  45.7M  38.1M
    nvme2n1    -      -     205    475  22.9M  19.1M
    nvme3n1    -      -     205    475  22.8M  19.0M
logs          -      -       0    600      0  12.0M
  mirror      -      -       0    600      0  12.0M
    nvme4n1    -      -       0    300      0   6.0M
    nvme5n1    -      -       0    300      0   6.0M

Signification : vous pouvez voir si les écritures frappent le périphérique de log (charge sync) et si un vdev traîne.

Décision : si un dispositif montre constamment moins d’opérations ou de bande passante sous mirror, suspectez la latence du dispositif, le firmware, la limitation thermique ou des problèmes de topologie PCIe.

Task 9: Check TXG behavior and “stutter cadence” clues

cr0x@server:~$ sudo arcstat.py 1 5
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
10:20:01   920    80      8     0   0%    40  50%    40  50%  128G   128G
10:20:02   980    70      7     0   0%    35  50%    35  50%  128G   128G
10:20:03   910    95     10     0   0%    47  49%    48  51%  128G   128G
10:20:04   940    82      9     0   0%    41  50%    41  50%  128G   128G
10:20:05   960    78      8     0   0%    39  50%    39  50%  128G   128G

Signification : la pression ARC et les misses se corrèlent souvent avec la latence de lecture, pas les saccades d’écriture. Mais si votre « saccade » est en réalité des tempêtes de misses du cache lecture,
cela le montrera : taux de miss élevé pendant que l’application se fige.

Décision : si l’ARC est saturé et miss% élevé, envisagez de la mémoire, une taille de working set, ou de déplacer les charges lecture-intensives vers des protocoles fichiers où le caching fonctionne mieux.
Ne sautez pas sur L2ARC avant de l’avoir mesuré.

Task 10: Confirm iSCSI target configuration and mapped LUNs

cr0x@server:~$ sudo targetcli ls
o- / ............................................................................. [...]
  o- backstores ................................................................. [...]
  | o- block ............................................................. [Storage Objects: 1]
  | | o- vmstore2 ........................................ [/dev/zvol/tank/zvol/vmstore2 (4.0TiB)]
  o- iscsi ............................................................... [Targets: 1]
    o- iqn.2025-12.lab:storage.tank ............................................. [TPGs: 1]
      o- tpg1 .......................................................... [no-gen-acls, no-auth]
        o- acls ............................................................... [ACLs: 1]
        | o- iqn.2025-12.lab:client01 .............................................. [Mapped LUNs: 1]
        o- luns ............................................................... [LUNs: 1]
        | o- lun0 ............................... [block/vmstore2 (/dev/zvol/tank/zvol/vmstore2)]
        o- portals ......................................................... [Portals: 1]
          o- 10.0.10.10:3260 .............................................................. [OK]

Signification : vérifiez que le backstore pointe vers le ZVOL prévu et que les ACL mappent le bon IQN d’initiateur.

Décision : si vous voyez des backstores fileio pour du stockage VM haute performance, reconsidérez : les backstores bloc vers ZVOLs sont typiquement plus propres et plus rapides.

Task 11: On the initiator, confirm sessions and negotiated parameters

cr0x@server:~$ sudo iscsiadm -m session -P 3 | sed -n '1,120p'
iSCSI Transport Class version 2.0-870
iscsiadm version 2.1.9
Target: iqn.2025-12.lab:storage.tank
    Current Portal: 10.0.10.10:3260,1
    Persistent Portal: 10.0.10.10:3260,1
    DataDigest: None
    HeaderDigest: None
    MaxRecvDataSegmentLength: 262144
    MaxXmitDataSegmentLength: 262144
    FirstBurstLength: 262144
    MaxBurstLength: 1048576
    InitialR2T: No
    ImmediateData: Yes

Signification : ces paramètres influencent la performance et le coût CPU. Les digests ajoutent une charge CPU mais aident à détecter la corruption sur de mauvais réseaux.
La plupart des réseaux DC propres fonctionnent sans digest.

Décision : si le CPU est le goulot ou si vous voyez des retransmissions, revoyez d’abord le réseau et le MTU. Ne « tunez » pas les bursts iSCSI en première intention ; ce n’est rarement le principal problème.

Task 12: Verify multipath status (path flaps create “stutter”)

cr0x@server:~$ sudo multipath -ll
mpatha (36001405f3f2b7f8d9d5b3d2a8c1e0001) dm-3 IET,VIRTUAL-DISK
size=4.0T features='1 queue_if_no_path' hwhandler='0' wp=rw
|-+- policy='service-time 0' prio=50 status=active
| `- 4:0:0:0 sdb 8:16 active ready running
`-+- policy='service-time 0' prio=50 status=enabled
  `- 5:0:0:0 sdc 8:32 active ready running

Signification : vous voulez des chemins stables « active ready running ». Si vous voyez des chemins alternant entre failed/active, c’est votre générateur de pics de latence.

Décision : corrigez le câblage/le switching/les NIC/les MTU avant de toucher aux tunables ZFS. Le tuning stockage ne peut pas voter contre la physique et la perte de paquets.

Task 13: Check for TCP retransmits and NIC drops on the target

cr0x@server:~$ sudo nstat -az | egrep 'TcpRetransSegs|TcpExtTCPRenoReorder|IpInDiscards'
TcpRetransSegs                    12                 0.0
IpInDiscards                      0                  0.0

Signification : les retransmissions ajoutent de la variance à la latence. Quelques-unes sur une longue uptime sont acceptables ; une montée rapide sous charge ne l’est pas.

Décision : si les retransmissions montent pendant vos « saccades », investigatez buffers de switch/ECN, mismatch MTU, optiques/câbles défectueux, ou saturation IRQ/CPU sur la NIC.

Task 14: Observe disk latency directly (don’t guess)

cr0x@server:~$ sudo iostat -x 1 3
Linux 6.8.0 (server)  12/25/2025  _x86_64_  (32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.10    0.00    5.20    2.80    0.00   79.90

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await  r_await  w_await
nvme0n1         210.0   480.0  24000   20000     0.0     0.0   71.0   1.80     1.20     2.10
nvme1n1         215.0   470.0  24500   19800     0.0     0.0   69.0   1.90     1.30     2.20
nvme2n1         205.0   475.0  23500   19900     0.0     0.0   73.0   1.70     1.10     2.00
nvme3n1         205.0   475.0  23600   20100     0.0     0.0   72.0   1.75     1.15     2.05

Signification : si await saute dans les dizaines/centaines de millisecondes pendant une saccade, vos dispositifs de pool sont le goulot (ou sont forcés en waits sync).

Décision : si la latence des dispositifs est correcte mais que le client voit des saccades, suspectez la mise en file iSCSI, les flaps multipath, ou le comportement des écritures synchrones (SLOG, tempêtes de flush).

Task 15: Identify synchronous write pressure (ZIL/SLOG usage clues)

cr0x@server:~$ sudo zfs get -o name,property,value sync,logbias tank/zvol/vmstore2
NAME               PROPERTY  VALUE
tank/zvol/vmstore2  sync      standard
tank/zvol/vmstore2  logbias   latency
cr0x@server:~$ sudo zpool iostat -v tank 1 | sed -n '1,20p'
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        18.2T  2.11T    200   2400  22.0M  98.0M
logs          -      -       0   1200      0  24.0M

Signification : des ops d’écriture significatives sur logs indiquent une activité sync (ZIL). Si vous n’avez pas de SLOG, ces écritures sync frappent les vdevs principaux et peuvent faire monter la latence.

Décision : si votre charge est sync-intensive et sensible à la latence, implémentez un SLOG adapté. Si vous en avez déjà un et que les saccades persistent, vérifiez qu’il n’est pas saturé ou en throttling thermique.

Task 16: Run a controlled fio test from the initiator (measure the right thing)

cr0x@server:~$ sudo fio --name=iscsi-4k-randwrite --filename=/dev/mapper/mpatha --direct=1 --rw=randwrite --bs=4k --iodepth=32 --numjobs=4 --time_based=1 --runtime=60 --group_reporting
iscsi-4k-randwrite: (groupid=0, jobs=4): err= 0: pid=4121: Thu Dec 25 10:30:12 2025
  write: IOPS=18.2k, BW=71.2MiB/s (74.7MB/s)(4272MiB/60001msec)
    slat (usec): min=4, max=1120, avg=15.2, stdev=9.8
    clat (usec): min=90, max=84210, avg=650.1, stdev=2100.4
     lat (usec): min=110, max=84230, avg=666.2, stdev=2101.0
    clat percentiles (usec):
     |  1.00th=[  140], 10.00th=[  180], 50.00th=[  310], 90.00th=[  980], 99.00th=[ 5800]
     | 99.90th=[38000], 99.99th=[79000]

Signification : la latence moyenne semble correcte, mais 99.90th à 38ms et 99.99th à 79ms représentent votre « saccade ». C’est ce que ressentent les utilisateurs.

Décision : optimisez pour la latence en queue, pas la moyenne. Si les queues montent périodiquement, corrélez avec les temps de sync TXG, le comportement du SLOG et les retransmissions réseau.

Feuille de route de diagnostic rapide

Quand la saccade se produit, vous avez besoin d’une liste courte qui réduit rapidement le périmètre. Voici l’ordre que j’utilise parce qu’il trouve
la classe des « évidences cassées » avant que vous perdiez des heures sur des tunables.

Première étape : exclure défaillances et flaps

  • zpool status : des erreurs de dispositif, vdevs dégradés, ou resilvers ? Si oui, c’est l’histoire.
  • multipath -ll sur les initiateurs : des chemins qui échouent/clignotent ? Si oui, corrigez le réseau/les chemins.
  • nstat / compteurs NIC : retransmissions, drops, overruns de ring. Si oui, vous avez un problème réseau/IRQ CPU.

Seconde étape : décider si c’est la latence disque ou la sémantique sync

  • iostat -x sur la cible : des pics d’await ? Si oui, disques ou SLOG lents ou saturés.
  • zpool iostat -v 1 : les périphériques logs montrent-ils beaucoup d’écritures ? Si oui, vous êtes sync-heavy ; la qualité du SLOG compte.
  • fio percentiles : une queue tail élevée avec une moyenne modérée pointe souvent vers des flushs/sync périodiques ou des événements de vidage de file.

Troisième étape : rechercher inadéquations de configuration et amplification

  • volblocksize : inadapté à la charge ? Un ZVOL 128K pour des écritures 4K aléatoires saccadera comme une horloge sous pression.
  • remplissage du pool : les pools proches de la capacité amplifient la douleur de l’allocateur et la fragmentation.
  • RAIDZ + petites écritures sync aléatoires : attendez-vous à des queues plus élevées, surtout pendant scrubs/resilvers.

Quatrième étape : régler la profondeur de file avec précaution

Le réglage de la profondeur de file est l’étape finale car il peut masquer des problèmes et en créer de nouveaux. Mais une fois le système vérifié sain,
ajuster la profondeur d’initiateur et les politiques multipath peut lisser la latence sans sacrifier la cohérence.

Erreurs courantes : symptôme → cause → correction

  • Symptôme : pics de latence périodiques toutes les quelques secondes sous charge d’écriture
    Cause : pression de sync TXG + stockage stable lent, souvent pas de SLOG ou SLOG inadéquat
    Correction : ajouter un SLOG miroir PLP ; vérifier que les écritures de log y accèdent ; s’assurer que le pool n’est pas presque plein ; programmer scrubs/resilvers en dehors des heures de pointe.
  • Symptôme : bon débit mais « gels » dans les VMs, surtout sur invités Windows
    Cause : tempêtes de flush (système de fichiers invité, hyperviseur ou appli), latence des écritures sync exposée via iSCSI
    Correction : SLOG correct, garder sync=standard ; envisager un réglage côté invité (politique de cache d’écriture) uniquement après revue des risques.
  • Symptôme : IOPS aléatoires 4K catastrophiques, forte utilisation disque, bande passante étonnamment basse
    Cause : volblocksize trop grand causant une amplification read-modify-write
    Correction : créer un nouveau ZVOL avec volblocksize 8K/16K et migrer ; ne tentez pas de « ruser » avec des réglages.
  • Symptôme : la saccade n’apparaît qu’avec multipath activé
    Cause : chemin secondaire instable, path checker trop agressif, mauvaise config de switch, routage asymétrique
    Correction : stabiliser L2/L3 d’abord ; définir des politiques multipath sensées ; éviter « round-robin tout » si votre cible ou réseau ne peut pas le gérer proprement.
  • Symptôme : la latence empire après activation de L2ARC
    Cause : L2ARC vole RAM/CPU, thrash de cache, ou contention SSD avec la charge pool
    Correction : retirer L2ARC ; augmenter la RAM ; n’ajouter L2ARC que si vous avez mesuré le working set lecture et pouvez dédier un périphérique rapide.
  • Symptôme : perf s’effondre pendant scrub/resilver
    Cause : disposition du pool (RAIDZ), marge IOPS limitée, scrub en concurrence avec la production, déséquilibre de vdev
    Correction : planifier les scrubs ; ajuster le comportement du scrub si nécessaire ; concevoir des pools avec marge ; mirrors pour charges bloc sensibles à la latence.
  • Symptôme : pauses « aléatoires » ; logs montrent timeouts/reconnects iSCSI
    Cause : retransmissions TCP, mismatch MTU, offloads NIC bugués, saturation IRQ CPU
    Correction : valider MTU de bout en bout ; vérifier les drops ; pinner les IRQs ; envisager de désactiver les offloads problématiques ; garder simple et mesurable.
  • Symptôme : l’utilisation d’espace semble correcte, puis tout ralentit soudainement près de la capacité
    Cause : sur-souscription liée au thin-provisioning + pool atteignant une forte utilisation
    Correction : appliquer quotas/refreservations ; alerter plus tôt ; maintenir de l’espace libre ; traiter la capacité comme un SLO, pas un tableur.

Trois mini-récits d’entreprise du terrain

Mini-récit 1 : L’incident causé par une fausse hypothèse (volblocksize « ça n’a pas d’importance »)

Une entreprise de taille moyenne a migré depuis un SAN Fibre Channel vieillissant vers une cible iSCSI basée sur ZFS. Le plan de test était « copier quelques VMs,
exécuter des tests de connexion, considérer que c’est bon ». Tout semblait correct en labo. En production, le système ERP a commencé à « se bloquer » quelques secondes
par fois pendant les heures de pointe.

L’équipe infra a supposé que le réseau était congestionné parce que iSCSI est « sur TCP », et TCP est « fragile ». Ils ont ajouté de la bande passante :
monté les uplinks, réorganisé les VLANs, même remplacé un switch. La saccade a persisté, poliment inchangée. Les utilisateurs décrivaient toujours « ça se fige puis rattrape ».

L’indice est venu d’un fio ciblé qui a reproduit le problème : des écritures aléatoires 4K montraient de grandes latences en queue, même si la latence moyenne
semblait acceptable. Du côté ZFS, le ZVOL avait été créé avec volblocksize 128K—parce que quelqu’un avait lu un billet « les blocs plus gros sont plus rapides ».
Pour la charge ERP, l’I/O était petite, aléatoire et sync-intensive.

Sous charge, ZFS faisait des cycles read-modify-write sur de gros blocs pour de petites mises à jour, moulinant des métadonnées et forçant des I/O supplémentaires.
Le système n’était pas « lent » autant que « par vagues avec de la misère périodique ». La correction fut sans gloire : créer un nouveau ZVOL avec volblocksize 8K,
migrer le LUN au niveau hyperviseur, et garder la compression activée.

L’upgrade réseau n’a pas été vain—il a amélioré la marge—mais il n’a pas touché la cause racine. Le postmortem a changé le processus de construction :
la taille de bloc est devenue une entrée de conception formelle, pas une valeur par défaut.

Mini-récit 2 : L’optimisation qui a échoué (sync=disabled « pour la perf »)

Un autre client faisait tourner un cluster de virtualisation sur ZFS iSCSI. Ils avaient un bon pool, un réseau décent, et recevaient quand même des plaintes sur des pauses VM
pendant les fenêtres de patch. Quelqu’un a découvert que mettre sync=disabled rendait les benchmarks excellents. Ils l’ont appliqué au ZVOL principal VM
pendant une fenêtre de maintenance et ont déclaré victoire.

Pendant un temps, tout semblait parfait. Les graphes de latence se sont lissés. Le helpdesk s’est tu. Puis un événement d’alimentation a touché un rack—pas tout le datacenter,
juste une rangée où un PDU a lâché. Le serveur de stockage a redémarré. Une poignée de VMs sont revenues avec des systèmes de fichiers corrompus.
Pas toutes. Juste assez pour rendre l’incident réel et rageant.

L’« optimisation » de l’équipe n’était pas un réglage de perf. C’était un changement d’intégrité. Avec sync disabled, le système accusait réception d’écritures encore en cache volatil.
Pour certaines VMs ça n’a pas posé de problème ; pour d’autres, absolument. La récupération fut un mélange de restaurations depuis sauvegarde,
réparations de systèmes de fichiers, et un long week-end à expliquer à la direction pourquoi la « solution rapide » avait fait disparaître des données.

La correction à long terme n’a pas été une conférence, mais une architecture : SLOG PLP mirror pour les charges sync, et refus de changer la sémantique de sécurité des données
pour poursuivre des graphes plus jolis. Ils ont aussi ajouté des tests qui simulent la perte d’alimentation en forçant des redémarrages brutaux pendant des charges synthétiques d’écriture sync
en pré-production.

Blague n°2 : sync=disabled est l’équivalent stockage de retirer les détecteurs de fumée parce que le bip est agaçant.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (marge de capacité + discipline des scrubs)

Une société financière utilisait ZFS iSCSI pour dev/test et un sous-ensemble de charges analytiques production. Leur lead stockage avait une politique opiniâtre :
garder les pools sous un seuil d’utilisation, et exécuter les scrubs selon un calendrier avec alertes claires pour toute erreur de checksum.

C’était ennuyeux. Cela signifiait aussi qu’ils se disputaient souvent avec les chefs de projet sur les demandes de capacité. Mais ils gardaient une marge, et traitaient
les scrubs comme une « hygiène non optionnelle » plutôt que « quelque chose qu’on fait quand on s’ennuie ».

Un trimestre, un job batch a commencé à marteler le stockage avec des écritures aléatoires et des flushs sync inattendus. La latence a augmenté, mais elle n’est pas devenue
catastrophique. Le pool avait encore de l’espace libre, l’allocateur n’était pas coincé, et les mirrors avaient une marge IOPS. L’équipe a pu ralentir le job batch,
ajuster les horaires, et régler la profondeur de file de l’initiateur sans être sous pression existentielle.

Pendant la même période, un scrub a détecté tôt des erreurs de checksum sur un dispositif. Ils l’ont remplacé de façon préventive. Pas d’incident, pas de drame, pas de « on a perdu une demi-journée ».
La politique ne les a pas rendus héros. Elle a rendu le système prévisible, ce qui est mieux.

Listes de contrôle / plan pas à pas

Pas à pas : construire un nouveau ZFS iSCSI ZVOL qui ne saccade pas

  1. Choisir la disposition du pool pour la latence : mirrors pour VM/iSCSI sauf raison forte contraire.
  2. Confirmer ashift avant de créer des données : vérifier avec zdb -C. Si c’est faux, corriger maintenant, pas plus tard.
  3. Décider volblocksize selon la charge : 8K/16K pour un magasin VM général ; aligner sur la taille de page DB quand connue.
  4. Créer le ZVOL avec des propriétés explicites : compression lz4, sync standard, logbias latency si sync-heavy.
  5. Planifier l’espace libre : cible opérationnelle (par ex., 20% libre). Mettre des alertes avant d’atteindre la douleur.
  6. Ajouter un SLOG approprié si sync-heavy : mirroré, PLP, faible latence, endurance adaptée.
  7. Exporter via iSCSI avec une config stable : backstore bloc vers ZVOL ; ACL explicites ; configuration portail cohérente.
  8. Configurer multipath sur les initiateurs : valider que les deux chemins sont vraiment indépendants ; confirmer « active ready running » stable.
  9. Exécuter des fio percentiles depuis les initiateurs : mesurer les queues, pas seulement la moyenne ; tester patterns sync-ish (fsync) et async.
  10. Enregistrer une baseline : zpool iostat, iostat -x, retransmits, percentiles de latence. Cela devient votre « connu bon ».

Checklist opérationnelle : lors d’ajout de nouveaux LUNs ou nouveaux tenants

  • Confirmer le seuil d’espace libre du pool et la croissance projetée.
  • Valider que volblocksize correspond au profil I/O attendu du tenant.
  • Décider d’utiliser refreservation pour éviter les surprises du thin-provisioning.
  • Vérifier la santé du SLOG et les indicateurs d’usure (hors du périmètre des commandes ZFS, mais obligatoire).
  • Planifier des scrubs et surveiller les erreurs de checksum comme signal prioritaire.
  • Exécuter un court test fio smoke après changements réseau ou politiques multipath.

FAQ

1) Dois‑je utiliser un dataset (fichier) via NFS au lieu d’un ZVOL iSCSI ?

Si votre hyperviseur et votre charge sont satisfaits d’un NFS et que vous voulez un réglage et une observabilité plus simples, les datasets NFS sont souvent plus faciles.
Utilisez ZVOL iSCSI quand vous avez besoin de sémantique bloc ou que l’OS l’exige.

2) Quel volblocksize choisir pour le stockage VM ?

Commencez avec 8K pour les charges VM générales quand vous tenez à la latence des écritures aléatoires. Utilisez 16K si vous avez mesuré que vos charges penchent vers du plus gros
et que vous voulez un peu moins d’overhead métadonnées. Évitez 64K/128K pour des stores VM mixtes sauf si vous avez vraiment des patterns séquentiels.

3) Puis‑je changer volblocksize plus tard ?

Pas en place. Vous créez typiquement un nouveau ZVOL avec le volblocksize désiré et migrez les données côté client/hyperviseur.
Planifiez cette réalité en amont.

4) Ai‑je besoin d’un SLOG pour iSCSI ?

Si votre charge émet des écritures sync (beaucoup le font) et que vous tenez à la cohérence de la latence, oui—un SLOG miroiré PLP adapté est souvent la différence
entre « fluide » et « pauses mystères ». Si votre charge est majoritairement async et que la latence sync est tolérable, vous pouvez vous en passer.

5) Pourquoi ne pas simplement mettre sync=disabled et profiter de la vitesse ?

Parce que cela change la correction. Vous pouvez perdre des écritures reconnues en cas de perte de puissance ou crash, et la défaillance peut être partielle et douloureuse.
Si vous acceptez ce risque pour un environnement scratch, documentez-le. En production, non.

6) L2ARC aide‑t‑il les performances iSCSI ZVOL ?

Parfois, pour des charges lecture‑lourdes avec un working set supérieur à la RAM. Mais L2ARC consomme RAM et CPU et peut entrer en concurrence avec les périphériques.
Mesurez les hits ARC et la latence réelle de lecture avant d’ajouter. Ce n’est pas un levier magique « plus de cache ».

7) Mirrors vs RAIDZ pour iSCSI : quelle différence pratique ?

Les mirrors offrent généralement une meilleure latence d’écriture aléatoire et des IOPS plus prévisibles pendant rebuild/scrub. RAIDZ échange cela contre une efficacité de capacité.
Pour VM/bases de données sur iSCSI, les mirrors sont le défaut le plus sûr.

8) Mon réseau est 25/100GbE—pourquoi j’ai toujours des saccades ?

Parce que la bande passante ne corrige pas la latence en queue. Microbursts, retransmissions, flaps de chemin, et saturation des interruptions CPU peuvent provoquer des pauses même sur des liens rapides.
Vérifiez d’abord les drops/retransmits et la stabilité multipath.

9) Dois‑je activer la compression sur les ZVOLs ?

Oui, généralement lz4. Cela réduit souvent les I/O physiques et améliore la latence. Il existe des exceptions (flux déjà compressés),
mais « désactivée par défaut » est une vieille superstition.

10) Pourquoi les benchmarks semblent corrects mais les applis se figent ?

Beaucoup de benchmarks rapportent des moyennes et masquent la latence en queue. Les utilisateurs ressentent le centile 99.9. Utilisez fio percentiles et corrélez avec TXG ZFS,
l’activité SLOG, et les retransmissions réseau.

Conclusion : prochaines étapes réalisables cette semaine

Si votre ZFS iSCSI ZVOL saccade sous charge, ce n’est généralement pas un seul réglage magique. C’est une chaîne : géométrie de taille de bloc, sémantique sync, qualité du périphérique de log,
mise en file, et stabilité réseau. Votre travail consiste à trouver où les attentes se créent, puis enlever les pires sans tricher sur la durabilité.

Prochaines étapes pratiques :

  1. Exécutez la feuille de route de diagnostic rapide pendant un événement de saccade et capturez des preuves : zpool status, zpool iostat -v 1, iostat -x 1, compteurs de retransmissions, et percentiles fio.
  2. Auditez le volblocksize de chaque ZVOL et identifiez les valeurs hors norme qui ne correspondent pas à la réalité de la charge.
  3. Décidez explicitement si vous avez besoin d’un SLOG. Si oui, achetez la bonne classe de périphérique (PLP, faible latence, mirror), pas un « NVMe grand public rapide ».
  4. Confirmez la stabilité multipath de bout en bout. Éliminez le path flapping avant de toucher aux tunables de stockage.
  5. Définissez une politique de capacité et des alertes qui maintiennent le pool hors de la zone « presque plein » où les saccades deviennent un mode de vie.
← Précédent
GPU d’ordinateurs portables : pourquoi un même nom peut cacher cinq niveaux de performance
Suivant →
MySQL vs TiDB : compatibilité MySQL vs complexité opérationnelle — à quoi vous vous engagez

Laisser un commentaire