Problema
Muchas pymes y startups necesitan un storage compartido que ofrezca alta disponibilidad, bajo latencia y alto IOPS, pero no pueden costear soluciones SAN o sistemas de almacenamiento dedicados. El reto típico es combinar servidores de cómputo con discos NVMe locales, exponer esos discos como bloques a una máquina virtual que actúe de NFS y, al mismo tiempo, garantizar que la pérdida de cualquiera de los nodos no interrumpa el servicio. En la práctica, los administradores se preguntan:
- ¿Es viable usar VirtIO‑SCSI como medio de paso de bloques entre Proxmox y la VM de NFS?
- ¿Qué capa de replicación o clustering debería estar bajo VirtIO‑SCSI?
- ¿Es mejor mantener el NFS dentro de una VM o desplegar varios servidores NFS independientes?
- ¿Cómo equilibrar rendimiento y complejidad con hardware commodity (NVMe, 25 GbE, EPYC)?
Causa
Los fallos habituales provienen de tres áreas:
-
Falta de sincronización de datos entre nodos
Si cada nodo solo mantiene su propio espejo ZFS, los bloques presentados a la VM de NFS pueden divergir cuando el hipervisor migra la VM o cuando un nodo falla. Sin una capa de replicación, la VM pierde acceso a los bloques que estaban en el nodo caído. -
Uso inapropiado de la interfaz de bloques
VirtIO‑SCSI es simplemente un canal de paso de bloques. Cuando se conecta a un disco virtual que está respaldado por un único pool ZFS, la pérdida del host que aloja ese pool implica la pérdida del disco completo, aunque la VM sea migrable. -
Diseño de HA a nivel de servicio y de datos
Tener un solo NFS VM crea un single point of failure a nivel de servicio. Incluso con migración automática, el tiempo de conmutación y la consistencia del estado pueden ser problemáticos si no hay un mecanismo de quorum que garantice cuál es la fuente de datos “verdadera”.
Solución
Una arquitectura robusta combina replicación de bloques a nivel de ZFS con un clúster de NFS que pueda decidir cuál nodo sirve los clientes. El esquema recomendado es:
[Proxmox Node A]---\
[Proxmox Node B]----> ZFS pool (mirrored) <---> DRBD/LINSTOR <---> VirtIO‑SCSI <---> NFS VM (activo‑pasivo)
[Proxmox Node C]---/
1. Replicación de bloques con LINSTOR + DRBD
- LINSTOR gestiona recursos de bloque distribuidos y permite crear volúmenes replicados en varios nodos.
- DRBD se encarga de la sincronización en tiempo real entre los discos locales de cada nodo.
- Cada nodo crea un resource que es un disco virtual (por ejemplo, 60 TB) replicado 2‑way o 3‑way según la tolerancia deseada.
Ventajas:
- La replicación ocurre a nivel de bloque, por lo que la VM de NFS ve un único disco consistente sin importar dónde se ejecute.
- DRBD ofrece conmutación rápida (milisegundos) porque el nodo secundario ya tiene una copia completa.
- LINSTOR simplifica la orquestación: crear, expandir o destruir volúmenes con un solo comando.
2. Exposición a la VM mediante VirtIO‑SCSI
Una vez que el recurso está disponible en los tres nodos, se crea una disk device en la VM de NFS usando el controlador VirtIO‑SCSI. La VM no necesita saber nada de la replicación; solo ve un disco SCSI estándar. Esto mantiene la latencia mínima porque el paso de bloques es directo y el driver VirtIO está optimizado para 25 GbE.
3. NFS en modo activo‑pasivo con Pacemaker/Corosync
Instala Pacemaker y Corosync dentro de la VM (o, mejor, en el host y controla la VM como recurso). Configura dos recursos:
- nfs-server (el daemon)
- virtual-disk (el recurso de bloque gestionado por LINSTOR)
El clúster garantiza que solo un nodo ejecute el daemon NFS mientras el otro está en standby. Cuando el nodo activo falla, Pacemaker promueve el standby y la IP flotante se mueve automáticamente, manteniendo la conectividad de los clientes.
4. Redundancia de la capa de red
Usa bonding 802.3ad o LACP entre los dos puertos 25 GbE de cada host y el switch. Configura VLANs separadas para tráfico de gestión, NFS y migraciones de VM. Esto evita cuellos de botella y permite que la conmutación de fallos sea transparente.
5. Dimensionamiento y afinación
- ZFS recordsize: para workloads con muchos archivos pequeños, usa
recordsize=64K. - ARC y L2ARC: asigna 8 GB de ARC por nodo y habilita L2ARC en SSDs de menor costo si la carga de lectura es alta.
- Synchronous writes: habilita
sync=standarden ZFS yasyncen NFS solo para datos que toleren pérdida parcial. - IO scheduler: mantén
noneomq-deadlineen los discos NVMe para evitar sobrecarga del scheduler.
Cuándo aplicar esta solución
Ideal cuando:
- Se necesita >60 TB de capacidad usable y se dispone de al menos tres nodos con NVMe.
- Los clientes dependen de NFS para compartir directorios y home directories.
- Se requiere HA a nivel de datos y servicio sin invertir en una solución Ceph completa.
- El presupuesto permite hardware commodity (EPYC, 25 GbE) pero no SAN dedicada.
No recomendado si:
- La carga es predominantemente bloques de alta concurrencia (por ejemplo, bases de datos que requieren OSDs dedicados). En ese caso Ceph o un almacenamiento dedicado puede ser más apropiado.
- El número de nodos es menor a tres; la replicación 2‑way con DRBD pierde tolerancia a fallos de red.
- Se busca latencia sub‑milisegundo constante; la capa de replicación añade algunos microsegundos.
Código
# 1. Crear pool ZFS en cada nodo (asume discos nvme0n1 y nvme1n1)
zpool create -f -o ashift=12 tank mirror /dev/nvme0n1 /dev/nvme1n1
# 2. Instalar LINSTOR y DRBD
apt-get install linstor-controller linstor-satellite drbd-utils
# 3. Crear recurso replicado de 60TB (3‑way)
linstor resource-definition create shared-nfs
linstor volume-definition create shared-nfs 60T
linstor resource create shared-nfs node-a
linstor resource create shared-nfs node-b
linstor resource create shared-nfs node-c
linstor resource-definition modify shared-nfs --auto-place 3
# 4. Exponer el recurso a la VM (en Proxmox)
qm set 101 --scsi0 linstor:shared-nfs
# 5. Configurar Pacemaker dentro de la VM (ejemplo simplificado)
crm configure primitive nfs-server ocf:heartbeat:nfs \
params nfs_shared_infodir="/export" \
op start timeout=60s interval=0 \
op stop timeout=60s interval=0
crm configure primitive ip-float ocf:heartbeat:IPaddr2 \
params ip=10.0.0.100 cidr_netmask=24 \
op monitor interval=30s
crm configure colocation nfs-with-ip inf: nfs-server ip-float:INFINITY
crm configure order start-ip-before-nfs inf: ip-float:start nfs-server:start
Verificación
-
Estado del recurso LINSTOR
linstor resource listdebe mostrar los tres nodos con estadoUpToDate. -
Sincronización DRBD
drbdadm statusdebe indicarConnectedyUpToDateen todas las réplicas. -
Failover de NFS
Detén el daemon NFS en el nodo activo (systemctl stop nfs-server). Pacemaker debe promover el nodo standby y la IP flotante debe permanecer accesible (ping 10.0.0.100). -
Rendimiento básico
Ejecutafiodesde una VM cliente contra un share NFS y verifica IOPS y latencia acorde a los requisitos (p.ej., >10 k IOPS, <1 ms latencia para bloques pequeños). -
Prueba de pérdida de nodo
Apaga físicamente uno de los nodos Proxmox. El clúster debe seguir sirviendo NFS sin interrupción y el recurso LINSTOR debe quedar enDegradedpero operativo.
Notas adicionales
- Monitorización: integra
zfs-stats,drbdypacemakeren Prometheus para detectar desincronizaciones antes de que se conviertan en fallos. - Backup: aunque la arquitectura es HA, sigue siendo buena práctica tener snapshots ZFS periódicos y replicarlos a un sitio externo (por ejemplo, usando
zfs senda un servidor de backup). - Actualizaciones: al actualizar el kernel o ZFS, programa una migración de la NFS VM a otro nodo y verifica que la replicación siga sincronizada antes de reiniciar el nodo original.
- Escalado: si la carga crece, puedes añadir más nodos al clúster LINSTOR y redistribuir los volúmenes sin downtime.
Con esta combinación de ZFS, LINSTOR/DRBD y NFS bajo Pacemaker, se consigue una solución de almacenamiento HA que aprovecha al máximo los discos NVMe y la red de 25 GbE, sin la complejidad ni el coste de un SAN o un clúster Ceph completo.