ZFS a fondo en Proxmox VE y replicación de storage

Por: Artiko
zfsarcraidzzpoolreplicacionpvesr

ZFS a fondo en Proxmox VE y replicación de storage

En el capítulo 7 viste el modelo de plugins de storage y ZFS apareció como una entrada más de la tabla: el plugin zfspool, formatos raw y subvol, snapshots baratos, sin qcow2. Eso basta para dar de alta un storage y arrancar VMs. No basta para explicar por qué un pool RAIDZ2 recién montado con seis discos SATA da menos IOPS que un solo SSD, por qué el host se queda sin RAM aparentemente sin motivo, o por qué el zfs_arc_max que escribiste en /etc/modprobe.d/zfs.conf no cambió absolutamente nada tras reiniciar.

Ese es el hueco que cierra este capítulo. ZFS no es un sistema de archivos que se configura una vez: es un conjunto de decisiones de diseño que se toman al crear el pool y que, en buena parte, no se pueden deshacer. El ashift es inmutable. El volblocksize de un zvol se fija al crearlo. Un special device añadido a un pool no se puede quitar. Equivocarte en esas tres cosas significa vaciar el pool y volver a empezar.

La segunda mitad usa ZFS para algo que a primera vista parece imposible: alta disponibilidad sin storage compartido. Con pvesr replicas los zvol de una VM a otro nodo del cluster cada minuto, y cuando el nodo origen muere, la VM arranca en el destino con los datos de la última sincronización. No es Ceph ni una SAN, y hay pérdida de datos acotada, pero para muchísimos escenarios es la relación coste/resiliencia correcta.

Al final tendrás criterio para elegir el layout del pool, un ARC acotado y persistente, un scrub programado con alertas por correo cuando algo se degrade, y jobs de replicación funcionando hacia otro nodo con red dedicada. Todo lo que sigue está verificado contra la documentación de Proxmox VE 9.2 sobre Debian 13 Trixie, con ZFS 2.4.

Prerrequisitos: RAM, ECC y por qué nunca sobre RAID hardware

Antes de crear ningún pool hay tres requisitos que la documentación oficial declara literalmente y que no son negociables. Uno. Memoria. “ZFS depends heavily on memory, so you need at least 8GB to start. In practice, use as much as you can get for your hardware/budget.” ZFS no usa la RAM como capricho: el ARC es la caché de lectura del sistema de archivos y sin él cada lectura baja al disco. Ocho gigas es el suelo para que arranque, no la cifra de un nodo con VMs encima.

Dos. ECC. “To prevent data corruption, we recommend the use of high quality ECC RAM.” ZFS verifica checksums de todo lo que lee del disco y repara automáticamente lo que puede. Lo que no puede verificar es lo que se corrompió en RAM antes de calcular el checksum: ahí ZFS escribiría el dato malo con un checksum perfectamente válido.

Tres. Acceso directo a los discos. Este es el que más instalaciones arruina:

“Do not use ZFS on top of a hardware RAID controller which has its own cache management. ZFS needs to communicate directly with the disks. An HBA adapter or something like an LSI controller flashed in ‘IT’ mode is more appropriate.”

Advertencia: una controladora RAID con caché propia miente sobre cuándo un dato ha llegado realmente al plato. ZFS construye su modelo transaccional sobre la premisa de que cuando emite un flush y el disco responde, el dato está persistido. Si en medio hay una caché con batería agotada o en modo write-back, un corte de corriente puede dejar el pool con transacciones a medias. Los discos en modo JBOD sobre una controladora RAID no son lo mismo que un HBA en modo IT: en muchos modelos la caché sigue interponiéndose.

Dos apuntes más. Si vas a correr Proxmox VE dentro de una VM para practicar (virtualización anidada), “don’t use virtio for disks of that VM, as they are not supported by ZFS. Use IDE or SCSI instead (also works with the virtio SCSI controller type)”: el controlador VirtIO SCSI sí vale, VirtIO Block no. Y para el SLOG y el L2ARC, cuando llegues ahí: “If you use a dedicated cache and/or log disk, you should use an enterprise class SSD.”

El pool que crea el instalador: rpool y local-zfs

Si en el capítulo 1 elegiste ZFS en el instalador, el sistema que tienes delante ya tiene un layout concreto que conviene conocer antes de tocarlo.

El instalador ofrece estos niveles:

NivelDescripciónDiscos mínimos
RAID0Striping. Capacidad = suma de todos; sin redundancia1
RAID1Mirroring. Escritura idéntica en todos; capacidad = 1 disco2 del mismo tamaño
RAID10Combinación de RAID0 sobre RAID14
RAIDZ-1Variación de RAID-5 con paridad simple3
RAIDZ-2Variación de RAID-5 con paridad doble4
RAIDZ-3Variación de RAID-5 con paridad triple5

Sea cual sea el nivel, el resultado es siempre el mismo pool rpool, con el sistema de archivos raíz en rpool/ROOT/pve-1 y un dataset rpool/data para las imágenes de los guests, dado de alta como storage:

zfspool: local-zfs
        pool rpool/data
        sparse
        content images,rootdir

La propiedad sparse es la que activa el thin provisioning: el refreservation del zvol no iguala su tamaño lógico, así que puedes asignar más disco del que hay. Es cómodo y es exactamente lo que llena pools por sorpresa; vigila zpool list como vigilarías un thin pool de LVM. Nada más instalar, zfs list te devuelve algo así:

NAME               USED  AVAIL  REFER  MOUNTPOINT
rpool             4.94G  7.68T    96K  /rpool
rpool/ROOT         702M  7.68T    96K  /rpool/ROOT
rpool/ROOT/pve-1   702M  7.68T   702M  /
rpool/data          96K  7.68T    96K  /rpool/data
rpool/swap        4.25G  7.69T    64K  -

Fíjate en rpool/swap: es un zvol, y más adelante verás por qué la propia documentación desaconseja esa configuración. La convención de nombres de los volúmenes dentro de un storage zfspool es fija:

vm-<VMID>-<NAME>       imagen de VM, es un zvol en formato raw
base-<VMID>-<NAME>     imagen de plantilla, solo lectura
subvol-<VMID>-<NAME>   subvolumen, es un dataset ZFS, para contenedores

Con <NAME> por defecto disk[N]. Que los contenedores usen datasets y las VMs zvol tiene consecuencias directas: el recordsize afecta a los primeros y el volblocksize a los segundos, y son parámetros distintos con reglas distintas. Los contenedores los viste en el capítulo 6.

vdevs: rendimiento real de mirror frente a RAIDZ

El bloque de construcción de un pool no es el disco, es el vdev: “The basic building block of a ZFS pool is the virtual device, or vdev. All vdevs in a pool are used equally and the data is striped among them (RAID0).” La referencia canónica es man zpoolconcepts. Esa frase encierra la regla más importante del diseño de pools: los vdevs se estripan entre sí, y el rendimiento del pool es la suma del rendimiento de sus vdevs. Por eso un pool con dos vdevs mirror rinde el doble en escritura que uno con un solo vdev mirror de cuatro discos.

Rendimiento por tipo de vdev

  • Un vdev mirror (RAID1) se comporta aproximadamente como un solo disco en IOPS y ancho de banda al escribir. Al leer escala linealmente con el número de discos del mirror.
  • Cuatro discos como dos vdevs mirror (RAID10): escritura equivalente a dos discos sueltos en IOPS y ancho de banda; lectura equivalente a cuatro discos sueltos.
  • Un RAIDZ de cualquier nivel se comporta como un solo disco en IOPS, con mucho ancho de banda; cuánto depende del tamaño del vdev y del nivel de paridad.
  • dRAID rinde como un RAIDZ equivalente.

Y la frase que decide el diseño en un hipervisor: “For running VMs, IOPS is the more important metric in most situations.” Una VM no lee ficheros secuenciales de 10 GB: hace lecturas y escrituras pequeñas y dispersas de su sistema de archivos, de su base de datos, de su log. Ahí el ancho de banda agregado de un RAIDZ de ocho discos no compensa que en IOPS se comporte como un disco. La recomendación oficial es explícita: “Mirror vdevs (RAID1, RAID10) have favorable behavior for VM workloads. Use them, unless your environment has specific needs and characteristics where RAIDZ performance characteristics are acceptable.”

Espacio útil y redundancia

  • Pool de mirrors: espacio útil = 50% de la suma de los discos, y menos todavía si el mirror es de tres vías. Basta con que sobreviva un disco sano por mirror para que el pool siga funcionando.
  • RAIDZ de N discos con nivel P: espacio útil aproximado N-P discos.

La comparación honesta hay que hacerla con el mismo número de discos, y aquí aparece el caso que la documentación señala expresamente:

Cuatro discos de 4 TBEspacio útilIOPS de escrituraTolerancia a fallos
2 vdevs mirror (RAID10)~8 TBComo 2 discos1 por mirror; 2 si son de mirrors distintos
RAIDZ2~8 TBComo 1 disco2 discos cualesquiera
RAIDZ1~12 TBComo 1 disco1 disco

Cita literal: “a 4 disk pool with RAIDZ2. In this situation it is usually better to use 2 mirror vdevs for the better performance as the usable space will be the same”. Con cuatro discos el RAIDZ2 da exactamente el mismo espacio que el RAID10 y la mitad de IOPS; lo único que ganas es tolerar dos fallos concretos que el RAID10 podría no tolerar. Para cargas de VM la balanza casi siempre cae del lado de los mirrors.

Amplificación de escritura de los zvol sobre RAIDZ

Este apartado es el que explica los pools que se llenan al doble de velocidad de lo previsto. Y no es un mito de foro: está en la documentación, con números.

“For each data block the pool needs parity data which is at least the size of the minimum block size defined by the ashift value of the pool. With an ashift of 12 the block size of the pool is 4k. The default block size for a ZVOL is 8k. Therefore, in a RAIDZ2 each 8k block written will cause two additional 4k parity blocks to be written, 8k + 4k + 4k = 16k.”

Desglosado: la paridad no puede ser más pequeña que el bloque mínimo del pool. Con ashift=12 ese mínimo es 4 KiB. Un zvol con volblocksize de 8k escribe bloques de 8 KiB, y en RAIDZ2 hacen falta dos bloques de paridad que ocupan 4 KiB cada uno aunque conceptualmente necesitaran menos. Resultado: 16 KiB en disco por cada 8 KiB de datos, un 100% de sobrecoste cuando la aritmética ingenua del RAIDZ2 con seis discos prometía un 50%.

flowchart TD
    A["Escritura del guest de 4 KiB"] --> B{"volblocksize del zvol"}
    B -->|"8k"| C["Read modify write de 8k"]
    B -->|"16k"| D["Read modify write de 16k"]
    C --> E{"Layout del vdev"}
    D --> E
    E -->|"mirror"| F["Una copia integra por disco del mirror"]
    E -->|"raidz2 con ashift 12"| G["Bloque de 8k mas dos bloques de paridad de 4 KiB"]
    G --> H["8k + 4k + 4k = 16k en disco"]
    F --> I["Sin paridad extra: coste previsible"]

Observa el otro efecto que el diagrama deja ver: si el guest escribe 4 KiB pero el zvol tiene bloques de 8k o 16k, ZFS tiene que leer el bloque completo, modificar la parte que cambió y reescribirlo entero. Ese read-modify-write también cuesta IOPS.

Cómo medirlo en tu propio pool

zfs get volsize,refreservation,used rpool/data/vm-100-disk-0
  • volsize: el tamaño que ve la VM, el número que pusiste al crear el disco.
  • refreservation: espacio reservado en el pool incluyendo la paridad esperada. Si el pool es thin provisioned (sparse), este valor será 0 y no te dirá nada.
  • used: útil si el pool es thin y no hay snapshots. Los snapshots sesgan el valor, así que compáralo solo en volúmenes limpios.

La forma práctica de verlo: crea un zvol de prueba de 10 GiB en el RAIDZ, escribe datos dentro y compara used con lo que escribiste.

Las tres contramedidas documentadas

  1. “Increase the volblocksize to improve the data to parity ratio.”
  2. “Use mirror vdevs instead of RAIDZ.”
  3. “Use ashift=9 (block size of 512 bytes).”

Con dos advertencias que hay que leer antes de aplicarlas:

Sobre subir el volblocksize: “The volblocksize property can only be set when creating a ZVOL. The default value can be changed in the storage configuration. When doing this, the guest needs to be tuned accordingly and depending on the use case, the problem of write amplification is just moved from the ZFS layer up to the guest.” Traducido: subir a 64k mejora la relación datos/paridad, pero si el guest hace escrituras de 4 KiB, ahora cada una obliga a un read-modify-write de 64 KiB. Has movido el problema de sitio.

Sobre ashift=9: “Using ashift=9 when creating the pool can lead to bad performance, depending on the disks underneath, and cannot be changed later on.” Con discos de sector físico 4K —prácticamente todos los actuales— un ashift=9 obliga al disco a hacer read-modify-write internamente en cada escritura. Es la peor de las tres opciones salvo casos muy concretos. Queda la segunda, que es la que la propia documentación recomienda como conclusión: usa mirrors.

ashift, recordsize y volblocksize: qué se puede cambiar y qué no

Tres parámetros que se confunden constantemente porque los tres hablan de tamaños de bloque, pero viven en capas distintas.

ParámetroÁmbitoQué haceMutabilidad
ashiftPool, en zpool create -o ashift=NTamaño mínimo de bloque = 2^N. ashift=12 da 4 KiB; ashift=9 da 512 BInmutable tras la creación
recordsizeDataset (filesystem)Tamaño máximo de bloque lógico para ficheros. Default 128KSe puede cambiar; solo afecta a bloques nuevos
volblocksizeZVOL (disco de VM)Tamaño de bloque fijo del zvolSolo al crear el zvol

Regla del ashift: “The ashift should have the same sector-size (2 power of ashift) or larger as the underlying disk.” Todos los ejemplos de la documentación de Proxmox usan ashift=12, y ese es el valor sensato salvo que sepas exactamente por qué quieres otro. El recordsize afecta a los subvolúmenes de contenedores y a los datos en ficheros; el volblocksize afecta a los discos de las VMs y en Proxmox VE se controla con la propiedad blocksize del storage zfspool:

zfspool: vmdata
        pool tank/vmdata
        content rootdir,images
        blocksize 16k
        sparse

En PVE 9.2 el schema de ese parámetro se validó de forma estricta (issue 3936): acepta enteros entre 512 B y 16 MiB, con sufijos k y m opcionales, y exige que el valor sea potencia de dos. Si venías de una configuración con un valor arbitrario, la validación lo rechazará.

Gotcha sobre el valor por defecto: la documentación de PVE describe la propiedad como “Set ZFS blocksize parameter” sin declarar un default, mientras el capítulo de consideraciones RAIDZ sigue citando “The default block size for a ZVOL is 8k”. Fuentes secundarias afirman que el default efectivo en PVE reciente es 16k, alineado con el cambio de OpenZFS. No des por bueno ninguno de los dos: compruébalo en tu nodo sobre un disco ya creado con zfs get volblocksize.

zfs get volblocksize rpool/data/vm-100-disk-0

Crear pools con comandos exactos

Antes de los comandos, las reglas del nombre del pool, literales: debe empezar por una letra a-z/A-Z; solo admite alfanuméricos y los caracteres -, _, ., : o espacio; no puede empezar por mirror, raidz, draid ni spare; y no puede llamarse log. Esas palabras están reservadas porque son las que interpreta zpool create en la línea de comandos.

# Un solo disco
zpool create -f -o ashift=12 tank /dev/disk/by-id/<id-disco>

# RAID-0, striping puro, sin redundancia (mínimo 1)
zpool create -f -o ashift=12 tank <device1> <device2>

# RAID-1, un vdev mirror (mínimo 2)
zpool create -f -o ashift=12 tank mirror <device1> <device2>

# RAID-10, dos vdevs mirror estripados (mínimo 4)
zpool create -f -o ashift=12 tank mirror <d1> <d2> mirror <d3> <d4>

# RAIDZ-1 (mínimo 3)
zpool create -f -o ashift=12 tank raidz1 <d1> <d2> <d3>

# RAIDZ-2 (mínimo 4)
zpool create -f -o ashift=12 tank raidz2 <d1> <d2> <d3> <d4>

zfs set compression=lz4 tank

Usa siempre rutas de /dev/disk/by-id/, nunca /dev/sdb. Los nombres sdX los asigna el kernel en el orden en que enumera los dispositivos y cambian entre arranques; un pool creado con sdb puede importarse mal tras añadir una controladora. Para dar de alta el pool como storage de Proxmox VE, ya sobre un dataset dedicado:

zfs create tank/vmdata
zfs set compression=on tank/vmdata
pvesm scan zfs
pvesm add zfspool vmdata --pool tank/vmdata --content rootdir,images --sparse 1

pvesm scan zfs lista los pools importables y es lo mismo que rellena el desplegable “ZFS Pool” de la GUI en Datacenter -> Storage -> Add -> ZFS. Para crear el pool desde la interfaz: Node -> Disks -> ZFS -> Create: ZFS. Recuerda las limitaciones del backend que ya viste en el capítulo 7: solo formatos raw (zvol) y subvol (dataset), sin qcow2 ni vmdk, y solo tipos de contenido images y rootdir. Nada de ISOs, plantillas ni backups en un storage zfspool.

Ampliar un RAIDZ existente

Durante años la respuesta a “quiero añadir un disco a mi RAIDZ” fue “no se puede, destruye el pool”. Eso cambió: “This feature only works starting with Proxmox VE 9 (ZFS 2.3.3).”

zpool attach <pool> <raidzN-M> <device>
zpool status <pool>
zpool list <pool> -v
zfs list <pool>

El segundo argumento no es el pool sino el vdev concreto al que se añade el disco, con el nombre que le da zpool status (por ejemplo raidz2-0). Comprueba el nombre antes de ejecutar el comando.

Gotcha: los bloques ya escritos conservan la relación datos/paridad antigua hasta que se reescriben, así que el espacio libre que reporta el pool tras expandir puede ser menor del que espera la aritmética.

dRAID: cuándo tiene sentido

dRAID distribuye los discos de repuesto dentro del propio RAID en lugar de dejarlos parados: su capacidad se reserva y se usa para reconstruir, lo que hace el resilver mucho más rápido que en un RAIDZ clásico. A cambio, hereda el perfil de rendimiento del RAIDZ. Las notas oficiales acotan cuándo usarlo:

“dRAID is intended for more than 10-15 disks. A RAIDZ setup should be better for a lower amount of disks in most use cases.”

“The GUI requires one more disk than the minimum (i.e. dRAID1 needs 3). It expects that a spare disk is added as well.”

NivelDiscos mínimosFallos tolerados
dRAID1 / dRAID21
dRAID232
dRAID343

Dos parámetros más que la GUI expone y conviene entender:

  • spares: default 0. “Without spares, rebuilding won’t get any speed benefits.” Un dRAID sin spares pierde exactamente la ventaja por la que lo elegiste.
  • data: número de dispositivos en un grupo de redundancia. Default 8, salvo que disks - parity - spares sea menor. “A smaller number of data devices leads to higher IOPS, better compression ratios and faster resilvering, but defining fewer data devices reduces the available storage capacity of the pool.”

Si tu nodo tiene cuatro u ocho discos, dRAID no es para ti.

SLOG y L2ARC: cuándo sí, cuándo no y con qué disco

Un pool para VMs puede llevar hasta cuatro clases de vdev, y conviene tenerlas claras antes de comprar hardware.

flowchart LR
    POOL["Pool tank"] --> DATA["vdevs de datos"]
    POOL --> LOG["vdev log: SLOG"]
    POOL --> CACHE["vdev cache: L2ARC"]
    POOL --> SPECIAL["vdev special: metadatos"]
    DATA --> D1["mirror de dos discos"]
    DATA --> D2["mirror de dos discos"]
    LOG --> L1["SSD con power loss protection"]
    LOG --> L2["Opcionalmente en mirror"]
    CACHE --> C1["SSD suelta sin redundancia posible"]
    SPECIAL --> S1["Mirror obligatorio en la practica"]
    SPECIAL --> S2["Punto unico de fallo de todo el pool"]

L2ARC: caché de lectura de segundo nivel

zpool create -f -o ashift=12 <pool> <device> cache <cache-device>
zpool add <pool> cache <cache-device>

Qué hace y para qué sirve realmente:

“It is possible to use a dedicated device, or partition, as second-level cache to increase the performance. Such a cache device will especially help with random-read workloads of data that is mostly static. As it acts as additional caching layer between the actual storage, and the in-memory ARC, it can also help if the ARC must be reduced due to memory constraints.”

Dos detalles operativos: “Note that for cache devices no mirror or raid modi exist, they are all simply accumulated” y “if any cache device produces errors on read, ZFS will transparently divert that request to the underlying storage layer”. Es decir, el L2ARC no necesita redundancia porque no contiene el único ejemplar de nada: si falla, ZFS baja al pool y sigue.

Cuándo NO: el L2ARC consume RAM para su tabla de índices, y esa RAM se la quita al ARC. En un nodo con poca memoria puedes empeorar el rendimiento cambiando caché rápida en RAM por caché lenta en SSD. Tampoco ayuda a escrituras, ni a lecturas realmente aleatorias sobre datos que cambian todo el tiempo.

SLOG: el ZIL en un disco aparte

zpool create -f -o ashift=12 <pool> <device> log <log-device>
zpool add <pool> log <log-device>

# Las dos cosas en particiones distintas de la misma SSD
zpool add -f <pool> log <device-part1> cache <device-part2>

Para lo segundo, crea antes dos particiones GPT con parted o gdisk y usa después las rutas de /dev/disk/by-id/. Qué es el ZIL y qué papel juega el SLOG:

“It is possible to use a dedicated drive, or partition, for the ZFS Intent Log (ZIL), it is mainly used to provide safe synchronous transactions, so often in performance critical paths like databases, or other programs that issue fsync operations more frequently.”

Requisitos del disco, literales: “use fast SSDs with power-loss protection, as those have much smaller commit latencies” y “use at least a few GB for the partition (or whole device), but using more than half of your installed memory won’t provide you with any real advantage”.

Y dos comportamientos que cambian cómo dimensionas la redundancia: “You can also mirror the log device to multiple devices, this is mainly useful to ensure that performance doesn’t immediately degrade if a single log device fails” y “if all log devices fail the ZFS main pool itself will be used again, until the log device(s) get replaced”. O sea: perder el SLOG no te cuesta el pool, te cuesta el rendimiento. Es una diferencia enorme respecto al special device, que viene a continuación.

Cuándo NO: si la carga es asíncrona —la mayoría de VMs con cache=writeback, copias de ficheros, backups— el SLOG no aporta nada, porque el ZIL solo interviene en escrituras síncronas. Y una SSD de consumo sin power-loss protection como SLOG es peor que no tener SLOG: añade latencia y, en un corte de corriente, puede perder justamente las transacciones que prometió haber persistido.

El special device y el peligro de special_small_blocks

Disponible desde ZFS 0.8.0, el vdev special guarda metadatos, tablas de deduplicación y, opcionalmente, bloques pequeños de ficheros. Su caso de uso claro son pools de HDD lentos con mucha actividad de metadatos: crear, actualizar y borrar muchos ficheros. Y viene con dos avisos que hay que leer juntos:

Important: “The redundancy of the special device should match the one of the pool, since the special device is a point of failure for the whole pool.”

Warning: “Adding a special device to a pool cannot be undone!”

Léelo otra vez: si pierdes el special device, pierdes el pool entero, porque los metadatos viven ahí y sin metadatos no hay datos localizables. Y no puedes quitarlo después. Una SSD suelta como special convierte un RAIDZ2 con tolerancia a dos fallos en un pool que muere con un solo fallo.

# Crear el pool con special desde el principio
zpool create -f -o ashift=12 <pool> mirror <d1> <d2> special mirror <d3> <d4>

# Añadirlo a un pool existente (recuerda: irreversible)
zpool add <pool> special mirror <device1> <device2>

zfs set special_small_blocks=4K <pool>              # todo el pool
zfs set special_small_blocks=4K <pool>/<filesystem> # un dataset
zfs set special_small_blocks=0  <pool>/<filesystem> # excluirse

special_small_blocks acepta 0 (desactivado) o una potencia de dos entre 512B y 1M. Aquí está la trampa:

Important: “If the value for special_small_blocks is greater than or equal to the recordsize (default 128K) of the dataset, all data will be written to the special device, so be careful!”

Poner special_small_blocks=128K en un dataset con recordsize=128K no envía “los bloques pequeños” al SSD: envía todo. Tu SSD de 480 GB se llena, el pool se queda sin espacio en el vdev special y el comportamiento deja de ser el que esperabas. Empieza por 4K o 8K y mide antes de subir.

Compresión y cifrado nativo

Compresión

zfs set compression=lz4 <dataset>
zfs set compression=off <dataset>

“When compression is enabled on a dataset, ZFS tries to compress all new blocks before writing them and decompresses them on reading. Already existing data will not be compressed retroactively.

“We recommend using the lz4 algorithm, because it adds very little CPU overhead. Other algorithms like lzjb and gzip-N, where N is an integer from 1 (fastest) to 9 (best compression ratio), are also available. Depending on the algorithm and how compressible the data is, having compression enabled can even increase I/O performance.”

Ese último punto no es marketing: si el dato se comprime, hay menos bytes que escribir en el disco, y en discos lentos eso compensa de sobra el coste de CPU de lz4. Que no se compriman los datos existentes tiene una consecuencia práctica: activar compresión en un pool con VMs ya dentro no libera nada; para que el disco de una VM se comprima hay que reescribirlo, por ejemplo moviéndolo a otro storage y de vuelta con qm move-disk. Y un apunte de rigor: la documentación oficial de Proxmox VE no menciona zstd en esta sección. OpenZFS 2.x lo soporta y en la práctica funciona, con mejor ratio y más CPU que lz4, pero si te ciñes a lo respaldado documentalmente, lz4 es la respuesta.

Cifrado nativo

Warning: “Native ZFS encryption in Proxmox VE is experimental.”

Las limitaciones declaradas son dos y ambas importan mucho aquí: problemas con la replicación de datasets cifrados (bugzilla 2350) y errores de checksum al usar snapshots o ZVOLs (openzfs/zfs#11688). Es decir, precisamente snapshots, zvols y replicación, que es todo lo que Proxmox VE usa.

zpool get feature@encryption tank
zpool set feature@encryption=enabled tank

zfs create -o encryption=on -o keyformat=passphrase tank/encrypted_data
pvesm add zfspool encrypted_zfs -pool tank/encrypted_data

zfs mount -l tank/encrypted_data     # carga la clave y monta en un paso

# Keyfile aleatorio en lugar de passphrase
dd if=/dev/urandom of=/path/to/keyfile bs=32 count=1
zfs change-key -o keyformat=raw -o keylocation=file:///path/to/keyfile tank/encrypted_data

Advertencias adicionales, literales:

  • “There is currently no support for booting from pools with encrypted datasets using GRUB, and only limited support for automatically unlocking encrypted datasets on boot.”
  • “It is recommended to either unlock storage datasets manually after booting, or to write a custom unit to pass the key material needed for unlocking on boot to zfs load-key.”
  • “Establish and test a backup procedure before enabling encryption of production data.”

Las propiedades a conocer son encryptionroot, encryption, keylocation, keyformat y keystatus, con los comandos zfs load-key, zfs unload-key y zfs change-key. Y el detalle que decide si puedes cifrar en un cluster: “For migration and storage replication of encrypted datasets in Proxmox VE, the data is sent without the encryption properties, and the state of encryption is determined by the target”. Si quieres que los datos sigan cifrados al otro lado, todos los storages en todos los nodos tienen que estar cifrados. Un solo nodo sin cifrar convierte la réplica en datos en claro.

El ARC: el default de hoy y cómo limitarlo de verdad

Este es el punto donde más desinformación circula, porque el comportamiento cambió y buena parte de los tutoriales de internet describen el mundo anterior.

Texto literal de la documentación 9.2.4:

“ZFS uses 10 % of the host memory, clamped to a maximum of 16 GiB, for the Adaptive Replacement Cache (ARC) by default. This value is written to /etc/modprobe.d/zfs.conf during installation.”

Before Proxmox VE 8.1, the ARC usage limit was not set during installation and matched ZFS’ default value which was either 62.5% starting on ZFS 2.3.0 or 50% for older versions. For existing installations that predate Proxmox VE 8.1, manual steps would have to be performed in order to lower the usage limit.”

Consecuencia directa: en una instalación nueva de PVE 9.2 el ARC ya viene acotado. En un nodo que arrastra su instalación desde PVE 7 u 8.0, no: ahí el ARC puede estar comiéndose la mitad de la RAM del host, y esa es la explicación de muchos “mi nodo no tiene memoria libre”. Para dimensionarlo:

“Allocating enough memory for the ARC is crucial for IO performance, so reduce it with caution. As a general rule of thumb, allocate at least 2 GiB Base + 1 GiB/TiB-Storage. For example, if you have a pool with 8 TiB of available storage space then you should use 10 GiB of memory for the ARC.”

“ZFS also enforces a minimum value of 64 MiB.”

Cambio temporal, se pierde al reiniciar

echo "$[10 * 1024*1024*1024]" >/sys/module/zfs/parameters/zfs_arc_max

Útil para probar un valor antes de fijarlo: si el rendimiento se hunde, reinicias y vuelves al de antes.

Cambio permanente

En /etc/modprobe.d/zfs.conf, con el valor solo en bytes:

options zfs zfs_arc_max=8589934592

Ese número son 8 GiB, es decir 8 por 2^30.

La trampa de zfs_arc_min

Important: “In case your desired zfs_arc_max value is lower than or equal to zfs_arc_min (which defaults to 1/32 of the system memory), zfs_arc_max will be ignored unless you also set zfs_arc_min to at most zfs_arc_max - 1.”

Aquí está la razón por la que a mucha gente “no le funciona” el límite. En un servidor con 512 GiB de RAM, zfs_arc_min por defecto son 16 GiB; si pides zfs_arc_max=8 GiB, el valor es menor que el mínimo y se ignora en silencio: ni error, ni aviso en el log, simplemente el ARC sigue creciendo. El ejemplo de abajo aplica a sistemas con más de 256 GiB de RAM, donde el zfs_arc_min por defecto ya supera los 8 GiB.

echo "$[8 * 1024*1024*1024 - 1]" >/sys/module/zfs/parameters/zfs_arc_min
echo "$[8 * 1024*1024*1024]"     >/sys/module/zfs/parameters/zfs_arc_max

Y el paso que casi todos olvidan

Important: “If your root file system is ZFS, you must update your initramfs every time this value changes.” Y: “You must reboot to activate these changes.”

update-initramfs -u -k all

El motivo es que con root en ZFS el módulo zfs se carga desde el initramfs, y ahí dentro va una copia de /etc/modprobe.d/. Editar el fichero del sistema sin regenerar el initramfs deja al módulo cargándose con los parámetros antiguos.

flowchart TD
    A["Quiero limitar el ARC"] --> B{"Es un cambio permanente?"}
    B -->|"No: solo este arranque"| G["echo N en /sys/module/zfs/parameters/zfs_arc_max"]
    B -->|"Si"| C["Editar /etc/modprobe.d/zfs.conf con options zfs zfs_arc_max=N"]
    C --> H{"zfs_arc_max es menor o igual que zfs_arc_min?"}
    H -->|"Si: min por defecto es RAM entre 32"| I["Bajar tambien zfs_arc_min a zfs_arc_max menos 1"]
    I --> J{"El root FS esta en ZFS?"}
    H -->|"No"| J
    J -->|"Si"| K["update-initramfs -u -k all"]
    K --> L["Reiniciar el nodo"]
    J -->|"No"| L
    L --> M["Verificar con cat /sys/module/zfs/parameters/zfs_arc_max"]

Swap sobre ZFS y vm.swappiness

El instalador crea rpool/swap como zvol, y la propia documentación advierte de lo que eso implica:

“Swap-space created on a zvol may generate some troubles, like blocking the server or generating a high IO load, often seen when starting a Backup to an external Storage.”

“We strongly recommend to use enough memory… Should you need or want to add swap, it is preferred to create a partition on a physical disk and use it as a swap device.”

El problema de fondo es una dependencia circular: el kernel necesita memoria para escribir en swap, y el zvol necesita memoria de ZFS para aceptar esa escritura. Bajo presión de memoria eso se convierte en un bloqueo, y el síntoma clásico es un nodo que se congela justo al lanzar un backup grande, como los que verás en el capítulo 13. La mitigación mientras tanto es reducir la agresividad del swapping:

sysctl -w vm.swappiness=10

Y para que persista, en un fichero nuevo /etc/sysctl.d/99-swappiness.conf:

vm.swappiness = 10
ValorEstrategia
vm.swappiness = 0El kernel solo hace swap para evitar un OOM
vm.swappiness = 1Swapping mínimo, sin desactivarlo del todo
vm.swappiness = 10Recomendado cuando hay memoria suficiente
vm.swappiness = 60Default del kernel
vm.swappiness = 100Swapping agresivo

Scrub, resilver y sustitución de un disco de arranque

Un scrub lee todos los bloques del pool, verifica sus checksums y repara con la redundancia disponible lo que esté mal. Es la operación que convierte “tengo redundancia” en “sé que mi redundancia sirve”. Proxmox VE hereda de Debian el cron de zfsutils-linux: /etc/cron.d/zfsutils-linux lanza un scrub de los pools sanos el segundo domingo de cada mes a las 00:24, salvo que la propiedad de pool org.debian:periodic-scrub lo desactive. Si prefieres timers de systemd por pool:

systemctl enable [email protected] --now
systemctl enable [email protected] --now

Manualmente: zpool scrub tank para lanzarlo, zpool scrub -s tank para pararlo y zpool status -v tank para ver el progreso y los errores por dispositivo.

Sustituir un disco de datos

Con zpool replace -f <pool> <old-device> <new-device>. Y para seguir el proceso: “Use the zpool status -v command to monitor how far the resilvering process of the new disk has progressed.”

Sustituir un disco de arranque

Este caso es distinto porque el disco no solo tiene datos del pool: tiene la partición EFI desde la que arranca el nodo. Reemplazar solo la partición ZFS deja un nodo que funciona hasta que se apaga y ya no vuelve.

# 1. Averiguar con qué bootloader arranca el sistema
proxmox-boot-tool status

# 2. Clonar la tabla de particiones de un disco sano al nuevo y darle GUIDs nuevos
sgdisk <healthy bootable device> -R <new device>
sgdisk -G <new device>

# 3. Reemplazar la partición ZFS dentro del pool
zpool replace -f <pool> <old zfs partition> <new zfs partition>

# 4. Preparar la ESP del disco nuevo y registrarla
proxmox-boot-tool format <new disk's ESP>
proxmox-boot-tool init <new disk's ESP> [grub]

sgdisk -R replica la tabla exacta, y sgdisk -G es obligatorio a continuación: sin él los dos discos compartirían los mismos GUID de partición y el sistema se confundiría al identificarlos.

Note: “Make sure to pass grub as mode to proxmox-boot-tool init if proxmox-boot-tool status indicates your current disks are using GRUB, especially if Secure Boot is enabled!”

La ESP es la EFI System Partition, la partición número 2 en los discos arrancables creados por el instalador desde PVE 5.4. En sistemas instalados con PVE 6.3 o anterior y nunca migrados, que siguen con GRUB plano, el paso equivalente es grub-install <new disk>. El Secure Boot y el arranque los viste en el capítulo 4 desde el lado del guest; aquí es el del host.

ZED: alertas por correo cuando el pool se degrada

Un pool degradado que nadie mira es un pool sin redundancia esperando al segundo fallo. ZFS trae su propio demonio de eventos para eso:

“ZFS comes with an event daemon ZED, which monitors events generated by the ZFS kernel module. The daemon can also send emails on ZFS events like pool errors. Newer ZFS packages ship the daemon in a separate zfs-zed package, which should already be installed by default in Proxmox VE.”

La configuración vive en /etc/zfs/zed.d/zed.rc y la variable imprescindible es ZED_EMAIL_ADDR, que viene definida como root:

ZED_EMAIL_ADDR="root"

“Proxmox VE forwards mails to root to the email address configured for the root user.”

Es decir, el circuito funciona si el usuario root@pam tiene un email configurado y el nodo sabe entregar correo. Esa segunda parte se monta en el capítulo 15. Otras variables del zed.rc:

VariableSignificado
ZED_EMAIL_ADDRDirecciones del administrador, separadas por espacios. Solo se envía correo si está definida
ZED_EMAIL_PROGEjecutable que envía el correo. Default mail
ZED_EMAIL_OPTSOpciones. Default -s '@subject@' @address@, con sustitución de @address@ y @subject@
ZED_NOTIFY_VERBOSE0 no notifica si el pool está sano; 1 notifica siempre

Los scripts de evento están en /etc/zfs/zed.d/, como enlaces a /usr/libexec/zfs/zed.d/. Tras editar la configuración, systemctl restart zfs-zed. Poner ZED_NOTIFY_VERBOSE=1 durante un tiempo es la forma sencilla de comprobar que el circuito de correo funciona sin tener que romper un disco a propósito.

zpool upgrade, features y el riesgo con GRUB

Tras cada actualización mayor, zpool status empieza a sugerir que el pool puede actualizarse a nuevas features. La documentación es tajante sobre si conviene:

“Changes to the on-disk format in ZFS are only made between major version changes and are specified through features.”

“Unless you need to use one of the new features, there is no upside to enabling them.”

Y las desventajas, literales:

  • “A system with root on ZFS, that still boots using GRUB will become unbootable if a new feature is active on the rpool, due to the incompatible implementation of ZFS in GRUB.”
  • “The system will not be able to import any upgraded pool when booted with an older kernel.”
  • “Booting an older Proxmox VE ISO to repair a non-booting system will likewise not work.”

Important: “Do not upgrade your rpool if your system is still booted with GRUB, as this will render your system unbootable. This includes systems installed before Proxmox VE 5.4, and systems booting with legacy BIOS boot.”

Los dos últimos puntos son los que muerden en una emergencia: si actualizas el pool y luego necesitas arrancar un ISO antiguo para reparar el sistema, ese ISO ya no podrá importar tu pool. Antes de escribir el comando, comprueba siempre con qué bootloader arranca el nodo:

proxmox-boot-tool status
zpool upgrade <pool>

Regla práctica: actualiza los pools de datos si una feature nueva te hace falta, y deja rpool como está salvo que proxmox-boot-tool status confirme que arrancas por systemd-boot y sepas exactamente qué ganas.

Replicación con pvesr: HA sin storage compartido

Hasta aquí, ZFS local. Ahora el motivo por el que ZFS es tan popular en clusters pequeños de Proxmox VE.

“The pvesr command-line tool manages the Proxmox VE storage replication framework. Storage replication brings redundancy for guests using local storage and reduces migration time. It replicates guest volumes to another node so that all data is available without using shared storage. Replication uses snapshots to minimize traffic sent over the network. Therefore, new data is sent only incrementally after the initial full sync.”

La tabla de backends soportados de la documentación tiene exactamente una fila: ZFS local, plugin zfspool, con snapshots y marcado como estable. Solo zfspool. Ningún otro backend soporta pvesr, y esa es la razón por la que ZFS aparece en tantos clusters de tres nodos sin SAN.

Cómo funciona un ciclo

sequenceDiagram
    participant A as nodoA origen
    participant Z as zfs local en A
    participant B as nodoB destino
    A->>Z: zfs snapshot __replicate_100-0_ts__
    A->>B: zfs send incremental desde el snapshot anterior
    B-->>A: recepcion correcta
    A->>Z: rotar snapshots antiguos del job
    B->>B: rotar snapshots antiguos del job
    Note over A,B: Siguiente ciclo segun el schedule
    A->>B: zfs send incremental
    B-->>A: error de red o sin espacio en destino
    Note over A,B: El job pasa a estado error
    A->>B: reintento automatico a los 30 minutos

Los snapshots que crea el framework se llaman __replicate_<jobid>_<timestamp>__, y listarlos con zfs list -t snapshot | grep __replicate_ es la forma más rápida de saber si un job está vivo.

Reglas del framework

  • Intervalo mínimo 1 minuto, máximo una vez por semana.
  • El formato del schedule es un subconjunto de los systemd calendar events.
  • El schedule por defecto al crear un job es */15, es decir cada 15 minutos.
  • Se puede replicar a varios nodos destino, pero no dos veces al mismo nodo.
  • Cada job admite límite de tasa con --rate, en MB/s.
  • El ID del job es <VMID>-<número> (por ejemplo 100-0) y es único en todo el cluster.
  • La configuración vive en /etc/pve/replication.cfg, dentro de pmxcfs, así que se replica sola a todos los nodos.

Requisito que se olvida y rompe jobs: el storage debe existir con el mismo storage ID en el nodo destino. Un local-zfs en origen y un zfs-vm en destino no valen, aunque ambos apunten a pools válidos.

Comandos

# Crear un job: VMID 100, job 0, destino pve1, cada 5 minutos, limitado a 10 MB/s
pvesr create-local-job 100-0 pve1 --schedule "*/5" --rate 10

pvesr list                            # todos los jobs
pvesr status                          # estado y última ejecución
pvesr update 100-0 --schedule '*/00'  # pasar a horario
pvesr disable 100-0
pvesr enable  100-0
pvesr run --id 100-0                  # ejecutar ahora
pvesr schedule-now --id 100-0
pvesr delete 100-0

Red dedicada para la replicación

Por defecto el tráfico de replicación usa la red de migración de guests y, si no hay ninguna configurada, la de gestión. Eso significa que una réplica cada minuto de una VM ocupada puede competir con corosync, con la GUI y con el tráfico de las propias VMs. Desde la interfaz se cambia en Datacenter -> Options -> Replication Settings; en datacenter.cfg:

# use dedicated replication network
replication: secure,network=10.1.2.0/24

La posibilidad de una red dedicada solo para replicación, separada de la de migración, llegó en PVE 9.0.

Errores y reintentos

“If a replication job encounters problems, it is placed in an error state. In this state, the configured replication intervals get suspended temporarily. The failed replication is repeatedly tried again in a 30 minute interval.”

Las tres causas que la documentación enumera: la red no funciona, no queda espacio libre en el storage destino, o no existe un storage con el mismo storage ID en el nodo destino.

Gotcha: ese reintento cada 30 minutos sustituye a tu schedule mientras el job esté en error. Si tenías réplicas cada minuto porque tu ventana de pérdida tolerable era de un minuto, un job en error la convierte en media hora sin que nadie te avise.

pvesr status
journalctl -u pvesr -b
zfs list -t snapshot | grep __replicate_

Y un detalle de configuración de VMs: desde PVE 5.0 la replicación exige que las imágenes de disco estén en un storage de tipo zfspool, así que añadir un disco en otro storage a una VM con replicación configurada obliga a marcar la opción Skip replication en ese disco concreto. Si no, el job entra en error de forma permanente. Los discos de VM y sus opciones los viste en el capítulo 3.

Recuperación manual cuando el nodo origen muere

Si el nodo con la VM se ha caído y no usas HA, los datos ya están en el destino gracias a la última réplica, pero el fichero de configuración del guest sigue apuntando al nodo muerto y hay que moverlo a mano:

# En el nodo B, por SSH o por la shell web
pvecm status
pvecm expected 1      # SOLO si no hay quorum y no se puede recuperar de otra forma

mv /etc/pve/nodes/A/qemu-server/100.conf /etc/pve/nodes/B/qemu-server/100.conf
mv /etc/pve/nodes/A/lxc/200.conf         /etc/pve/nodes/B/lxc/200.conf

qm start 100
pct start 200

Warning: “Avoid changes which affect the cluster if expected votes are set (for example adding/removing nodes, storages, virtual guests) at all costs.”

pvecm expected 1 desactiva la protección de quorum, que es justo lo que impide que dos mitades de un cluster partido escriban a la vez. Úsalo para arrancar los guests y devuelve el cluster a su estado normal en cuanto puedas.

Replicación + HA: qué ganas y qué pierdes

La documentación de HA pide “shared storage for VMs and containers”. La replicación ZFS es la alternativa reconocida cuando no lo tienes, y viene con una advertencia que hay que aceptar antes de montarla:

Important: “High-Availability is allowed in combination with storage replication, but there may be some data loss between the last synced time and the time a node failed.”

La ventana de pérdida es, como máximo, el intervalo del job: con --schedule "*/1" pierdes hasta un minuto de escrituras. Eso es aceptable para un servidor de aplicaciones y no lo es para la base de datos de facturación. La decisión es de negocio, no técnica. Configuración típica en un cluster de tres nodos, apoyada en lo que verás en el capítulo 11:

# 1) Réplica cada minuto a los otros dos nodos
pvesr create-local-job 100-0 nodo2 --schedule "*/1"
pvesr create-local-job 100-1 nodo3 --schedule "*/1"

# 2) HA con afinidad hacia los nodos que tienen la réplica
ha-manager add vm:100 --state started
ha-manager rules add node-affinity vm100-zfs \
    --resources vm:100 --nodes "nodo1:3,nodo2:2,nodo3:1" --strict 1

La regla de afinidad importa: sin ella, el HA podría recuperar la VM en un nodo que no es destino de ninguna réplica, y ahí no hay datos que arrancar.

Efecto sobre la migración

  • Al migrar a un nodo que ya es destino de la réplica: “Only changes since the last replication (so-called deltas) need to be transferred”. La migración es rapidísima porque el disco ya está allí.
  • Además, “the replication direction automatically switches if you migrate a guest to the replication target node”. Si la VM 100 estaba en nodoA replicando a nodoB y la migras a nodoB, pasa a replicarse de nodoB a nodoA. No hay que reconfigurar nada.
  • Al migrar a un nodo no replicado: se transfiere el disco completo y después la replicación se reanuda hacia los destinos configurados.

Comparativa honesta de las tres estrategias

ZFS local + pvesrCeph hiperconvergidoNFS / iSCSI compartido
Nodos mínimos razonables2 para replicar, 3 para HA con quorum31 cabina + los nodos
Pérdida de datos en failoverHasta el intervalo del jobNingunaNinguna
Red dedicada necesariaRecomendableObligatoria y rápidaRecomendable
Punto único de falloNoNoLa cabina, salvo que sea redundante
Coste de entradaEl de los discos que ya tienesAlto: más discos y más redEl de la cabina
Snapshots y checksums localesDepende del backend

Si Ceph es lo que buscas, el capítulo 12 lo cubre entero. Y una nota operativa: antes de sacar un nodo del cluster con pvecm delnode, para sus jobs de replicación. La documentación lo dice explícitamente, y dejarlos activos deja jobs huérfanos apuntando a un nodo que ya no existe.

Errores comunes y diagnóstico

SíntomaCausa probableDiagnóstico y solución
El ARC no baja tras editar /etc/modprobe.d/zfs.confFalta update-initramfs y reboot, o zfs_arc_max es menor o igual que zfs_arc_mincat /sys/module/zfs/parameters/zfs_arc_max; ejecutar update-initramfs -u -k all y reiniciar
zfs_arc_max ignorado en un servidor con más de 256 GiB de RAMzfs_arc_min, que es RAM/32, es mayor que el máximo pedidoBajar también zfs_arc_min a zfs_arc_max - 1
El pool se llena mucho más rápido de lo calculadoAmplificación de escritura de zvol sobre RAIDZzfs get volsize,refreservation,used <pool>/vm-<vmid>-disk-X; migrar a mirrors o subir blocksize del storage
IOPS muy bajos con muchos discos en RAIDZUn vdev RAIDZ rinde como un solo disco en IOPSRediseñar como varios vdevs mirror; ver zpool status
El sistema no arranca tras zpool upgradeGRUB no entiende las features nuevas del rpoolNunca hacer zpool upgrade del rpool con GRUB; comprobar antes con proxmox-boot-tool status
El nodo arranca pero no encuentra el disco tras cambiar hardwarePool creado con nombres /dev/sdXReimportar con zpool import -d /dev/disk/by-id <pool>
Nodo sustituido: el pool está sano pero no arrancaSolo se reemplazó la partición ZFS, no la ESPproxmox-boot-tool status; proxmox-boot-tool format e init de la ESP nueva
El special device se llenó enseguidaspecial_small_blocks mayor o igual que el recordsize del datasetzfs get special_small_blocks,recordsize <dataset>; bajarlo a 4K u 8K
Activé compresión y no se liberó espacioLos datos existentes no se comprimen retroactivamenteReescribir los volúmenes, por ejemplo con qm move-disk de ida y vuelta
El nodo se congela al lanzar un backup grandeSwap sobre zvol bajo presión de memoriaMover la swap a una partición física; vm.swappiness = 10
Job de pvesr en estado errorRed caída, sin espacio en destino, o storage ID inexistente en el destinopvesr status; journalctl -u pvesr -b; corregir y pvesr run --id <job>; reintenta solo cada 30 min
Replicación configurada pero un disco no se replicaEse disco no está en un storage zfspoolMarcar Skip replication en el disco o moverlo a ZFS
Migración falla con target storage does not existEl storage local no tiene el mismo ID en el destinopvesm status en ambos nodos; crear el storage con el mismo ID
Checksum errors en volúmenes de un dataset cifradoLimitación conocida del cifrado nativo con snapshots y ZVOLsEs experimental; openzfs/zfs#11688. Restaurar de backup y evitar cifrado nativo en producción
Nunca llegan avisos de un pool degradadoZED_EMAIL_ADDR sin definir o el nodo no entrega correoRevisar /etc/zfs/zed.d/zed.rc, systemctl restart zfs-zed, y el email del usuario root@pam

Checklist antes de dar un pool por productivo

  • Discos conectados a un HBA en modo IT, sin controladora RAID con caché en medio.
  • Pool creado con ashift=12 y rutas de /dev/disk/by-id/.
  • Layout de mirrors si el pool va a alojar discos de VM.
  • compression=lz4 activada antes de mover datos dentro.
  • blocksize del storage zfspool decidido y verificado con zfs get volblocksize sobre un disco real.
  • zfs_arc_max fijado en /etc/modprobe.d/zfs.conf, con update-initramfs -u -k all y reinicio hechos.
  • Scrub programado, sea por el cron de zfsutils-linux o por timers de systemd.
  • zfs-zed con ZED_EMAIL_ADDR configurado y una notificación de prueba recibida.
  • Si hay cluster: jobs de pvesr creados, storage con el mismo ID en todos los destinos y red de replicación dedicada en datacenter.cfg.
  • Si hay HA sobre replicación: ventana de pérdida de datos aceptada y regla de afinidad hacia los nodos con réplica.

Lo que queda funcionando

Con este capítulo tienes decidido el layout del pool con criterio y no por costumbre, sabes qué parámetros son irreversibles y cuáles no, tienes el ARC acotado de forma que sobreviva al reinicio, un scrub programado con alertas por correo, un procedimiento probado para cambiar un disco de arranque, y jobs de replicación llevando los zvol de tus guests a otro nodo.

Lo que todavía no tienes es la red dedicada por la que ese tráfico debería viajar. Toda la conversación sobre replicación, migración y corosync asume interfaces separadas, bridges correctos, VLANs donde toca y bonding donde importa la disponibilidad del enlace. Eso es lo que monta el capítulo 9: bridges Linux, VLAN-aware, bonding, NAT y las overlays de SDN de Proxmox VE.