Problema
En entornos de virtualización con Proxmox es frecuente mezclar pools ZFS de diferentes tecnologías (SSD para arranque y VM, HDD para backup). Cuando una de esas pools recibe una carga de escritura intensa, el host y todas las VMs pueden quedar sin respuesta. El síntoma típico es un “I/O Pressure stall” que se aproxima al 100 % y que persiste hasta que la operación de disco finaliza o se aborta. El efecto colateral es la pérdida de conectividad (por ejemplo, un pfSense que deja de enrutar) y la aparente congelación del hipervisor.
Este comportamiento no está limitado a un modelo de hardware concreto; se reproduce en servidores con CPUs de alto número de hilos, grandes cantidades de RAM y configuraciones de ZFS que incluyen discos externos (USB‑UAS) o SSD de consumo. El problema se vuelve crítico cuando la carga de I/O proviene de distintas pools simultáneamente, porque el subsistema de ZFS compite por recursos comunes (memoria ARC, hilos de ZVOL, scheduler del kernel).
Causa
-
Presión del ARC
ZFS usa la RAM como caché de lectura/escritura (ARC). Cuando la carga de escritura supera la capacidad de la caché, el ARC se vuelve “agresivo”, expulsando datos y generando thrashing. En servidores con 64 GB de RAM, un ARC de 16 GB puede ser suficiente, pero si la carga de escritura es sostenida y los discos son lentos (HDD USB), el ARC consume la mayor parte de la RAM y deja poco espacio para el resto del sistema, provocando stalls. -
Límites de hilos de ZVOL
Cada zvol (disco virtual) utiliza un pool de hilos configurable mediantezvol_threads. Si el número de hilos es insuficiente para la concurrencia de VMs, las peticiones de I/O se encolan y el kernel marca presión de I/O. El valor por defecto suele ser 8 hilos por zvol; en máquinas con 32 hilos de CPU ese número puede ser demasiado bajo. -
Sin SLOG dedicado
Los discos SSD de consumo usados como pool de arranque carecen de una capa de ZIL (SLOG) optimizada. Cuando una VM escribe de forma síncrona, ZFS espera a que el dato llegue al disco. En un SSD de baja resistencia o en un disco externo USB, la latencia de la ZIL se dispara y bloquea el flujo de I/O. -
Scheduler y cola de I/O del kernel
El scheduler predeterminado (mq-deadlineobfq) puede no ser el más adecuado para mezclas de SSD y HDD. Además, la cola de I/O del bloque (/sys/block/<dev>/queue/nr_requests) está limitada y se llena rápidamente bajo carga pesada, lo que genera stalls visibles eniostat. -
Contención entre pools
Aunque los pools están físicamente separados, comparten el mismo bus PCIe/USB y la misma CPU. Un pico de I/O en la pool HDD (por ejemplo, una copia masiva) puede saturar el controlador USB y afectar indirectamente a la pool SSD, provocando que todas las VMs experimenten latencia.
Solución
1. Ajustar el tamaño del ARC
Limitar el ARC a un valor que deje suficiente RAM libre para el hipervisor y los procesos de las VMs. En un host con 64 GB, un rango de 12‑14 GB suele ser seguro.
echo 15000000000 > /sys/module/zfs/parameters/zfs_arc_max # 15 GB
Para hacerlo permanente, añade a /etc/modprobe.d/zfs.conf:
options zfs zfs_arc_max=15000000000
2. Incrementar los hilos de ZVOL
Aumentar zvol_threads a un número que se aproxime a la mitad de los hilos de CPU disponibles. En un CPU de 32 hilos, 16 hilos por zvol es un buen punto de partida.
echo 16 > /sys/module/zfs/parameters/zvol_threads
Persistente en /etc/modprobe.d/zfs.conf:
options zfs zvol_threads=16
3. Añadir un SLOG de alta calidad
Instalar un pequeño SSD NVMe empresarial (por ejemplo, 400 GB) y asignarlo como SLOG para la pool que aloja los discos de VM. El comando:
zpool add NAS2 log /dev/nvme0n1p1
Esto desvía los writes síncronos al SLOG, reduciendo la latencia de la ZIL.
4. Optimizar los parámetros de I/O del kernel
Ajustar la profundidad de la cola y el scheduler para los dispositivos críticos:
echo 256 > /sys/block/nvme0n1/queue/nr_requests
echo mq-deadline > /sys/block/nvme0n1/queue/scheduler
Repetir para los discos HDD (/dev/sdX). En algunos casos bfq ofrece mejor aislamiento entre workloads mixtas.
5. Revisar la configuración de los discos de VM
- Cache mode: usar
writebackpara discos en SSD ywritethroughpara discos en HDD. - IOThread: habilitar
io_thread=1en la definición de la VM cuando el disco está en un pool ZFS. - Throttle: aplicar límites de IOPS o throughput mediante
qm set <vmid> -disk iops=5000(ombps) si la carga es predecible.
6. Separar workloads de alta y baja prioridad
Crear dos pools distintas: una con SSD + SLOG para VMs críticas y otra con HDD para backup. Montar los discos de backup con recordsize=1M y compression=off para minimizar la sobrecarga de metadatos.
7. Monitoreo continuo
Instalar zfs-stats o zpool iostat -v 5 y observar los valores de io_stall. Si el promedio supera 50 % durante más de 30 s, la configuración necesita ajuste.
Cuándo aplicar esta solución
- Síntomas: I/O stall > 80 % en
zpool iostat, VMs sin respuesta, pérdida de conectividad de servicios críticos. - Entorno: Proxmox con pools ZFS mixtos (SSD + HDD), al menos 8 GB de RAM reservados para el host, y carga de escritura sostenida (copias de backup, bases de datos, contenedores).
- No aplica: Cuando el host ya está usando discos empresariales con SLOG dedicado y el ARC está configurado por debajo del 10 % de la RAM; en ese caso el problema suele estar en la aplicación o en la red, no en ZFS.
Código
# 1. Limitar ARC a 14 GB
echo 14000000000 > /sys/module/zfs/parameters/zfs_arc_max
# 2. Incrementar hilos de ZVOL a 16
echo 16 > /sys/module/zfs/parameters/zvol_threads
# 3. Añadir SLOG (SSD NVMe) a la pool NAS2
zpool add NAS2 log /dev/nvme0n1p1
# 4. Ajustar cola y scheduler del SSD
echo 256 > /sys/block/nvme0n1/queue/nr_requests
echo mq-deadline > /sys/block/nvme0n1/queue/scheduler
# 5. Configurar VM disk cache y IOThread (ejemplo VMID 101)
qm set 101 -scsi0 local-zfs:vm-101-disk-0,cache=writeback,io_thread=1
Verificación
- Ejecutar
zpool iostat -v 5y observar queio_stallse mantiene bajo 30 % durante operaciones intensas. - Verificar que la RAM libre después del ARC sea al menos 20 % del total (
free -h). - En la VM, lanzar una carga de escritura (
dd if=/dev/zero of=/mnt/disk/test bs=1M count=10240 oflag=direct) y comprobar que la latencia no supera 100 ms. - Revisar los logs de Proxmox (
journalctl -u pvedaemon) en busca de mensajes de “I/O stall” o “zvol thread limit”.
Notas adicionales
- Evita colocar pools críticos en cajas USB; la latencia del controlador puede anular cualquier ajuste de ZFS.
- Si el host tiene más de 128 GB de RAM, considera usar
zfs_arc_maxdel 20‑25 % del total para mantener suficiente espacio de caché sin comprometer la memoria del hipervisor. - En entornos con muchas VMs pequeñas, la opción
zfs_txg_timeout=5puede reducir la duración de los grupos de transacciones y mejorar la reactividad. - Cuando uses discos de consumo como SLOG, monitoriza el desgaste (
smartctl -a /dev/nvme0n1) y reemplázalos antes de que el nivel de vida caiga por debajo del 20 %.