Los problemas de rendimiento en Proxmox casi nunca aparecen como un único error claro. Se manifiestan como “la base de datos se siente lenta”, “RDP va con retardo”, “nodos de Kubernetes se desconectan” o “¿por qué la carga del host es 60 cuando la CPU está al 10%?” Obtendrás gráficos contradictorios y teorías seguras de todos en la sala.
Esta es una guía de campo para romper el punto muerto. Separaremos CPU steal de IO wait, mapearemos el comportamiento de ZFS ARC a latencia real y rastrearemos la VM vecina ruidosa que está convirtiendo tu nodo en un microondas compartido de oficina.
Guion de diagnóstico rápido
No necesitas un tablero de 40 paneles para encontrar el cuello de botella. Necesitas las tres comprobaciones correctas, en el orden correcto, con la disciplina para dejar de adivinar.
Primero: confirma qué tipo de “lentitud” es (contención de CPU vs almacenamiento vs presión de memoria)
- El host se siente lento pero la CPU parece baja: sospecha IO wait, steal time o recuperación de memoria (swap/kswapd).
- La CPU del invitado está al máximo pero la del host no: sospecha límites de CPU, steal time o desajuste de topología vCPU.
- Todo está “bien” hasta que comienza un backup/replicación: sospecha saturación de IO y comportamiento de grupos de transacciones de ZFS.
Segundo: identifica el limitador en el host (one-liners que no mienten)
Ejecuta esto en el nodo Proxmox:
- Snapshot de CPU + steal + iowait:
mpstat -P ALL 1 5 - Presión de memoria / swap / reclaim:
vmstat 1 10 - Latencia de disco y colas:
iostat -x 1 10 - Comportamiento del pool ZFS:
zpool iostat -v 1 10
Tercero: mapea al VM o carga culpable
- VM(s) con mayor CPU:
qm list+ps -eo pid,cmd,%cpu --sort=-%cpu | head+ correlacionar PIDs de QEMU - VM(s) con mayor IO:
iotop -oPaypvesh get /nodes/$(hostname)/qemu --output-format json - Ofensor por presión de memoria: métricas de ballooning y swap del host, además de “quién está asignando page cache”
Si sólo haces una cosa: elige un host, una ventana de 10 minutos y captura los snapshots anteriores. Esa es tu fuente de la verdad. Todo lo demás es opinión.
Un modelo mental que te mantiene honesto
Los problemas de rendimiento en Proxmox suelen ser una de cuatro cosas:
- Contención de CPU: las vCPU quieren ejecutarse; el scheduler no puede darles tiempo. En una VM a menudo verás esto como steal time.
- Latencia de almacenamiento: las CPU están inactivas porque los hilos esperan al disco. Eso es IO wait, y no es un problema de CPU: es un problema de la tubería de almacenamiento.
- Presión de memoria: ARC, page cache, memoria anónima y ballooning luchan por la RAM. Si el host hace swap, todo se vuelve “misteriosamente lento”.
- Efectos de vecinos ruidosos: una VM satura el IO, inunda la CPU con interrupciones o desencadena amplificación patológica de escrituras en ZFS que castiga a todos.
El truco es evitar mezclar síntomas. El load average incluye tareas ejecutables y tareas bloqueadas en sleep no interrumpible. Por eso puedes tener una carga de 40 y una CPU mayormente inactiva. No es que “Linux mienta”. Es que hiciste una pregunta vaga y obtuviste una respuesta honesta.
Una cita que deberías pegar en tu monitor: Parafraseando una idea de Deming: sin datos sólo eres otra persona con una opinión.
Es extremadamente relevante cuando tres equipos discuten si “es la red”.
Hechos interesantes e historia que puedes usar
- Steal time nació en la era temprana de la virtualización para cuantificar con qué frecuencia un invitado era ejecutable pero el hipervisor programaba a otro. Es un medidor de deuda de scheduling, no un bug del invitado.
- El load average de Linux precede a las pilas modernas de IO; cuenta tareas en estado D (sleep no interrumpible), así que la latencia alta de almacenamiento infla la carga incluso si las CPU descansan.
- ZFS ARC no es “solo caché”. Es un consumidor de memoria autoajustable con múltiples listas (MRU/MFU y compañeras), y tomará RAM hasta que algo lo fuerce a comportarse.
- ZFS se diseñó pensando primero en la integridad de datos (copy-on-write, checksums, semántica transaccional). El ajuste de rendimiento es real, pero siempre paga un impuesto de integridad.
- IOPS se convirtió en una métrica de negocio por la virtualización: cuando muchas corrientes pequeñas de IO aleatorio comparten discos, el throughput deja de ser el cuello de botella y la latencia domina.
- La amplificación de escritura no es solo para SSDs. Los sistemas de archivos copy-on-write también pueden amplificar escrituras, especialmente con escrituras sincrónicas pequeñas y fragmentación.
- Virtio no fue la opción por defecto al principio. Se introdujeron drivers paravirtualizados para evitar el overhead de emulación; usar el modelo de disco/controlador equivocado sigue perjudicando.
- La escalación de frecuencia de CPU puede imitar “rendimiento aleatorio”. Si los governors bajan agresivamente la frecuencia, tu “misma carga” cambia cada minuto.
- El ballooning resolvió la economía del overcommit pero introdujo un nuevo modo de fallo: el host parece “bien” mientras los invitados thrashan, porque el dolor se delega.
CPU steal: cuando los invitados están listos pero no pueden ejecutarse
CPU steal es tiempo en el que vCPU de una VM quería ejecutarse pero no pudo porque el scheduler del host (u otra capa) no la programó. En metal desnudo, el steal es básicamente cero. En virtualización, el steal es una confesión: “estoy contendido”.
Cómo aparece el steal (y cómo te engaña)
- Dentro de una VM,
topmuestra alto%st, pero%usno es exagerado. - Las aplicaciones hacen timeout aunque los gráficos de “uso de CPU” parezcan moderados.
- La latencia interactiva es mala: los pulsos de teclado en SSH se ven con retraso, los cron jobs se ejecutan tarde.
El steal no es “la VM usando demasiada CPU”. Es “la VM que no recibe CPU cuando la necesita”. Esa distinción importa al decidir si añadir núcleos, mover VMs o dejar de overcommitear.
Causas comunes de steal en Proxmox
- Overcommit demasiado agresivo: sum(vCPU) excede ampliamente los cores físicos y los picos de carga coinciden.
- Límites y shares de CPU: cuotas de cgroup o unidades de CPU en Proxmox que privan a una VM durante la contención.
- Desajuste NUMA/topología: VMs grandes que abarcan sockets, acceso a memoria remota y fallos de caché generan “CPU lenta” incluso sin mucho steal.
- Frecuencia de CPU del host fijada en bajo: parece contención, actúa como contención, pero es solo underclocking.
Consejo práctico: si ves steal sostenido por encima de unos pocos porcentajes durante un incidente visible para usuarios, trátalo como un problema real. El steal por ráfagas no es fatal; el steal sostenido sí lo es.
Broma #1: Si tu VM tiene 30% de steal, no está “robando”—está educadamente esperando mientras otro se roba su dinero del almuerzo.
IO wait: la CPU está inactiva porque el almacenamiento es lento
IO wait significa que la CPU no tenía nada para ejecutar porque los hilos estaban bloqueados en IO. No es que la CPU esté “ocupada”. Es que la CPU está desempleada mientras el almacenamiento arruina el fin de semana de todos.
Qué hace que IO wait se dispare en Proxmox
- Backups/snapshots/replicación que generan lecturas secuenciales intensas mezcladas con escrituras aleatorias.
- Escrituras sincronizadas de bases de datos o configuraciones NFS que forzan flushes.
- Saturación de profundidad de cola en SSDs SATA o arreglos HDD: la latencia explota antes de que el throughput parezca saturado.
- Comportamiento TXG de ZFS: ráfagas de escrituras en los límites de commit pueden parecer paradas periódicas.
- Almacenamiento thin-provisioned o pools casi llenos que producen fragmentación y asignaciones más lentas.
Interpretando iowait sin autoengaños
Un alto porcentaje de iowait te dice: “el almacenamiento está limitando el progreso”. No te dice si el culpable es el pool, el controlador, el firmware del SSD, el sistema de archivos del invitado o una sola VM martillando escrituras sincrónicas.
Concéntrate en métricas de latencia: await, svctm (menos útil en kernels modernos), tamaño de cola y comportamiento por-vdev de ZFS. Los gráficos de throughput son mentiras confortantes si tu carga es sensible a la latencia.
ZFS ARC: caché, presión de memoria y la trampa del swap
ZFS ARC es una característica potente de rendimiento y un chivo expiatorio frecuente. Cachea lecturas, metadata y puede reducir drásticamente el IO de disco. Pero en un host de virtualización, ARC también compite políticamente: compite con las VMs por RAM.
Modos de fallo de ARC que realmente ves en producción
- Swap en el host: el kernel hace swap porque ARC + page cache + procesos qemu + todo lo demás exceden la RAM. Una vez que el host intercambia, la latencia se vuelve rara y “aleatoria”.
- ARC demasiado pequeño: misses constantes en caché hacen que los discos reales hagan el trabajo; iowait sube; las VMs se quedan cortas.
- ARC demasiado grande: las VMs pierden memoria, entra el ballooning, los invitados empiezan a intercambiar y el host “parece bien” mientras la aplicación se quema.
- Presión de metadata: muchos archivos pequeños o muchos datasets pueden inflar metadata; ARC se vuelve metadata-intenso y menos efectivo para bloques reales de VM.
Guía de dimensionamiento ARC con opinión para Proxmox
Si ejecutas muchas VMs y quieres comportamiento predecible, fija un máximo de ARC. Dejar que ARC crezca automáticamente puede estar bien en un appliance de almacenamiento; en un nodo de virtualización con memoria de invitados cambiante, es así como terminas depurando “lentitud fantasma”.
No hay un porcentaje mágico, pero un punto de partida común es: limita ARC para que el host siempre tenga margen para guests + kernel + overhead de qemu. Luego observa ratios reales de hit en caché y latencia. Ajusta según evidencia, no sensaciones.
Nota sobre SLOG/L2ARC (porque la gente preguntará)
Un dispositivo SLOG separado puede ayudar la latencia de escrituras síncronas si tienes cargas intensivas en sync y entiendes los riesgos y la resistencia. L2ARC puede ayudar cargas de lectura intensiva pero consume RAM para metadata y puede salir mal si ya estás corto de memoria.
Vecinos ruidosos: encontrar la VM que arruina tu día
“Vecino ruidoso” es la jerga corporativa para “una carga es egoísta y todos los demás la pagan”. En Proxmox, los vecinos ruidosos típicamente se manifiestan como:
- Una VM generando escrituras aleatorias extremas (a menudo escrituras síncronas).
- Una VM ejecutando un escaneo de disco completo, trabajo de indexado, ejecución de antivirus o backup interno.
- Una VM saturando la CPU con altas tasas de interrupciones (inundaciones de paquetes, polling mal configurado, bucles ocupados).
- Una VM con demasiadas vCPUs causando fricción de scheduling para VMs más pequeñas.
La jugada diagnóstica: deja de mirar promedios del host y empieza a atribuir consumo a procesos QEMU individuales, luego correlaciónalos con VMIDs. La mayoría de la “contención misteriosa” se convierte en un nombre y un responsable en 15 minutos.
Tareas prácticas: comandos, salidas, decisiones (12+)
Tarea 1: Snapshot de uso de CPU, iowait y steal por núcleo
cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.8.12-pve (pve01) 12/28/2025 _x86_64_ (32 CPU)
12:01:11 PM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:01:12 PM all 12.10 0.00 4.20 9.80 0.00 1.10 6.50 66.30
12:01:12 PM 0 22.00 0.00 6.00 18.00 0.00 2.00 10.00 42.00
12:01:12 PM 1 8.00 0.00 3.00 1.00 0.00 0.50 0.00 87.50
Qué significa: %steal en 6.5% en todas las CPUs sugiere contención de scheduling para invitados. %iowait en 9.8% también sugiere que la latencia de almacenamiento está limitando el progreso.
Decisión: Si el steal se mantiene > 5% durante el dolor, trata la contención de CPU como un problema real: reduce el overcommit de vCPU, mueve VMs calientes o ajusta límites. Si iowait también es alto, ejecuta tareas de almacenamiento para determinar cuál es primario.
Tarea 2: Confirmar presión de memoria y swapping en el host
cr0x@server:~$ vmstat 1 10
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
5 3 262144 18432 10240 8123456 80 120 900 1400 9200 18000 12 4 66 10 8
3 2 262144 16000 10240 8012345 60 100 1100 1600 9400 17500 10 4 67 12 7
Qué significa: Si si/so no son cero indica swapping activo. b (procesos bloqueados) se alinea con IO wait. Tener poco free solo no es malo; lo que importa es el intercambio.
Decisión: Si el host está intercambiando, arregla la presión de memoria primero: capea ARC, deja de jugar al overcommit con ballooning, añade RAM o reduce asignaciones de invitados. La optimización de almacenamiento no te salvará si el host está haciendo paging.
Tarea 3: Identificar síntomas de latencia y saturación de disco
cr0x@server:~$ iostat -x 1 5
Linux 6.8.12-pve (pve01) 12/28/2025 _x86_64_ (32 CPU)
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await r_await w_await
nvme0n1 1200 1800 48000 92000 0.0 0.0 98.5 18.2 6.1 26.4
nvme1n1 110 1600 3200 88000 0.0 0.0 91.0 22.8 4.5 24.1
Qué significa: Alto %util más await elevado indica que tus dispositivos están saturados y la latencia está subiendo. La latencia de escritura es especialmente alta.
Decisión: Mueve al desglose a nivel ZFS (zpool iostat -v). Si los picos de await coinciden con backups o replicación, limita/filtra esos trabajos.
Tarea 4: Desglosa ZFS IO por vdev para encontrar el verdadero limitador
cr0x@server:~$ zpool iostat -v rpool 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
rpool 3.12T 1.48T 1.20K 1.85K 46.2M 92.8M
mirror 3.12T 1.48T 1.20K 1.85K 46.2M 92.8M
nvme0n1 - - 620 980 23.1M 46.4M
nvme1n1 - - 580 870 23.0M 46.4M
Qué significa: Lecturas/escrituras balanceadas entre miembros del mirror, así que el pool no es “un disco muriendo” (todavía). Si un vdev va atrás, verías sesgo.
Decisión: Si ancho de banda y operaciones son altas pero la latencia sigue mala, busca escrituras síncronas, fragmentación o presión de memoria causando misses constantes en caché.
Tarea 5: Comprobar salud del pool, errores y dispositivos lentos
cr0x@server:~$ zpool status -xv
all pools are healthy
Qué significa: No hay errores obvios de ZFS. Eso es bueno. No significa que el rendimiento sea bueno.
Decisión: Si el rendimiento es malo pero la salud es buena, céntrate en la forma de la carga, latencia y contención en lugar de “un disco está fallando”.
Tarea 6: Observar tamaño de ARC y señales de hit ratio
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:03:01 4.2K 1.1K 26 210 19 540 49 350 32 52.1G 60.0G
12:03:02 4.0K 1.4K 35 260 19 700 50 440 31 52.4G 60.0G
Qué significa: ARC es grande (52G) y está limitado a 60G. Una tasa de misses del 26–35% sugiere que los discos aún están haciendo trabajo. No es necesariamente malo, pero es una pista.
Decisión: Si miss% es alto y iowait también lo es, considera si ARC es demasiado pequeño para el working set. Si el host está intercambiando, ARC es demasiado grande para tu realidad.
Tarea 7: Confirmar swap y contabilidad de memoria en el host (no confíes sólo en “free”)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 128Gi 109Gi 18Gi 2.1Gi 1.6Gi 15Gi
Swap: 16Gi 256Mi 15Gi
Qué significa: Hay algo de swap en uso. Eso no es instantáneamente fatal, pero si está creciendo o si vmstat muestra swapping activo, estás en problemas.
Decisión: Si el uso de swap está estable y si/so son cero, puedes aceptarlo. Si el swap está activo, reduce la presión de memoria antes de tunear cualquier otra cosa.
Tarea 8: Identificar procesos con mayor IO en el host (a menudo QEMU)
cr0x@server:~$ iotop -oPa
Total DISK READ: 65.20 M/s | Total DISK WRITE: 110.30 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
18342 be/4 root 2.10 M/s 38.70 M/s 0.00 % 92.10% kvm -id 107 -name vm107 ...
19110 be/4 root 0.40 M/s 26.80 M/s 0.00 % 88.30% kvm -id 112 -name vm112 ...
Qué significa: Dos VMs son responsables de la mayoría de las escrituras. IO> indica tiempo esperando IO.
Decisión: Investiga las VMs 107 y 112: ¿qué están haciendo?, ¿estan haciendo backups?, ¿son bases de datos con escrituras sync?, ¿están mal configuradas?
Tarea 9: Mapear PID de QEMU a VMID y confirmar que Proxmox ve las mismas VMs “calientes”
cr0x@server:~$ qm list
VMID NAME STATUS MEM(MB) BOOTDISK(GB) PID
107 db-prod-01 running 32768 256.00 18342
112 files-prod-02 running 16384 1024.00 19110
Qué significa: Los PIDs coinciden con la salida de iotop. Ahora tienes atribución real: db-prod-01 y files-prod-02 son grandes escritores.
Decisión: Si el escritor pesado es una base de datos, evalúa el comportamiento de sync, configuraciones de caché y la idoneidad de un SLOG. Si es un servidor de archivos, revisa escaneos, tormentas de rsync o snapshots.
Tarea 10: Comprobar límites de CPU por VM, unidades de CPU y ballooning que pueden crear contención
cr0x@server:~$ qm config 107
balloon: 16384
boot: order=scsi0;net0
cores: 16
cpu: x86-64-v2-AES
memory: 32768
name: db-prod-01
net0: virtio=DE:AD:BE:EF:10:07,bridge=vmbr0,firewall=1
scsi0: rpool:vm-107-disk-0,discard=on,iothread=1,ssd=1
sockets: 1
Qué significa: Ballooning está habilitado y puede reclamar memoria bajo presión. cores=16 puede ser demasiado si el host está contendiendo; altos recuentos de vCPU aumentan la fricción de scheduling.
Decisión: Si el steal es alto, considera reducir vCPU sobredimensionadas para VMs que no escalan. Si existe presión de memoria, desactiva ballooning para cargas críticas de baja latencia y en su lugar dimensiona la memoria correctamente.
Tarea 11: Revisar frecuencia de CPU y throttling (el clásico “¿por qué está lento hoy?”)
cr0x@server:~$ lscpu | egrep 'Model name|Socket|Thread|CPU\(s\)'
CPU(s): 32
Model name: Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz
Socket(s): 2
Thread(s) per core: 2
cr0x@server:~$ cpupower frequency-info | egrep 'current CPU frequency|governor'
current CPU frequency: 1200 MHz (asserted by call to kernel)
governor "powersave" may decide which speed to use
Qué significa: Las CPUs están a 1.2GHz bajo un governor powersave. Eso puede perfectamente parecer “contención misteriosa”.
Decisión: En nodos de virtualización, prefiere performance o una política de governor afinada salvo que tengas una razón medida para no hacerlo. Si estás limitando potencia, acepta explícitamente la compensación de rendimiento.
Tarea 12: Verificar presión de interrupciones/softirq (tormentas de red o almacenamiento)
cr0x@server:~$ sar -I SUM 1 3
Linux 6.8.12-pve (pve01) 12/28/2025 _x86_64_ (32 CPU)
12:05:12 PM INTR intr/s
12:05:13 PM SUM 82000.00
12:05:14 PM SUM 91000.00
Qué significa: Una tasa de interrupciones muy alta puede consumir CPU en softirq y crear latencia. Es común con PPS altos en redes o algunos drivers de almacenamiento.
Decisión: Si %soft en mpstat es alto, investiga bridges de red, ajustes de virtio-net y si una VM está causando inundaciones de paquetes.
Tarea 13: Revisar logs del kernel por timeouts o resets de almacenamiento (el detector de mentiras “está bien”)
cr0x@server:~$ journalctl -k --since "30 min ago" | egrep -i 'nvme|blk|zfs|reset|timeout|error' | tail -n 20
Dec 28 11:41:03 pve01 kernel: nvme nvme0: I/O 123 QID 7 timeout, aborting
Dec 28 11:41:03 pve01 kernel: nvme nvme0: Abort status: 0x371
Dec 28 11:41:04 pve01 kernel: blk_update_request: I/O error, dev nvme0n1, sector 123456789 op 0x1:(WRITE) flags 0x0 phys_seg 1 prio class 0
Qué significa: Tienes problemas a nivel de dispositivo. El tuning de rendimiento es irrelevante hasta que la estabilidad hardware/firmware esté resuelta.
Decisión: Trata esto como un incidente: revisa firmware, cables, errores PCIe, salud de la unidad y comportamiento del controlador. Planifica reemplazo si es necesario.
Tarea 14: Medir propiedades del dataset ZFS que impactan el IO de VM
cr0x@server:~$ zfs get -o name,property,value -r compression,atime,recordsize,sync,logbias rpool | head
NAME PROPERTY VALUE
rpool compression zstd
rpool atime off
rpool recordsize 128K
rpool sync standard
rpool logbias latency
Qué significa: atime=off es sensato. recordsize importa: los volúmenes de VM suelen comportarse mejor con tamaños de registro más pequeños cuando domina IO aleatorio, pero hay que medir. sync=standard respeta semánticas de sync del invitado; cambiarlo puede ser peligroso.
Decisión: No cambies sync a disabled por “rendimiento” a menos que aceptes explícitamente el riesgo de pérdida de datos y tengas la aprobación del negocio. Ajusta recordsize y dispositivos especiales con cuidado, y prueba con IO representativo.
Tarea 15: Confirmar qué es el disco de la VM (virtio-scsi + iothread vs legacy)
cr0x@server:~$ qm config 112 | egrep 'scsi|sata|ide|virtio|iothread'
scsi0: rpool:vm-112-disk-0,discard=on,iothread=1,ssd=1
Qué significa: Estás en virtio-scsi con un hilo de IO. Eso suele ser una buena base para rendimiento y aislamiento.
Decisión: Si ves sata0 o discos IDE en VMs sensibles al rendimiento, corrígelo. Los controladores emulados no son adorables en 2025.
Tres microhistorias corporativas desde la trinchera
Incidente causado por una suposición incorrecta: “Bajo uso de CPU significa que el host está bien”
Una empresa mediana operaba un clúster Proxmox con mezcla de apps web y un par de bases de datos. Una mañana, el on-call recibió la queja habitual: “las páginas tardan 10 segundos en cargarse”. Los gráficos de CPU del host parecían tranquilos. Alguien concluyó que era una regresión de aplicación y empezó a revertir despliegues.
Mientras tanto, la carga promedio estaba alta. El rollback no ayudó. La gente se puso más ruidosa. El equipo de red fue arrastrado porque eso es lo que pasa cuando nadie tiene una teoría fiable.
Al final, alguien ejecutó iostat -x. El arreglo de SSDs SATA subyacente mostraba alto await con %util al máximo. Luego iotop señaló a una sola VM haciendo ráfagas enormes de escritura. Resultó ser un trabajo de reportes “temporal” que empezó a hacer exportaciones nocturnas de tablas completas y a comprimirlas dentro de la VM, escribiendo una avalancha de bloques pequeños y sync-heavy al disco de la VM.
La suposición incorrecta fue sutil: equipararon “CPU no ocupada” con “sistema sano”. En realidad, la CPU estaba esperando al almacenamiento, y el almacenamiento estaba siendo golpeado por una carga que nadie consideraba “producción”. La solución fue aburrida y efectiva: programar el trabajo de reportes fuera de pico, limitar su tasa y mover la VM a un pool con mejor latencia de escritura. El rollback fue solo teatro.
Optimización que salió mal: “Deshabilitar sync en ZFS y verlo volar”
Otra organización tenía una VM PostgreSQL que se quejaba de latencia de escritura. Un ingeniero bienintencionado sugirió establecer la propiedad del dataset ZFS sync=disabled en el almacenamiento de la VM para “arreglarlo”. Los benchmarks lucieron geniales. Todos se felicitaron. Lo desplegaron en producción.
Dos semanas después, un nodo perdió energía abruptamente. No fue un apagado limpio; simplemente murió. Al volver, la VM de la base de datos arrancó, pero la base mostraba síntomas de corrupción. Restauraron desde backups, pero la ventana de recuperación fue fea y el equipo tuvo que explicar por qué un “cambio de rendimiento” aumentó el riesgo de pérdida de datos.
El problema no fue que ZFS sea malo. Hizo lo que prometía. El equipo había cambiado el contrato de durabilidad: las escrituras sync del invitado ya no eran durables. Eso está bien para algunas cargas, catastrófico para otras, y no debe hacerse a la ligera.
Al final resolvieron el problema original de latencia a la manera difícil: movieron la VM de la base de datos a NVMe espejo más rápido, validaron políticas de caché de escritura y añadieron un dispositivo SLOG con protección contra pérdida de energía tras calcular la resistencia. La lección quedó clara: los hacks de rendimiento que reescriben la corrección no son optimizaciones; son apuestas con papeleo.
Práctica aburrida pero correcta que salvó el día: margen de capacidad y límites predecibles
Un tercer equipo gestionaba Proxmox para servicios internos. No glamoroso. Tenían algunas reglas que sonaban a exceso: mantener pools ZFS por debajo de cierto nivel de llenado, capear ARC en todos los nodos y prohibir “simplemente darle 32 vCPUs” a menos que alguien demostrara que la carga escala.
Entonces tuvieron un incidente real: una VM de un proveedor empezó a comportarse mal tras una actualización. Comenzó a escribir logs a una tasa ridícula y a rotarlos constantemente. Esto normalmente habría sido un incendio de rendimiento en todo el clúster.
En cambio, el radio de impacto fue limitado. Porque habían reservado margen y límites sensatos de ARC, el host no empezó a hacer swap. Porque tenían dimensionamiento y límites de CPU razonables, la VM no pudo robar el nodo entero. Porque tenían monitorización que seguía latencia de disco por VM, el culpable fue obvio en minutos.
Limitóron la IO de la VM y la movieron a un nodo menos crítico mientras se negociaba la corrección del proveedor. Nadie aplaudió. Nadie escribió un post sobre “mitigación heroica”. Pero la producción se mantuvo arriba, y ese es el punto de las prácticas aburridas.
Broma #2: La mejor solución de rendimiento es la que no requiere una sala de crisis—principalmente porque las salas de crisis funcionan con café y negación.
Errores comunes: síntoma → causa raíz → solución
1) El load average es enorme, la CPU está mayormente idle
Síntoma: load 30–80, CPU idle 60%+, usuarios se quejan de latencia.
Causa raíz: tareas bloqueadas en estado D esperando almacenamiento; IO wait es el verdadero limitador.
Solución: verifica con vmstat (alto b, alto wa) y iostat -x (alto await, alto %util); luego identifica la VM con más IO vía iotop y limita/ubica la carga.
2) La VM reporta alta CPU, pero la CPU del host parece normal
Síntoma: el invitado dice CPU al máximo, el dashboard del host no luce alarmante.
Causa raíz: CPU steal o cuota/límite de CPU en cgroups; el invitado quiere CPU pero no se le programa.
Solución: revisa %st en el invitado y el steal en mpstat; revisa límites/unidades de CPU de la VM; reduce overcommit o mueve la VM.
3) Pánico “ZFS se está comiendo toda la memoria”
Síntoma: poca memoria “free”, ARC grande, la gente quiere “deshabilitar ARC”.
Causa raíz: malentendido de la contabilidad de memoria en Linux; el problema real es swapping o presión de reclaim, no la RAM baja por sí sola.
Solución: comprueba vmstat por si/so, revisa memoria available; limita ARC si el host intercambia; de lo contrario ignora “free” y céntrate en latencia y hit rate.
4) Bloqueos periódicos aleatorios cada pocos segundos/minutos
Síntoma: aplicaciones se congelan brevemente y luego se recuperan, repitiéndose.
Causa raíz: picos de commit TXG de ZFS, ráfagas de snapshot/replicación o comportamiento de flush del controlador.
Solución: correlaciona con zpool iostat 1 y horarios de backup; limita jobs; considera vdevs más rápidos o separar tráfico de backup; verifica estabilidad de firmware y cachés de drive.
5) El rendimiento empeoró después de “afinar recordsize”
Síntoma: más IOPS, peor latencia; o mejor secuencial, peor aleatorio.
Causa raíz: desajuste de recordsize con el patrón de IO; para volúmenes de VM, cambiarlo a ciegas puede aumentar la amplificación y la fragmentación.
Solución: vuelve a una base sensata; mide con la carga real; afina por dataset (y entiende que zvol vs dataset difieren).
6) Los backups destruyen producción cada noche
Síntoma: patrón de incidentes a medianoche; de día está bien.
Causa raíz: lecturas de backup saturan el pool y compiten con latencia de escritura; las cadenas de snapshot y la compresión amplifican el trabajo.
Solución: programa y limita; almacenamiento separado para backups; limita jobs concurrentes; considera descargar lecturas pesadas a nodos de replicación.
7) “Añadimos L2ARC y ahora está más lento”
Síntoma: la latencia aumenta, la presión de memoria sube tras añadir un dispositivo de caché.
Causa raíz: overhead de metadata de L2ARC que consume RAM; la caché se calienta lentamente; el dispositivo añade contención.
Solución: quita L2ARC a menos que tengas un working set de solo lectura probado que no cabe en ARC y dispongas de RAM libre.
Listas de verificación / plan paso a paso
Checklist A: Cuando los usuarios dicen “todo está lento”
- En el host: captura
mpstat,vmstat,iostat -xyzpool iostatdurante 2–5 minutos. - Si el steal es alto: reduce la contención de CPU (mueve VMs, baja vCPU, revisa límites, ajusta governor).
- Si el iowait es alto: identifica VMs con más IO (iotop), luego correlaciona con la carga (backup, BD, escaneo, replicación).
- Si el swap está activo: detén la hemorragia (capea ARC, reduce ballooning, libera memoria, migra una VM) y luego arregla la capacidad.
- Revisa logs del kernel por errores/timeouts de almacenamiento. Si están presentes, deja de tunear y trata la estabilidad hardware/driver como prioridad uno.
Checklist B: Cuando una VM está lenta pero las demás van bien
- Dentro de la VM: comprueba
toppor%sty%wa. - En el host: mapea VMID a PID (
qm list) y observa CPU/IO de ese PID (ps,iotop). - Verifica tipo de disco/controlador: virtio-scsi con iothread para VMs con mucho IO.
- Revisa límites de CPU/ballooning que podrían estar restringiendo la VM silenciosamente.
- Si es una BD: confirma si la carga realiza muchas escrituras sync; considera almacenamiento diseñado para escrituras de baja latencia, no solo “alto throughput secuencial”.
Checklist C: Antes de cambiar ajustes ZFS en un nodo de producción
- Anota las propiedades actuales del dataset/zvol que planeas cambiar.
- Define métricas de éxito: latencia p99, tiempo de consulta, fsync, ventana de backup, iowait del host, hit% de ARC.
- Cambia una cosa a la vez, idealmente en una VM o dataset.
- Tener un plan de rollback que tome minutos, no horas.
- Nunca sacrifiques durabilidad por velocidad sin aprobación explícita del negocio (
sync=disabledno es una “opción de tuning”, es reescribir un contrato).
Preguntas frecuentes
1) ¿Cuál es un porcentaje “malo” de CPU steal?
No hay un umbral universal, pero steal sostenido por encima de ~5% durante latencia visible para usuarios es una señal fuerte de contención. El steal por ráfagas es común; el steal sostenido es un problema de capacidad o scheduling.
2) ¿Por qué el load average está alto cuando el uso de CPU es bajo?
Porque la carga cuenta tareas ejecutables y tareas en sleep no interrumpible (usualmente esperando IO). La alta latencia de IO produce carga alta sin CPU alta.
3) ¿Debería desactivar atime en ZFS?
Para almacenamiento de VM y la mayoría de cargas de servidor, atime=off suele ser lo correcto para evitar escrituras extra. Si tienes una carga que depende de la semántica atime, mantenla activada solo en ese dataset.
4) ¿ZFS ARC “está robando” memoria a las VMs?
ARC usa memoria disponible agresivamente por diseño, pero puede ser reclamada. El problema real es cuando el host hace swap o el ballooning empuja a los invitados a swap. Limita ARC si el host está bajo presión de memoria.
5) ¿Es buena idea añadir un SSD L2ARC para Proxmox?
A veces, pero no por defecto. L2ARC consume RAM para metadata y ayuda principalmente con working sets de solo lectura que no caben en ARC. Si estás corto de RAM, L2ARC suele ser una regresión de rendimiento.
6) Mi IO wait es alto. ¿Significa que necesito discos más rápidos?
Puede ser, pero primero averigua qué tipo de IO es. Una VM haciendo escrituras aleatorias sync puede saturar hardware excelente. Identifica al ofensor, el patrón de acceso y si tu layout de almacenamiento se ajusta a ello.
7) ¿Debería dar muchas vCPUs a cada VM “por si acaso”?
No. Sobredimensionar vCPUs aumenta la fricción de scheduling y puede empeorar la latencia tail para todos. Empieza más pequeño, mide y escala vCPU solo cuando la carga demuestre que se beneficia.
8) ¿Se supone que los backups de Proxmox deben afectar el rendimiento?
Los backups consumen IO. La pregunta es si el sistema está diseñado para absorberlo: almacenamiento separado para backups, throttling y límites de concurrencia. Si los backups causan incidentes de producción regularmente, el diseño de backups está incompleto.
9) ¿Cómo encuentro rápidamente la VM vecina ruidosa en un nodo?
Usa iotop -oPa para encontrar procesos QEMU con mucho IO y mapea sus PIDs a VMIDs vía qm list. Para contención de CPU, usa mpstat y ps ordenado por CPU, luego correlaciona.
10) ¿Debería desactivar el ballooning?
Para VMs críticas y sensibles a latencia, a menudo sí—el ballooning puede crear presión de memoria impredecible. Para cargas menos críticas, el ballooning puede aumentar la densidad. La clave es evitar swap del host a toda costa.
Conclusión: siguientes pasos que realmente mueven la aguja
Si tu nodo Proxmox está lento, deja de debatir y empieza a atribuir: captura mpstat, vmstat, iostat -x y zpool iostat. Decide si el limitador es scheduling de CPU (steal), latencia de almacenamiento (iowait + await) o presión de memoria (swap/reclaim). Luego nombra a la VM ofensor principal con iotop y qm list.
A partir de ahí, haz primero los arreglos poco glamorosos: capea ARC para proteger el host, evita swap en el host, dimensiona correctamente las vCPU y limita los backups. Solo entonces considera cambios estructurales como vdevs más rápidos, pools separados para cargas sensibles a latencia o añadir un SLOG correctamente diseñado. Afinar el rendimiento sin diagnóstico es sólo gasto improvisado.