Si ejecutas VMs sobre ZFS, probablemente ya has ajustado recordsize en datasets, discutido sobre SLOGs en el chat y culpado a los “vecinos ruidosos” al menos una vez. Y luego un día descubres volblocksize en zvols y te das cuenta de que has estado conduciendo con el freno de mano puesto—en silencio, caro y mirando con cara confusa tus gráficas de IOPS.
volblocksize es uno de esos ajustes que parece demasiado pequeño para importar, como la pestaña “Avanzado” que nadie pulsa. Pero decide cómo ZFS trocea las E/S del disco virtual de tu VM, lo que determina la amplificación de escritura, que determina la latencia, y que decide si tu base de datos piensa que hoy es un buen día para hacer timeout. Esto no es teoría; aparece como picos de latencia del percentil 99 en producción en el peor momento posible.
Qué es realmente volblocksize (y qué no es)
Un zvol es ZFS haciéndose pasar por un dispositivo de bloques. Lo creas con zfs create -V ..., y aparece como algo tipo /dev/zvol/pool/vm-101-disk-0. Tu hipervisor luego lo formatea (o lo pasa en crudo), y el sistema invitado cree que es un disco.
volblocksize es el tamaño de bloque interno que ZFS usa para los datos de ese zvol. Cuando el invitado escribe fragmentos de 4K, 8K, 64K o 1M, ZFS al final tiene que mapear esas escrituras en sus propios bloques. Con un dataset, ese control generalmente es recordsize. Con un zvol, el control análogo es volblocksize.
Es tentador tratarlo como “simplemente ponlo a 4K para VMs”, o “ponlo a 128K porque a ZFS le gustan los bloques grandes”. Ambas son medias verdades que envejecen mal. La respuesta correcta depende del tipo de I/O (aleatorio vs secuencial), del sistema de archivos invitado, del tamaño de página de la base de datos, del comportamiento de escrituras sync y del perfil de latencia del hardware.
Qué no es:
- No es el tamaño de bloque del sistema de archivos del invitado. Tu invitado puede usar bloques de 4K sobre un zvol con
volblocksizede 16K; seguirá “funcionando”, sólo que puede funcionar de forma cara. - No es el
ashiftdel pool. El alineamiento importa, peroashifttrata del tamaño de sector físico que ZFS asume para los vdevs. - No es un interruptor mágico de IOPS. Es una perilla de balance entre eficiencia de IOPS, sobrecarga de metadatos, ratio de compresión y riesgo de cola de latencia.
Primera broma, porque la hemos ganado: Cambiar volblocksize después de haber puesto datos en el zvol es como intentar cambiar el tamaño de una pizza después de que ya se comió—hay métodos, pero ninguno parece “redimensionar”.
Por qué decide IOPS y latencia
IOPS y latencia no son sólo “qué tan rápido son los discos”. En almacenamiento para VM, también son cuántas operaciones fuerzas al stack de almacenamiento por cada operación del invitado. volblocksize cambia ese factor multiplicador.
La amplificación de escritura que sí puedes razonar
Supón que tu invitado emite una escritura aleatoria de 4K. Lo que ZFS hace depende de volblocksize:
- Si
volblocksize=4K, ZFS puede actualizar un solo bloque de 4K (más metadatos). Esta es la correspondencia “literal”. - Si
volblocksize=16K, ZFS debe actualizar un bloque de 16K. Si sólo cambió 4K, ZFS aún escribe un nuevo bloque de 16K (copy-on-write), lo que implica comportamiento de lectura-modificación-escritura a nivel lógico: tiene que construir el nuevo contenido de 16K y luego escribirlo. Dependiendo del caché y de cómo se arme ese bloque, estás quemando ancho de banda y añadiendo riesgo de latencia. - Si
volblocksize=128K, ahora potencialmente reescribes 128K por un cambio de 4K—otra vez, al menos lógicamente. Si los datos son compresibles y la escritura cubre mayormente ceros, puedes tener suerte. Pero “tal vez los datos se comprimen” no es una estrategia.
Ahora inviértelo para I/O secuencial. Si tu VM está leyendo/escribiendo grandes secuencias (backup, log shipping, copias de archivos grandes): los bloques grandes pueden reducir la sobrecarga y mejorar el rendimiento porque ZFS hace menos operaciones de I/O por megabyte.
La latencia es un juego de cola (tail)
La latencia media te halaga. La latencia en la cola te humilla. Los bloques mayores aumentan la cantidad de trabajo por cambio lógico y ensanchan la distribución de latencias cuando el sistema está bajo presión: más bytes que mover, más tiempo atascado detrás de otras escrituras, más tiempo esperando commits de txg y más oportunidades de chocar con requisitos de escrituras sync.
En producción, los peores momentos son previsibles: tormentas de snapshots, ventanas de backup, scrubs, resilvers y ese trabajo por lotes trimestral que nadie le dijo a SRE. Una elección de volblocksize que “está bien en un benchmark” puede convertirse en tu generador de fallos del percentil 99.9 cuando el pool está al 70% y fragmentado.
Metadatos y CPU tampoco son gratis
Bloques más pequeños significan más bloques, lo que implica más metadatos: más punteros de bloque, más bloques indirectos, más operaciones de checksumming, más decisiones de compresión, más trabajo para ARC. Puedes crear un sistema que sea “rápido en IOPS” pero limitado por CPU en la capa de almacenamiento, especialmente con cifrado o compresión pesada.
Datos interesantes y contexto histórico
A los ingenieros de almacenamiento les encanta el folklore; aquí están los trozos concretos que realmente importan:
- ZFS nació en un mundo de discos giratorios, donde el rendimiento secuencial importaba y las penalizaciones por seek eran brutales. Los bloques grandes tenían mucho sentido.
- Copy-on-write es la superpotencia de ZFS y su recaudador de impuestos. Permite snapshots y consistencia, pero también significa que “pequeños cambios pueden reescribir bloques grandes” cuando eliges bloques grandes.
- Los zvols se construyeron para proporcionar dispositivos de bloques para iSCSI, discos de VM y casos de uso tipo swap—cargas que no se comportan como tráfico NAS de archivos grandes.
- Los sectores de 4K ganaron en la industria, pero la transición fue desordenada (512e, Advanced Format). El desalineamiento puede reducir silenciosamente el rendimiento de escritura a la mitad.
- Los hipervisores de VM cambiaron el juego de patrones de I/O. Thin provisioning, snapshots a nivel de hipervisor y ráfagas de escrituras aleatorias se volvieron normales, no excepcionales.
- Los tamaños de página de bases de datos suelen ser 8K o 16K (varía por motor y configuración). Cuando tu tamaño de bloque de almacenamiento pelea con el tamaño de página de la BD, pagas dos veces: una en I/O, otra en comportamiento de WAL/redo.
- Las SSD hicieron que el I/O aleatorio fuera barato comparado con HDDs, pero no lo hicieron gratis. La latencia es menor, pero la amplificación de escritura (tanto en ZFS como dentro del FTL de la SSD) aún importa.
- NVMe redujo la latencia tanto que el “overhead de software” se volvió visible. De repente, las elecciones de tamaño de bloque aparecen como tiempo de CPU y contención de locks, no sólo espera de disco.
- La compresión en ZFS se volvió mainstream porque las CPU se aceleraron y el almacenamiento siguió siendo caro. La compresión interactúa fuertemente con el tamaño de bloque: los bloques más grandes suelen comprimirse mejor, pero pueden amplificar escrituras pequeñas.
Un modelo mental útil a las 3 a.m.
Piensa en volblocksize como la “unidad mínima de reescritura” para el zvol dentro de ZFS. El invitado puede escribir 4K, pero si ZFS almacena los datos en fragmentos de 64K, ZFS es responsable de producir una nueva versión de 64K de ese fragmento en cada cambio.
Hay tres grandes consecuencias:
- IOPS de escritura aleatoria: generalmente un volblocksize más pequeño ayuda, porque reescribes menos bytes por escritura.
- Throughput secuencial: un volblocksize mayor puede ayudar, porque amortizas metadatos y trabajo de checksums.
- Latencia en cola: lo más pequeño tiende a ser más predecible bajo cargas mixtas, mientras que lo más grande puede dispararse brutalmente cuando combinas escrituras sync + presión del pool.
Segunda y última broma: El tuning de almacenamiento es como hacer espresso: un grado demasiado fino y todo se atasca; un grado demasiado grueso y sabe a arrepentimiento.
Escrituras sync, SLOGs y por qué volblocksize cambia el dolor
Las cargas de VM a menudo generan escrituras sync incluso cuando no las pediste. Las bases de datos hacen fsync. Los sistemas de archivos con journaling confirman transacciones. Los hipervisores pueden emitir flushes. Y si exportas el zvol por iSCSI/NFS (o usas ciertas configuraciones del hipervisor), “sync” puede convertirse en el comportamiento por defecto.
En ZFS, las escrituras sync deben hacerse durables antes de que el sistema las confirme. Sin un dispositivo SLOG separado, eso significa que los vdevs principales del pool deben comprometer suficientes registros de intentos a almacenamiento estable rápidamente. Con un buen SLOG, puedes confirmar rápido y luego vaciar al pool en commits de txg.
¿Dónde entra volblocksize? De dos maneras:
- Cuánto datos convierte una escritura “pequeña” dentro de ZFS antes de poder ser confirmada. Los bloques mayores pueden incrementar la cantidad de trabajo para representar de forma segura ese cambio.
- Cuánta fragmentación y churn creas en el pool principal, lo que afecta el tiempo de commit de txg y por lo tanto la latencia sync durante la congestión.
Un patrón clásico de dolor: todo está bien hasta que empieza una ventana de backup. El pool se llena de lecturas/escrituras secuenciales grandes, los tiempos de commit de txg suben y de repente la latencia de fsync de tu base de datos pasa de “bien” a “la app está caída”. Si tu volblocksize es demasiado grande para discos VM con escrituras aleatorias, has incrementado la cantidad de trabajo por fsync bajo presión.
Matiz importante: el rendimiento de escrituras sync no es sólo sobre volblocksize. También importa la propiedad sync, logbias, la calidad del SLOG, la disposición del pool (mirrors vs RAIDZ) y si tu almacenamiento está saturado. Pero volblocksize es una de las pocas palancas que cambia la granularidad fundamental del cambio.
Compresión, checksums y el impuesto oculto de CPU
La compresión en zvols no es automáticamente mala. Pero tampoco es automáticamente gratis.
Los bloques más grandes suelen comprimirse mejor porque los compresores ven más patrones repetidos. Esto puede reducir las escrituras físicas, lo que compensa el coste de reescribir bloques lógicos más grandes. En el mundo real, la mezcla importa:
- Discos OS: a menudo se comprimen bien (texto, binarios, muchos ceros). La compresión puede ayudar, y la elección del tamaño de bloque puede ser menos castigadora.
- Bases de datos con páginas ya comprimidas: pueden no comprimirse bien. Entonces los bloques mayores sólo significan más bytes reescritos sin ahorro.
- Invitados cifrados: si el invitado cifra el sistema de archivos, la compresión puede volverse ineficaz en la capa ZFS. No cuentes con la compresión para salvarte de un mal tamaño de bloque cuando los datos son de alta entropía.
El checksumming y el cifrado (opcionales) también escalan con “número de bloques” y “bytes procesados”. Un tamaño de bloque demasiado pequeño puede volverse pesado en CPU con IOPS altos; demasiado grande puede volverse costoso en latencia con escrituras aleatorias. Buscas el codo en la curva para tu entorno.
ashift, tamaños de sector y alineamiento
Si volblocksize es la unidad lógica de reescritura del zvol, ashift es la suposición de alineamiento físico del pool. La mayoría de los pools modernos deberían usar al menos ashift=12 (4K). Algunos entornos prefieren ashift=13 (8K) para ciertos dispositivos.
El desalineamiento es el asesino silencioso: si ZFS piensa que el sector del dispositivo es más pequeño que la realidad, puede hacer lectura-modificación-escritura a nivel de unidad. Ese es el tipo de problema de rendimiento que parece “picos aleatorios de latencia” y sobrevive meses de reuniones.
Regla empírica que raramente falla: asegúrate de que el ashift de tu pool sea correcto al crear el pool. No puedes cambiarlo después sin reconstruir. Luego elige volblocksize como un múltiplo del tamaño de sector (4K, 8K, 16K…). La mayoría de despliegues trata 4K u 8K como la base segura para discos VM que hacen escrituras aleatorias.
Elegir volblocksize para cargas reales de VM
Hablemos de opciones, no de ideología.
Puntos de partida comunes
- Discos OS de propósito general:
volblocksize=8Kes un compromiso común: no es muy pesado en metadatos, ni propenso a amplificación.4Kpuede ser excelente para cargas sensibles a latencia y mixtas, pero puede costar más metadatos y CPU a escala. - Discos VM para bases de datos (escrituras aleatorias intensas): empieza en
8Ko16Kdependiendo del tamaño de página de la BD y del I/O observado. Si no sabes,8Ksuele ser un valor por defecto más seguro que128K. - Volúmenes orientados a secuencial (objetivos de backup dentro de VMs, medios, almacenes de objetos grandes):
64Ko128Kpueden tener sentido si la carga es realmente secuencial grande y no realiza muchas reescrituras aleatorias pequeñas.
Aquí está la parte que la gente salta: no eliges volblocksize por lo que “le gusta” a ZFS, lo eliges por lo que hace realmente tu invitado. Si tu invitado hace escrituras aleatorias de 4K todo el día, darle una unidad de reescritura de 128K es básicamente inscribirte a trabajo y variación innecesarios.
Mirrors vs RAIDZ cambia las apuestas
En mirrors, las escrituras aleatorias suelen ser más amigables. En RAIDZ, las escrituras aleatorias pequeñas son más costosas debido a la paridad y patrones de lectura-modificación-escritura en el nivel de vdev. Un volblocksize demasiado pequeño en RAIDZ puede ser castigador. Un volblocksize demasiado grande puede ser castigador de otra forma (amplificación). El valor “correcto” es más sensible en RAIDZ.
Si ejecutas almacenamiento de VM sobre RAIDZ y te importa la latencia, ya estás jugando en modo difícil. Puede funcionar, pero el tuning y el margen de capacidad deben ser aburridamente disciplinados.
Tres mini-historias del mundo corporativo
1) Incidente causado por una suposición errónea: “ZFS lo manejará”
Estaban migrando una plataforma SaaS interna y muy ocupada desde un SAN legado a un clúster de virtualización respaldado por ZFS. El equipo hizo lo sensato: vdevs en mirror, RAM decente, SSDs y un SLOG separado porque sabían que la base de datos hacía fsync con agresividad. El piloto se veía bien. El corte estaba programado, el riesgo evaluado, el ticket de cambio aprobado y el café preparado.
La suposición era simple: “ZFS es inteligente; se adaptará.” Los zvols se crearon con un volblocksize grande porque alguien recordó que a ZFS le gustan los bloques grandes para throughput, y la diapositiva del vendor tenía un gráfico que premiaba el rendimiento secuencial. Nadie mapeó el perfil real de I/O de la VM al tamaño de bloque.
El incidente no ocurrió inmediatamente. Esperó hasta que el sistema estuvo tiempo suficiente para acumular snapshots, churn y fragmentación del mundo real. Entonces, durante una ventana de batch rutinaria, la latencia de commit de la base de datos empezó a tambalearse. Los hilos de aplicación se acumularon. Los reintentos se multiplicaron. El pool no estaba “caído”, simplemente lento de la forma que hace todo lo demás parecer roto.
On-call miró CPU (bien), red (bien), el SLOG (bien) y todavía vio picos de latencia en escrituras sync. El avance vino cuando alguien ejecutó rápido zfs get volblocksize y lo comparó con los tamaños de I/O del invitado desde iostat y una breve ejecución de fio. Los invitados hacían muchas escrituras de 8K y 16K; la unidad de reescritura del zvol era mucho mayor. Bajo carga, cada pequeño commit arrastraba un tren de reescritura de bloques mayor.
La solución no fue un toggle. Tuvieron que crear zvols nuevos con un volblocksize más sensato y migrar discos en vivo donde fue posible, y con downtime donde no. Fue una noche larga, pero útil: ZFS es inteligente, sí. No es clarividente.
2) Una optimización que salió mal: “Vamos 4K en todas partes”
En otro lugar, problema distinto. Tenían una flota sensible a latencia de VMs pequeñas (workers de CI, agentes de build, bases de datos de prueba) y querían mejor IOPS. Alguien leyó que 4K volblocksize mejora el rendimiento de escritura aleatoria, y decidieron estandarizarlo. Sin excepciones. La consistencia reconforta, especialmente en entornos corporativos donde cada excepción se vuelve una reunión.
La primera semana se vio genial. Los benchmarks mejoraron. Los dashboards sonrieron. La gente chocó las palmas en Slack con ese emoji que usas cuando no quieres que la gerencia note que cambiaste algo importante.
Luego la realidad llegó en forma de presión de CPU y memoria. Los nodos de almacenamiento empezaron a pasar más tiempo en metadatos y trabajo de checksums. El churn de ARC aumentó porque el working set contenía muchos más bloques y punteros. Algunos hosts chocaron contra un muro donde el pool no estaba saturado en ancho de banda, pero la finalización de I/O se ralentizó porque la ruta de software hacía más por operación.
No falló catastróficamente; falló burocráticamente. Los desarrolladores se quejaron de que los builds “a veces son lentos”. Los trabajos de prueba se volvieron erráticos. Los SRE pasaron días persiguiendo “vecinos ruidosos” fantasma antes de notar el hilo común: un tamaño de bloque 4K uniforme en cargas que incluían mucho I/O secuencial y actividad de archivos grandes.
La política eventual se volvió más madura: 4K para zvols probados como intensivos en escritura aleatoria; 8K o 16K para discos OS generales; más grandes sólo para volúmenes claramente secuenciales. La lección no fue “4K es malo”. La lección fue que la dogma es cara.
3) La práctica aburrida pero correcta que salvó el día: “Perfiles estándar + ruta de migración”
Este equipo ya había sido quemado antes, así que hicieron algo poco fashion: crearon un pequeño conjunto de perfiles de almacenamiento y los convirtieron en estándar. Cada perfil tenía un volblocksize documentado, configuración de compresión, expectativas de sync/logbias y una breve razón. Nada exótico. Sólo decisiones escritas antes del outage.
Cuando se aprovisionaba una VM nueva, el solicitante elegía “general”, “db-heavy” o “secuencial”. Si no elegían, por defecto era general. Eso por sí solo previno muchas malas combinaciones accidentales.
Pero la verdadera victoria fue la ruta de migración. Aceptaron que volblocksize es efectivamente inmutable para datos existentes y construyeron músculo operativo para mover discos: crear nuevo zvol con el volblocksize deseado, replicar o copiar a nivel bloque, cortar, validar, destruir el viejo. Lo practicaron en horas normales, no como improvisación desesperada durante una crisis.
Así que cuando llegó una VM appliance de un vendor con una carga sync pesada que empezó a dañar el clúster, la solución no fue una sala de guerra. Fue una migración planificada usando un perfil “latencia-sync” conocido y un corte controlado. El día fue salvado por el aburrimiento: un procedimiento repetible y la disciplina de usarlo.
Tareas prácticas: comandos, salidas e interpretación
Estas son las tareas que realmente ejecuto cuando alguien dice “el almacenamiento de VM está lento” y la única pista es una captura de pantalla de una gráfica roja. Los comandos asumen un host Linux con utilidades ZFS. Ajusta nombres de pool y zvol según haga falta.
Tarea 1: Identificar si el disco de la VM es un zvol y encontrar su nombre 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
Interpretación: Estás tratando con un zvol. Bien—volblocksize aplica. El dispositivo de respaldo aquí es /dev/zd0.
Tarea 2: Comprobar volblocksize (y algunas propiedades relacionadas)
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
Interpretación: 128K es grande para un disco VM general a menos que sepas que es mayormente secuencial. La compresión está activada (lz4), lo que puede ayudar o no. sync=standard significa que los flushes del invitado importan.
Tarea 3: Confirmar ashift del pool (base de alineamiento)
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
52: vdev_tree:
76: ashift: 12
104: ashift: 12
Interpretación: ashift=12 (sectores de 4K) es una base sensata. Si ves ashift=9 en discos modernos, probablemente has encontrado un problema fundamental.
Tarea 4: Vigilar rápidamente tamaños de I/O y latencia en el host
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
Interpretación: Tamaño medio de petición de escritura ~16K, no 128K. Si volblocksize es 128K, espera churn adicional en escrituras pequeñas, especialmente bajo presión sync. w_await en ~18ms sugiere dolor de latencia ya.
Tarea 5: Observar latencia a nivel ZFS con iostat por 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
Interpretación: El ancho de banda no es grande, pero las operaciones sí. Eso es típico de I/O aleatorio de bloques pequeños. Si ves ops altas y MB/s moderados, las decisiones sobre el tamaño de bloque importan.
Tarea 6: Comprobar presión de txg sync (¿los commits están tardando demasiado?)
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
Interpretación: Los campos exactos varían por plataforma, pero el objetivo es detectar retrasos en txg y si acumulas datos sucios más rápido de lo que puedes syncar. Si los tiempos de sync de txg suben durante tus picos de latencia, estás en territorio de “presión de commits”.
Tarea 7: Verificar si un zvol usa un log separado (SLOG) de forma efectiva
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
Interpretación: Hay un SLOG en espejo. Genial. Pero recuerda: un SLOG ayuda sólo para escrituras sync. Si tu carga es asíncrona o el cuello de botella está en otro sitio, no te salvará.
Tarea 8: Comprobar si el invitado está forzando sync (o si tú lo forzaste accidentalmente)
cr0x@server:~$ zfs get -H -o property,value sync tank/vm-101-disk-0
sync standard
Interpretación: standard significa que ZFS respeta la semántica O_SYNC/fsync/flush. Si alguien puso sync=always, la latencia aumentará dramáticamente para muchas cargas VM. Si alguien puso sync=disabled, la latencia se ve genial hasta que pierdes energía y tienes un día muy educativo.
Tarea 9: Crear un zvol de prueba con un volblocksize candidato (no cambies prod a ciegas)
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
Interpretación: Usa un zvol de prueba para benchmarkear con fio desde una VM o el host. Elegir con datos vence elegir con opiniones.
Tarea 10: Ejecutar un fio rápido de escritura aleatoria contra el zvol (lado host)
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
Interpretación: Mira percentiles, no sólo IOPS. Si tu percentil 99.9 es feo, tu aplicación eventualmente se quejará.
Tarea 11: Comparar con un zvol de prueba de volblocksize mayor (mismo trabajo 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]
Interpretación: Misma carga de 8K, cola de latencia mucho peor y menos IOPS. Ahí se ve el impuesto de amplificación.
Tarea 12: Comprobar uso lógico/físico del zvol (efectos de compresión y 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
Interpretación: Los bloques mayores no comprimieron mejor aquí; usaron más espacio. Las cargas reales varían, pero esto es exactamente por qué mides.
Tarea 13: Confirmar si un zvol es sparse (thin) y si refreservation lo está enmascarando
cr0x@server:~$ zfs get -H -o property,value volsize,volmode,refreservation tank/vm-101-disk-0
volsize 500G
volmode default
refreservation none
Interpretación: El comportamiento de thin provisioning depende del entorno. Refreservation puede evitar sorpresas de “sin espacio” a costa de eficiencia de capacidad.
Tarea 14: Medir la distribución real de tamaños de I/O del invitado (muestreo en host)
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]
Interpretación: Las “+ 16” en sectores aquí equivalen a 8K (16 × 512B) en un esquema de reporte de 512B. Esto te dice lo que la VM está emitiendo realmente, no lo que desearías que emitiera.
Tarea 15: Planificar una migración segura a un nuevo volblocksize (crear + copiar)
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
Interpretación: Este es el instrumento tocho. Requiere downtime (o al menos una estrategia de snapshots consistente en el hipervisor). Pero es fiable y mantiene la semántica del dispositivo de bloques clara.
Guion de diagnóstico rápido
Cuando la latencia es alta y la gente grita, necesitas una secuencia corta que reduzca el espacio de búsqueda rápido. Aquí está la mía para VMs respaldadas por zvols ZFS.
Primero: ¿El pool está realmente saturado o simplemente “lento”?
- Ejecuta
zpool iostat -v 1y mira ops y bandwidth. - Comprueba si un vdev está más caliente que los demás (el desequilibrio puede parecer “latencia aleatoria”).
- Mira utilización alta con throughput moderado: eso suele indicar I/O aleatorio pequeño o presión de sync.
Segundo: ¿Es esto dolor por escrituras sync?
- Comprueba
zfs get sync,logbiasen el zvol. - Verifica si existe un SLOG y está sano (
zpool status). - Correlaciona picos de latencia con presión de commit de txg (kstats específicos de plataforma; en Linux, SPL kstats y logs del sistema ayudan).
Tercero: ¿Coincide volblocksize con el perfil de I/O?
- Revisa
zfs get volblocksizepara el zvol afectado. - Muestra tamaños reales de I/O con
iostat -xen el dispositivo del zvol y, si hace falta,blktrace. - Si las escrituras del invitado son mayormente 4K–16K y volblocksize es 64K–128K, asume amplificación hasta demostrar lo contrario.
Cuarto: ¿Estás limitado por capacidad/fragmentación?
- Revisa el llenado del pool:
zfs list/zpool list. El uso alto aumenta fragmentación y ralentiza asignaciones. - Busca trabajo en segundo plano: estado de scrub/resilver en
zpool status. - Cuenta snapshots: un número grande puede aumentar la sobrecarga de metadatos y ralentizar algunas operaciones.
Quinto: Validar con un micro-benchmark controlado
- Crea un zvol de prueba con valores candidatos de volblocksize.
- Ejecuta fio con tamaños de bloque que coincidan con la carga de la VM y mide latencia en cola.
- No midas en un pool ya al rojo vivo a menos que te gusten los tickets autoinfligidos.
Errores comunes, síntomas y soluciones
Error 1: Usar volblocksize 128K para discos VM con escrituras aleatorias
Síntomas: Gran throughput secuencial, pero bases de datos hacen timeout bajo carga mixta; alta latencia 99/99.9; IOPS más bajos de lo esperado; “todo está bien hasta los backups”.
Solución: Migra a un zvol nuevo con volblocksize=8K o 16K (según la carga). Valida con fio y métricas de la app real. No esperes que un cambio de propiedad in-place vuelva a bloquear los datos existentes.
Error 2: Fijar volblocksize 4K en todas partes sin chequear coste de CPU/metadatos
Síntomas: IOPS aleatorio mejora, pero hosts de almacenamiento muestran más CPU de sistema; ARC churn; jitter en rendimiento secuencial; “es rápido pero inconsistente”.
Solución: Usa perfiles. Mantén 4K para discos realmente intensivos en escritura aleatoria; usa 8K/16K para propósito general; mayores sólo para volúmenes secuenciales conocidos.
Error 3: Confundir tuning de dataset (recordsize) con tuning de zvol (volblocksize)
Síntomas: Alguien pone recordsize=16K en el dataset padre esperando que el rendimiento del disco VM cambie. Nada cambia; la culpa viaja hacia arriba.
Solución: Los zvols no son archivos en un dataset de la misma manera. Usa zfs get volblocksize en el zvol mismo.
Error 4: Usar sync=disabled para “arreglar latencia”
Síntomas: Las gráficas de latencia se ven increíbles; la gerencia feliz; luego ocurre un apagado no limpio y la base de datos necesita reparación o pierde transacciones recientes.
Solución: Mantén sync=standard por corrección. Si necesitas rendimiento en sync, arregla el SLOG, la disposición del pool y el alineamiento de volblocksize con la carga.
Error 5: Ignorar llenado del pool y fragmentación
Síntomas: La misma carga se vuelve más lenta con los meses; la latencia de escritura va subiendo; las asignaciones se vuelven costosas; “ZFS fue rápido al comienzo”.
Solución: Mantén margen de capacidad. Planifica expansiones antes del pánico. Trata snapshots y retención como un presupuesto, no como un hobby.
Error 6: Benchmarking con el tamaño de bloque equivocado y llamarlo “prueba”
Síntomas: fio muestra MB/s enormes con escrituras secuenciales de 1M, pero en producción todo es lento; surgen discusiones.
Solución: Mide con los tamaños de bloque y semántica sync que usa tu carga VM. Incluye percentiles de latencia y patrones mixtos de lectura/escritura.
Listas de verificación / plan paso a paso
Checklist A: Aprovisionamiento de disco VM nuevo (respaldado por zvol)
- Clasifica la carga: general OS, DB-heavy con escrituras aleatorias o secuencial-heavy.
- Elige un perfil:
- General:
volblocksize=8K(a menudo) concompression=lz4 - DB-heavy:
8Ko16K; prueba si puedes - Secuencial-heavy:
64Ko128Ksi es realmente secuencial
- General:
- Crea el zvol explícitamente (no confíes en defaults que no has auditado).
- Confirma propiedades con
zfs get. - Documenta la elección en los metadatos/ticket de la VM para que el tú del futuro no tenga que excavar arqueológicamente.
Checklist B: Cuando sospechas que volblocksize es incorrecto
- Confirma el
volblocksizeactual del zvol. - Mide tamaños reales de I/O (
iostat -x, opcionalmenteblktrace). - Crea un zvol de prueba con un volblocksize candidato más pequeño/mayor.
- Benchmarkea con fio usando tamaños de bloque que coincidan con la carga.
- Haz un plan de migración:
- Ventana de downtime o herramientas de migración en vivo
- Método de copia a nivel bloque (dd, replicación a nivel hipervisor, o copia a nivel guest)
- Pasos de validación (chequeo de sistema de archivos, comprobaciones de la app, pruebas de rendimiento)
- Corta, monitoriza latencia en cola y luego desmantela el zvol antiguo.
Checklist C: “Salvaguardas aburridas” para almacenamiento VM en ZFS
- Mantén la utilización del pool sensata; evita vivir cerca del lleno.
- Programa scrubs, pero no los solapes con tus ventanas de escritura más pesadas a menos que quieras probar resiliencia.
- Monitorea percentiles de latencia, no sólo promedios.
- Estandariza un pequeño conjunto de perfiles de volblocksize y cúmplelos.
- Practica el procedimiento de migración antes de necesitarlo.
Preguntas frecuentes
1) ¿Cuál es el volblocksize por defecto y debo confiar en él?
Los valores por defecto varían por plataforma y versión, y pueden no estar alineados con tu carga VM. Trata los defaults como “seguros para alguien”, no “óptimos para ti”. Siempre revisa qué usa tu entorno y prueba contra tu perfil de I/O.
2) ¿Puedo cambiar volblocksize en un zvol existente sin migración?
A menudo puedes cambiar el valor de la propiedad, pero eso no reescribirá retroactivamente los bloques existentes al nuevo tamaño de forma limpia e instantánea. En la práctica, si necesitas el comportamiento de rendimiento de otro volblocksize, planea crear un zvol nuevo y migrar los datos.
3) ¿4K es siempre lo mejor para discos VM?
No. 4K puede ser excelente para cargas intensivas en escritura aleatoria y latencia predecible, pero incrementa la sobrecarga de metadatos y trabajo de CPU. Para discos VM de propósito general, 8K o 16K suele ser un equilibrio mejor.
4) ¿Cómo se relaciona volblocksize con el tamaño de bloque del sistema de archivos del invitado?
Son capas independientes. El tamaño de bloque del invitado influye en el I/O que emite. El volblocksize del zvol influye en cómo ZFS almacena y reescribe esas escrituras. Cuando están desajustados (p. ej., invitado escribe páginas de 8K, zvol usa bloques de 128K), puedes crear amplificación y variación de latencia.
5) Si tengo un pool NVMe rápido, ¿sigue importando volblocksize?
Sí. NVMe hace que la latencia sea lo bastante baja como para que el overhead de software y la amplificación se vuelvan visibles. Puedes desperdiciar el rendimiento de NVMe con una mala elección de tamaño de bloque, especialmente en cargas sync o de escrituras aleatorias.
6) ¿La compresión cambia el volblocksize recomendado?
Pude hacerlo. La compresión puede reducir escrituras físicas y a veces suavizar el coste de bloques mayores, pero depende de la carga. Datos cifrados o ya comprimidos a menudo no se benefician. Mide compressratio y percentiles de latencia bajo carga realista.
7) ¿Y RAIDZ—me empuja a volblocksize mayor o menor?
RAIDZ hace que las escrituras aleatorias pequeñas sean más caras por paridad y comportamiento de lectura-modificación-escritura. Eso no significa “usa siempre bloques enormes”, pero sí que debes tener más precaución y probar. Los mirrors suelen ser más amigables para I/O aleatorio de VM.
8) ¿Puede un SLOG arreglar malas elecciones de volblocksize?
Un buen SLOG puede mejorar dramáticamente la latencia de escrituras sync, pero no eliminará la amplificación y los efectos de fragmentación de un volblocksize mal emparejado. Piensa en SLOG como “te ayuda a confirmar sync de forma segura”, no como “arregla escrituras ineficientes”.
9) Mi hipervisor usa QCOW2 u otro formato de imagen—¿sigue importando volblocksize?
Sí, pero la pila se vuelve más compleja. Los formatos de imagen pueden introducir su propia granularidad de asignación y sobrecarga de metadatos, lo que puede agravar el comportamiento de ZFS. Si te importa mucho la latencia predecible, los volúmenes raw son más simples de razonar y volblocksize se vuelve una palanca más clara.
10) ¿Cuál es el volblocksize más seguro si debo estandarizar uno solo?
Si te fuerzan a elegir uno para discos VM generales sin conocer las cargas, 8K es una opción común “menos mala” en muchos entornos. Pero la respuesta honesta es: estandariza perfiles, no un único valor.
Conclusión
volblocksize no es glamoroso, y eso es exactamente por lo que muerde. Es una decisión de bajo nivel que de forma silenciosa modela cuánto trabajo hace tu stack de almacenamiento por cada escritura de VM, cuán estable es tu latencia bajo presión y qué tan rápido tu “pool rápido” se convierte en una disculpa.
El enfoque operacionalmente maduro es también el más sencillo: mide tamaños de I/O reales, elige un pequeño conjunto de perfiles sensatos, benchmarkea latencia en cola y acepta que cambiar volblocksize suele ser un proyecto de migración—no un toggle. Haz eso, y obtendrás lo que ZFS hace genuinamente bien: corrección predecible, buen rendimiento y un comportamiento de almacenamiento que puedes explicar a otra persona durante un incidente.