ZFS: Añadir discos — la trampa de ‘zpool add’ que genera desequilibrio

¿Te fue útil?

Suena la alarma porque “el almacenamiento está lento”. La gráfica muestra que la latencia subió, pero solo a veces. El equipo de la aplicación
asegura que no cambiaron nada.
Inicias sesión, ejecutas zpool status, y ahí está: un vdev nuevo brillante añadido la semana pasada, apoyado junto a vdevs más antiguos y llenos como un nuevo contratado
que recibió todos los tickets fáciles y aun así rompió producción.

ZFS hace que sea fácil añadir capacidad. También hace fácil crear una disposición del pool que quede permanentemente desequilibrada en rendimiento y riesgo.
La trampa es simple: zpool add añade un vdev al pool, pero ZFS no redistribuye automáticamente los datos existentes entre vdevs.
Esa decisión de diseño es la razón por la que tu “expansión rápida” se convierte en el incidente de almacenamiento del próximo trimestre.

La trampa de «Add VDEV»: qué ocurre realmente

Cuando ejecutas zpool add, no estás “añadiendo discos” a un grupo de redundancia existente. Estás añadiendo un vdev de nivel superior completamente nuevo
al pool. ZFS entonces stripea las nuevas escrituras a través de los vdevs de nivel superior según el espacio y algunas heurísticas, pero no mueve los bloques antiguos.
Los vdevs antiguos permanecen llenos de datos antiguos; el vdev nuevo comienza vacío y absorbe muchas de las nuevas asignaciones.

Esto parece inocuo hasta que recuerdas qué significa un vdev de nivel superior: las IOPS del pool, el throughput y la tolerancia a fallos son la suma (o el mínimo)
de sus vdevs de nivel superior. Añade un vdev con diferente ancho, otra clase de disco, distinto ashift o un perfil de salud diferente, y has cambiado el
comportamiento del pool para el resto de su vida.

Un pool, muchas personalidades

Los pools ZFS no son “un RAID”. Un pool es una colección de vdevs de nivel superior. Cada vdev de nivel superior tiene su propia redundancia (mirror, raidz, dRAID),
y el pool hace striping entre ellos. Ese es el modelo. Es poderoso. También es cómo la gente construye accidentalmente un quimera de almacenamiento:
tres vdevs RAIDZ2 anchos, luego un mirror único “solo para añadir capacidad rápido”, luego un vdev SSD especial “por metadatos”, y ahora el pool
se comporta como un comité donde todos votan y la persona más lenta sigue bloqueando la reunión.

Por qué ZFS no rebalancea por defecto

La ausencia de auto-rebalanceo no es pereza; es conservadurismo. Mover bloques en un pool en vivo es costoso, desgasta unidades, arriesga casos límite por pérdida de energía
y complica las garantías. ZFS prefiere mantener punteros a donde ya viven los datos. Optimiza para corrección y supervivencia, no para “quedar bonito”.

La consecuencia operativa es directa: si añades un vdev y luego te preguntas por qué un vdev hace todo el trabajo, la respuesta es «porque está vacío y estás escribiendo
datos nuevos». Si te preguntas por qué las lecturas siguen golpear vdevs antiguos, es porque los datos antiguos siguen allí. ZFS no está siendo misterioso.
Está siendo literal.

Hechos y contexto que explican el comportamiento

  • ZFS nació en Sun a principios de los 2000, diseñado para integridad de datos end-to-end con checksums en cada bloque, no solo «RAID rápido».
  • Los pools fueron un cambio radical: en vez de tallar LUNs de grupos RAID fijos, agregas vdevs y asignas dinámicamente.
  • Copy-on-write es la mecánica central: ZFS nunca sobreescribe bloques en vivo; escribe nuevos bloques y cambia punteros. Genial para snapshots, complejo para «rebalance».
  • Los vdevs de nivel superior son la unidad de fallo: pierdes un vdev de nivel superior, pierdes el pool. Por eso un vdev de un solo disco es una bomba de tiempo.
  • RAIDZ no es RAID5/6 en detalles de implementación; es stripe variable con paridad, interactuando con recordsize y patrones de asignación de formas que sorprenden a quienes migran desde RAID hardware.
  • Ashift es para siempre por vdev: elegir la alineación de sector equivocada puede fijar amplificación de escritura durante la vida de ese vdev.
  • Los vdevs especiales (metadata/pequeños bloques) hicieron más práctico los pools híbridos, pero también introdujeron un nuevo componente cuyo fallo puede dejar el pool inservible a menos que esté en mirror.
  • L2ARC nunca fue un caché de escritura: es un caché de lectura y se reinicia en reinicio salvo que se habilite L2ARC persistente y sea soportado. La gente todavía lo trata como RAM mágica.
  • El comportamiento de resilver difiere según tipo de vdev: mirrors resilverean el espacio usado; RAIDZ resilverea más y toca más la geometría del vdev.

Por qué el desequilibrio perjudica: rendimiento, resiliencia y coste

Rendimiento: el pool es tan fluido como su vdev más lento

En un pool equilibrado, tus IOPS y throughput escalan al añadir más vdevs de nivel superior de capacidad similar. En un pool desequilibrado, obtienes rarezas:
picos de buen rendimiento seguidos de saltos de latencia cuando un vdev se satura mientras otros están infrautilizados.

Aquí está el patrón clásico: añades un vdev nuevo, las escrituras nuevas caen mayoritariamente ahí, y tu monitorización se ve bien. Luego la carga cambia a lecturas de datos antiguos
(restauraciones de backup, trabajos analíticos que tocan particiones históricas, o una flota de VMs arrancando desde imágenes antiguas). De repente los vdevs antiguos se vuelven hotspots de lectura,
y el vdev nuevo está ocioso.

Resiliencia: mezclar niveles de redundancia es comprar riesgo con CAPEX

La tolerancia a fallos de un pool no es la “redundancia media”. Es la redundancia de cada vdev de nivel superior, y perder cualquiera de ellos mata el pool.
Añade un vdev mirror simple junto a varios vdevs RAIDZ2, y acabas de introducir un eslabón más débil: un mirror de dos discos sobrevive a un fallo,
mientras tus RAIDZ2 sobreviven a dos. Ahora tienes dominios de fallo desiguales.

Añade un vdev de un solo disco «temporalmente» y felicitaciones: acabas de crear un pool que está a un fallo de disco de perderse completamente.
La palabra «temporal» tiene una vida media larga en infraestructura.

Coste: pagas dos veces — por los discos y por las consecuencias

Los pools desequilibrados son caros de maneras aburridas: más tiempo on-call, más escalados, más «por qué este host es más lento» en debugging, más upgrades prematuros.
Y si tienes que «arreglar» el layout, la solución suele ser disruptiva: migrar datos, reconstruir pools o reescrituras controladas de bloques que toman semanas.

Broma #1: Añadir un vdev desajustado a un pool ZFS es como poner una rueda de repuesto en un coche de carreras—sí, rueda, y sí, todos la escuchan.

Guía de diagnóstico rápido (primera/segunda/tercera comprobación)

Cuando alguien dice “ZFS se volvió lento después de añadir discos”, no empieces por sintonizar recordsize. Empieza por probar si el pool está desequilibrado y dónde se va el tiempo.

Primera: ¿Un vdev de nivel superior está haciendo todo el trabajo?

  • Comprueba I/O y latencia por vdev con zpool iostat -v.
  • Busca un vdev con busy/await consistentemente más alto que los demás.
  • Si lo ves, para. No estás depurando “rendimiento ZFS”. Estás depurando “disposición y asignación”.

Segunda: ¿Es bound por lectura, por escritura o por sync?

  • Mix lectura/escritura: zpool iostat -v 1 y arcstat (si está disponible) para ver tasa de aciertos del ARC y presión de lectura.
  • Escrituras sync: comprueba zfs get sync y si existe un SLOG y está sano.
  • Si los picos de latencia se correlacionan con ráfagas de escrituras sync, estás mirando ZIL/SLOG o latencia de escritura en la capa subyacente.

Tercera: ¿Realmente estás bloqueado en la capa de dispositivo?

  • Revisa estadísticas de dispositivos del kernel: iostat -x y contadores de errores con smartctl.
  • Confirma ashift y tamaños de sector. Un vdev nuevo con tamaño de sector físico distinto puede comportarse diferente bajo carga.
  • Busca travesuras silenciosas: un enlace de controlador degradado, una unidad en modo SATA 1.5G o NCQ deshabilitado.

Tareas prácticas (comandos, salidas, decisiones)

Debajo hay tareas reales que puedes ejecutar en un host Linux con OpenZFS típico. Cada una incluye qué significa la salida y qué decisión tomar.
Úsalas como una lista de verificación cuando estés privado de sueño y la ventana de cambios esté cerrándose.

Tarea 1: Mostrar la topología del pool y detectar el “vdev nuevo” inmediatamente

cr0x@server:~$ sudo zpool status -v tank
  pool: tank
 state: ONLINE
  scan: scrub repaired 0B in 03:12:44 with 0 errors on Tue Dec 10 02:40:11 2025
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          raidz2-0                  ONLINE       0     0     0
            sda                     ONLINE       0     0     0
            sdb                     ONLINE       0     0     0
            sdc                     ONLINE       0     0     0
            sdd                     ONLINE       0     0     0
            sde                     ONLINE       0     0     0
            sdf                     ONLINE       0     0     0
          raidz2-1                  ONLINE       0     0     0
            sdg                     ONLINE       0     0     0
            sdh                     ONLINE       0     0     0
            sdi                     ONLINE       0     0     0
            sdj                     ONLINE       0     0     0
            sdk                     ONLINE       0     0     0
            sdl                     ONLINE       0     0     0
          mirror-2                  ONLINE       0     0     0
            nvme0n1p2               ONLINE       0     0     0
            nvme1n1p2               ONLINE       0     0     0
errors: No known data errors

Significado: Este pool tiene dos vdevs RAIDZ2 grandes y después un mirror. Ese mirror es de otra clase (NVMe) y tiene un perfil de redundancia distinto.

Decisión: Trátalo como un pool heterogéneo. Espera sesgo en las asignaciones y “modos” de rendimiento. Si ese mirror se añadió por capacidad, planifica una migración
o un enfoque deliberado de rebalance en lugar de más chapuzas.

Tarea 2: Identificar qué vdev está caliente (IOPS y ancho de banda)

cr0x@server:~$ sudo zpool iostat -v tank 1 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        82.1T  21.9T  3.21K  1.05K   410M   198M
  raidz2-0                  41.0T  3.7T   2.80K   190    360M    31M
    sda                         -      -    470    32     61M   5.2M
    sdb                         -      -    458    30     59M   5.1M
    sdc                         -      -    472    31     61M   5.1M
    sdd                         -      -    469    33     60M   5.3M
    sde                         -      -    467    31     60M   5.2M
    sdf                         -      -    464    33     59M   5.2M
  raidz2-1                  40.9T  3.8T     380   175     49M    29M
    sdg                         -      -     64    29    8.1M   4.8M
    sdh                         -      -     63    30    8.0M   4.9M
    sdi                         -      -     62    28    8.0M   4.7M
    sdj                         -      -     64    29    8.2M   4.8M
    sdk                         -      -     63    29    8.0M   4.8M
    sdl                         -      -     64    30    8.1M   4.9M
  mirror-2                    250G   850G    30    690    1.2M   138M
    nvme0n1p2                   -      -     15   345   0.6M    69M
    nvme1n1p2                   -      -     15   345   0.6M    69M
--------------------------  -----  -----  -----  -----  -----  -----

Significado: Las lecturas están dominadas por raidz2-0. Las escrituras están dominadas por el mirror NVMe. Ese es el clásico comportamiento de “el vdev nuevo absorbe escrituras”
y “los vdevs antiguos sirven lecturas”.

Decisión: Si las quejas de latencia son por lecturas, añadir más vdevs orientados a escritura no lo arreglará. Necesitas mover datos calientes,
añadir más vdevs RAIDZ similares o reconsiderar el diseño del pool.

Tarea 3: Confirmar el sesgo de asignación y su gravedad

cr0x@server:~$ sudo zpool list -v tank
NAME         SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
tank        104T  82.1T  21.9T        -         -    28%    78%  1.00x  ONLINE  -
  raidz2-0  45.5T  41.0T  3.7T        -         -    34%    91%      -  ONLINE
  raidz2-1  45.5T  40.9T  3.8T        -         -    31%    90%      -  ONLINE
  mirror-2  1.09T   250G   850G       -         -    2%     22%      -  ONLINE

Significado: Dos vdevs grandes están ~90% llenos, el mirror nuevo está ~22% usado. Eso no es “capacidad equilibrada”, por lo tanto no será carga equilibrada
a menos que tu carga consista mágicamente solo de escrituras nuevas.

Decisión: No sigas añadiendo vdevs pequeños esperando que ZFS “lo distribuya”. Decide si vas a añadir más vdevs equivalentes de la misma clase/anchura
o migrar a un pool nuevo con un layout limpio.

Tarea 4: Comprobar ashift y detectar el problema de “vdev nuevo diferente”

cr0x@server:~$ sudo zdb -C tank | sed -n '1,120p'
MOS Configuration:
        version: 5000
        name: 'tank'
        state: 0
        txg: 1293387
        pool_guid: 14772854026870511222
        vdev_children: 3
        vdev_tree:
            type: 'root'
            id: 0
            guid: 14772854026870511222
            children[0]:
                type: 'raidz'
                ashift: 12
                nparity: 2
                children[0]: type: 'disk' path: '/dev/sda'
                ...
            children[2]:
                type: 'mirror'
                ashift: 13
                children[0]: type: 'disk' path: '/dev/nvme0n1p2'
                children[1]: type: 'disk' path: '/dev/nvme1n1p2'

Significado: Los vdevs RAIDZ tienen ashift=12 (4K). El mirror tiene ashift=13 (8K). No es automáticamente incorrecto, pero es una diferencia de comportamiento permanente.

Decisión: Si ves desajustes de ashift accidentales por detección errónea de sectores o por mezcla de tipos de dispositivo, deja de hacer “arreglos incrementales”.
Planifica una reconstrucción con ashift correcto en lugar de apilar más inconsistencia.

Tarea 5: Verificar propiedades de datasets que afectan la latencia (sync, recordsize, compression)

cr0x@server:~$ sudo zfs get -r compression,recordsize,sync tank/vmstore
NAME         PROPERTY     VALUE     SOURCE
tank/vmstore compression  lz4       local
tank/vmstore recordsize   16K       local
tank/vmstore sync         standard  default

Significado: Valores razonables para VMs: compresión lz4 y recordsize 16K. Sync es standard, lo que significa que las escrituras síncronas se respetan.

Decisión: Si ves latencia por escrituras sync y no tienes SLOG, considera añadir un SLOG en mirror sobre dispositivos con protección ante pérdida de energía.
No “arregles” la latencia sync poniendo sync=disabled a menos que estés dispuesto a explicar pérdida de datos en el postmortem.

Tarea 6: Comprobar si existe un SLOG y si es un punto único de dolor

cr0x@server:~$ sudo zpool status tank | sed -n '1,80p'
  pool: tank
 state: ONLINE
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          raidz2-0                  ONLINE       0     0     0
          raidz2-1                  ONLINE       0     0     0
          mirror-2                  ONLINE       0     0     0
        logs
          mirror-3                  ONLINE       0     0     0
            nvme2n1p1               ONLINE       0     0     0
            nvme3n1p1               ONLINE       0     0     0

Significado: Hay un dispositivo de registro en mirror. Bien: no es un SLOG de un solo disco. Además, está separado del mirror de nivel superior.

Decisión: Si los logs aparecen como un solo disco, arréglalo antes de cualquier otra cosa. Un SLOG único es una interrupción esperando ocurrir,
y un SLOG lento es una fábrica de latencia para cargas con mucho sync.

Tarea 7: Medir latencia real y colas en el nivel del SO

cr0x@server:~$ iostat -x 1 3
Linux 6.8.0 (server)  12/25/2025  _x86_64_  (32 CPU)

Device            r/s     w/s   rMB/s   wMB/s  avgrq-sz avgqu-sz   await  r_await  w_await  svctm  %util
sda             78.0    5.0    62.1     4.1     173.2     9.80   110.2    120.3     18.1   2.8   23.1
sdg             12.0    4.0     9.5     3.2     194.0     0.90    22.4     24.1     17.3   2.4    3.8
nvme0n1          1.0  420.0     0.2   132.0      64.0     1.10     2.6      1.9      2.6   0.1    4.2

Significado: sda tiene await alto y profundidad de cola comparado con otros discos. Eso suele significar que el vdev al que pertenece está saturado.

Decisión: Confirma que esto se alinea con zpool iostat -v. Si es así, tienes un vdev caliente.
Si no, podrías tener problemas de controlador o de path específicos de ese disco.

Tarea 8: Comprobar presión del ARC y si las lecturas fallan en caché

cr0x@server:~$ sudo arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  size     c
12:01:11  3200  1200     37   800   25   350   11    50    2  118G  128G
12:01:12  3400  1500     44   980   29   420   12    60    2  118G  128G
12:01:13  3350  1480     44   950   28   430   13    60    2  118G  128G

Significado: La tasa de misses del caché es ~40%+ bajo carga. Eso puede convertir tu historia de “vdev nuevo rápido” en “las lecturas en vdevs antiguos nos están matando”, porque el ARC
no te está salvando.

Decisión: Si los misses de lectura se correlacionan con latencia, considera añadir RAM (a menudo la mejor mejora de IOPS por dolar),
o separar datasets calientes en vdevs rápidos y redundantes en lugar de mezclarlos.

Tarea 9: Inspeccionar fragmentación y reconocer el precipicio de “RAIDZ casi lleno”

cr0x@server:~$ sudo zpool get -H -o property,value fragmentation tank
fragmentation	28%

Significado: La fragmentación es no trivial. En RAIDZ, alta ocupación + fragmentación puede amplificar el coste de escritura y la variabilidad de latencia.

Decisión: Si tus vdevs RAIDZ están al 85–90% y la frag aumenta, deja de intentar “tunear” eso. Añade vdevs equivalentes o migra.
La física de la asignación ganará.

Tarea 10: Ver si un dataset es el matón (y muévelo si es necesario)

cr0x@server:~$ sudo zfs list -o name,used,available,refer,compressratio -r tank | head -n 12
NAME                 USED  AVAIL  REFER  RATIO
tank                82.1T  21.9T   128K  1.22x
tank/vmstore        42.8T  21.9T  42.8T  1.05x
tank/backups        25.6T  21.9T  24.1T  1.48x
tank/analytics      11.9T  21.9T  10.7T  1.31x
tank/home           1.8T   21.9T   1.8T  1.63x

Significado: VMstore domina el espacio. Si la carga caliente está ahí, también domina el I/O.

Decisión: Considera mover tank/vmstore a un pool nuevo y bien diseñado y dejar los datasets fríos atrás.
Una separación quirúrgica vence a un “rebalance total” heroico.

Tarea 11: Comprobar errores silenciosos de dispositivo antes de culpar a la asignación

cr0x@server:~$ sudo smartctl -a /dev/sda | egrep "Reallocated_Sector_Ct|Current_Pending_Sector|Offline_Uncorrectable|UDMA_CRC_Error_Count"
Reallocated_Sector_Ct     0
Current_Pending_Sector    0
Offline_Uncorrectable     0
UDMA_CRC_Error_Count      27

Significado: Errores CRC sugieren problemas de cableado/backplane/controlador, no necesariamente medios que estén muriendo. Eso puede presentarse como “un vdev está lento.”

Decisión: Arregla la ruta física y limpia la tendencia de errores (reemplaza cable, vuelve a insertar, cambia de bahía) antes de rediseñar el pool.

Tarea 12: Confirmar que el “vdev nuevo” realmente recibe nuevas asignaciones

cr0x@server:~$ sudo zpool iostat -v tank 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        82.1T  21.9T   980   2.20K   210M   420M
  raidz2-0                  41.0T  3.7T    820    180    180M    40M
  raidz2-1                  40.9T  3.8T    150    160     28M    36M
  mirror-2                    250G   850G    10   1.86K   2.0M   344M
--------------------------  -----  -----  -----  -----  -----  -----

Significado: Las escrituras fluyen principalmente al vdev mirror. Eso es esperado cuando está más vacío y es más rápido.

Decisión: Si este mirror no está pensado para ser el sumidero principal de escrituras (por coste, resistencia o políticas),
necesitas rediseñar, no “esperar a que se equilibre”.

Tarea 13: Demuestra que no puedes eliminar después el vdev de nivel superior (la parte irreversible)

cr0x@server:~$ sudo zpool remove tank mirror-2
cannot remove mirror-2: operation not supported on this pool

Significado: La eliminación de vdev de nivel superior puede no estar soportada según tu versión de OpenZFS y flags de características del pool—e incluso cuando está soportada,
es limitada y puede durar mucho.

Decisión: Trata zpool add como permanente a menos que hayas validado soporte para remover vdevs en tu entorno
y puedas tolerar el tiempo y riesgo.

Tarea 14: Validar configuración de vdev especial (porque perderlo puede perder el pool)

cr0x@server:~$ sudo zpool status tank | sed -n '1,120p'
  pool: tank
 state: ONLINE
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          raidz2-0                  ONLINE       0     0     0
          raidz2-1                  ONLINE       0     0     0
        special
          mirror-4                  ONLINE       0     0     0
            nvme4n1p1               ONLINE       0     0     0
            nvme5n1p1               ONLINE       0     0     0

Significado: El vdev special está en mirror. Bien. Si fuera un único dispositivo y fallara, podrías perder metadatos/pequeños bloques y efectivamente perder el pool.

Decisión: Nunca uses un vdev special como dispositivo único en producción. Míralo en mirror, monitorízalo y dotalo de capacidad de sobra.

Tres mini-historias corporativas desde el campo

Mini-historia 1: El incidente causado por una suposición equivocada

Una empresa SaaS mediana tenía un pool ZFS que soportaba un clúster de virtualización. Tenían dos vdevs RAIDZ2 de HDD idénticos. El crecimiento fue constante.
Luego llegaron un cliente nuevo y el almacenamiento se disparó. Alguien preguntó lo obvio: “¿Podemos simplemente añadir discos?”

El ingeniero de guardia hizo lo que muchos haríamos a las 2 a.m.: añadió un vdev mirror nuevo usando dos discos de repuesto, porque era rápido y la capacidad era urgente.
Asumió que el pool “se reequilibraría”, o al menos “repartiría el I/O” con el tiempo. El pool se mantuvo online, las gráficas mejoraron, todos volvieron a dormir.

Dos semanas después, el equipo de plataforma desplegó una nueva imagen de VM y la flota arrancó en todo el clúster. Las tormentas de arranque son una fiesta de I/O aleatorio de pequeñas lecturas.
Las lecturas golpearon los datos antiguos en los vdevs RAIDZ casi llenos. La latencia se disparó. El mirror nuevo tenía espacio y velocidad, pero no ayudó porque contenía mayormente bloques nuevos.

El outage no fue misterioso; fue deuda topológica cobrada con intereses. Su solución no fue ajustar finamente. Migraron los datasets de VM más ocupados a un pool nuevo
construido con mirrors (que coincidía con la carga), y luego reutilizaron el pool antiguo para datos más fríos. La lección dolorosa: ZFS no te protege de tus propias suposiciones.

Mini-historia 2: La optimización que salió mal

Un equipo empresarial de analítica quería consultas más rápidas. Tenían un pool ZFS de vdevs RAIDZ2 HDD. Llegó una propuesta bien intencionada de optimización:
“Añadamos un par de NVMe en mirror como vdev, y ZFS stripeará por ahí. Boom: más rápido.”

Añadieron el mirror NVMe como vdev de nivel superior. Las escrituras nuevas (incluyendo datos temporales de spills de consultas) cayeron desproporcionadamente en NVMe. Al principio, se veía fantástico.
Pero las NVMe eran de consumo y no eran seguras ante pérdida de energía. Peor: ahora estaban en la senda caliente de escritura para una carga que hacía muchas escrituras síncronas
debido al comportamiento de las aplicaciones que no controlaban por completo.

Unos meses después, una NVMe empezó a lanzar errores de media. El mirror los protegió de pérdida inmediata de datos, pero el rendimiento se degradó bruscamente durante el manejo de errores
y la actividad de resilver. El impacto en el negocio fue “las consultas a veces tardan 10x más”, que es como se presentan los outages de analítica: no caído, pero inusable.

El retroceso fue sutil: no solo habían “añadido rendimiento”. Cambiaron la asignación, las características de fallo y el perfil operativo del pool entero.
Su solución eventual fue aburrida: mover el scratch analítico a un pool NVMe separado (mirrors), mantener el pool HDD para datos durables y hacer explícitas las semánticas de sync.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Una empresa financiera usaba ZFS para un archivo de documentos y una granja de VMs. Su responsable de almacenamiento era alérgico a “arreglos rápidos” e insistía en una política:
los vdevs de nivel superior deben ser idénticos en ancho, tipo y clase de disco; no hay excepciones sin una firma de riesgo por escrito.

La política molestaba porque hacía las expansiones más lentas. Cuando se acercaba la capacidad, no “añadían lo que hubiera”.
Compraron suficientes discos para añadir un nuevo vdev RAIDZ2 completo que coincidiera con los existentes y lo planificaron como un cambio con pruebas de burn-in.

Meses después tuvieron un problema de firmware del controlador que aumentaba intermitentemente la latencia en una ruta SAS. Porque sus vdevs eran consistentes,
las métricas contaron una historia clara: una ruta estaba mal; el layout del pool no enturbió la señal. Aislaron la ruta defectuosa, la pusieron en failover limpiamente,
y planificaron un arreglo de firmware.

La salvación no fue heroica; fue claridad. Vdevs homogéneos y cambios disciplinados hicieron que el sistema se comportara de forma predecible cuando el hardware se puso raro,
que es la única forma en que realmente descubres de qué está hecha tu arquitectura.

Cómo ampliar ZFS de forma segura (qué hacer en su lugar)

Regla 1: Añade vdevs de nivel superior que coincidan, o acepta la rareza permanente

Si vas a crecer un pool añadiendo vdevs, añade vdevs del mismo tipo, mismo ancho y clase de rendimiento similar. Mirrors con mirrors.
RAIDZ2 con RAIDZ2 del mismo número de discos. Ashift similar. Modelos de disco similares si puedes.

Esto no es estético. Es matemática operativa: el pool stripea entre vdevs, y cualquier vdev puede convertirse en el factor limitante según los patrones de acceso.
La homogeneidad hace que la planificación de capacidad y rendimiento sea tractable.

Regla 2: Si necesitas diferentes tipos de medios, usa pools separados o vdevs especiales deliberadamente

¿Quieres rendimiento NVMe y capacidad HDD? Tienes tres patrones sensatos:

  • Pools separados: un pool NVMe para datasets sensibles a latencia, un pool HDD para datos masivos. Sencillo, predecible y fácil de razonar.
  • Vdev special (en mirror): para metadatos y pequeños bloques, para acelerar operaciones de directorio y I/O aleatorio pequeño manteniendo los datos en HDD.
  • SLOG (en mirror, dispositivos con PLP): para acelerar la latencia de escrituras síncronas sin cambiar dónde terminan los datos.

Lo que debes evitar es “solo añadir un vdev rápido” y esperar que ZFS lo convierta en un sistema de niveles (tiering). No lo hará. Lo convertirá en un sumidero de asignación.

Regla 3: Considera reconstruir en lugar de parchear cuando el layout ya está comprometido

A veces la respuesta correcta es: construye un pool nuevo con el layout que querías y luego migra datasets. Sí, es trabajo. También es trabajo finito.
Vivir con un pool comprometido es trabajo infinito.

¿Y el «rebalance»?

ZFS no tiene un comando único online de rebalanceo que redistribuya bloques existentes entre vdevs del modo en que algunos sistemas distribuidos lo hacen.
Si quieres mover datos, por lo general tienes que reescribirlos.

Enfoques prácticos incluyen:

  • Send/receive de datasets a un pool nuevo (mejor cuando puedes aprovisionar un pool nuevo).
  • Reescritura a nivel de dataset in situ vía replicación a un dataset temporal y de vuelta (funciona, pero es pesada y operativamente arriesgada).
  • Migración selectiva de datasets calientes a un pool nuevo, dejando los datos fríos donde están.

Broma #2: ZFS no «rebalancea» tu pool; «preserva la historia». Como tu departamento de auditoría, recuerda todo y no mueve nada sin papeleo.

Listas de verificación / plan paso a paso

Paso a paso: Antes de ejecutar zpool add en producción

  1. Escribe el objetivo: ¿capacidad, IOPS, throughput o latencia? “Más espacio” no es un plan de rendimiento.
  2. Confirma la geometría actual de vdev: mirror vs raidz, conteo de discos, ashift, clases de dispositivo.
  3. Decide si puedes mantener vdevs homogéneos: si no, decide si aceptas heterogeneidad permanente.
  4. Revisa espacio libre y fragmentación: si ya estás en alta ocupación y fragmentado, espera peor comportamiento bajo escrituras.
  5. Valida salud del controlador y paths: no añadas complejidad encima de hardware inestable.
  6. Planifica el rollback: asume que no podrás eliminar el vdev más tarde; planifica cómo migrarías si sale mal.
  7. Prepara y burn-in de discos: ejecuta tests SMART largos, comprueba firmware, confirma tamaños de sector.

Paso a paso: Patrones de expansión seguros

  1. Necesitas más capacidad en un pool RAIDZ existente: añade un nuevo vdev RAIDZ que coincida en ancho y paridad.
  2. Necesitas más IOPS para cargas tipo VM: añade vdevs mirror (múltiples mirrors escalan bien IOPS).
  3. Necesitas latencia de escritura sync más rápida: añade un SLOG en mirror sobre dispositivos con protección ante pérdida de energía; no añadas un vdev rápido al azar.
  4. Necesitas aceleración de metadatos/pequeños bloques: añade un vdev special en mirror; configura special_small_blocks solo con un modelo de dimensionado y monitorización.
  5. Necesitas tiers rápidos y lentos: construye dos pools y coloca datasets explícitamente.

Paso a paso: Si ya caíste en la trampa

  1. Cuantifica el desequilibrio: %alloc por vdev, iostat por vdev bajo carga real.
  2. Identifica datasets calientes: qué datasets dominan I/O y las quejas de latencia.
  3. Elige una arquitectura objetivo: vdevs homogéneos, o pools separados por carga de trabajo.
  4. Planifica la migración: send/receive con snapshots incrementales, o mueve datasets calientes primero.
  5. Programa scrubs y resilvers: las expansiones y migraciones son cuando las unidades débiles se revelan.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: “Tras añadir discos, las lecturas siguen lentas”

Causa raíz: Añadiste un vdev nuevo, pero los datos antiguos se quedaron en los vdevs viejos; la carga de lectura sigue golpeando los vdevs antiguos y llenos.

Solución: Mueve datasets calientes a un pool nuevo o reescríbelos para que los bloques se reasignen. Si debes expandir in situ, añade vdevs que coincidan para aumentar el paralelismo de lectura donde viven los datos.

2) Síntoma: “Las escrituras se volvieron rápidas, luego empezamos a ver picos de latencia aleatorios”

Causa raíz: El vdev nuevo está tomando la mayoría de escrituras; los vdevs antiguos están casi llenos y fragmentados; mezclar tipos de medios causa tiempos de servicio desiguales.

Solución: Deja de mezclar clases de vdev para asignación general. Separa pools o usa SLOG/special vdev para aceleración dirigida.

3) Síntoma: “El rendimiento del pool es inconsistente entre hosts / momentos del día”

Causa raíz: Fases de la carga (leer datos antiguos vs escribir datos nuevos) interactúan con sesgos de asignación. El pool tiene “modos”.

Solución: Mide I/O por vdev durante cada fase de la carga. Coloca datasets según patrón de acceso, no según donde había espacio libre.

4) Síntoma: “El scrub o resilver se disparó tras la expansión”

Causa raíz: Vdevs con alta utilización + geometría RAIDZ + discos lentos = operaciones de mantenimiento largas. La expansión no redujo la ocupación de los vdevs antiguos.

Solución: Añade vdevs equivalentes antes de alcanzar alto %CAP. Mantén margen. Reemplaza discos envejecidos proactivamente. Considera mirrors para resilver más rápido si la carga lo exige.

5) Síntoma: “Añadimos un disco individual temporalmente y ahora tenemos miedo de tocar el pool”

Causa raíz: Un vdev de nivel superior de un solo disco deja el pool a un fallo de disco de pérdida total.

Solución: Convierte ese disco único en redundante de inmediato adjuntando un segundo disco (mirror), o migra datos fuera de ese pool.
No lo dejes para “más tarde”.

6) Síntoma: “El vdev nuevo es más rápido pero el pool global se volvió más lento”

Causa raíz: Ashift mezclados, tamaños de sector o amplificación de escritura en un vdev; además, comportamiento de metadatos/pequeños bloques puede concentrarse en ciertos dispositivos.

Solución: Valida ashift y propiedades de los dispositivos antes de añadir. Si están mal, reconstrúyelo con ashift correcto; no sigas añadiendo.

7) Síntoma: “Las escrituras síncronas son terribles, así que alguien propone sync=disabled”

Causa raíz: No hay SLOG o es lento/insalubre. El pool está respetando semánticas sync y pagando la latencia.

Solución: Añade un SLOG en mirror sobre dispositivos con protección ante pérdida de energía; confirma necesidades de la aplicación; mantén sync=standard para durabilidad a menos que aceptes explícitamente pérdida de datos.

FAQ (preguntas que se hacen después del incidente)

1) ¿»añadir discos» es lo mismo que «añadir un vdev» en ZFS?

Prácticamente, sí. O bien reemplazas discos dentro de un vdev existente (creciéndolo o renovándolo), o añades un vdev de nivel superior al pool.
zpool add añade un vdev de nivel superior. Eso cambia el comportamiento del pool permanentemente.

2) ¿ZFS rebalancea automáticamente los datos después de añadir un vdev?

No. Los bloques existentes se quedan donde están. Las nuevas asignaciones tienden a preferir vdevs con más espacio libre, por eso los vdevs nuevos suelen recibir la mayoría de escrituras nuevas.

3) Si añado un vdev más rápido, ¿las lecturas mejorarán?

Solo para datos que estén asignados en ese vdev (o cacheados en ARC/L2ARC). Si tus datos calientes viven en vdevs antiguos, las lecturas seguirán golpeando esos vdevs.

4) ¿Puedo «arreglar el desequilibrio» sin migrar a un pool nuevo?

A veces parcialmente, reescribiendo datos (send/receive a un dataset temporal y de vuelta, o moviendo datasets). Pero no hay almuerzo gratis: para mover bloques debes reescribirlos, y eso consume I/O y tiempo.

5) ¿Es seguro mezclar mirrors y RAIDZ en un mismo pool?

Puede ser seguro en el sentido de que ZFS funcionará, pero rara vez es prudente para rendimiento predecible o características de fallo consistentes.
Si lo haces, hazlo deliberadamente y monitoriza comportamiento por-vdev. Si no, separa pools.

6) ¿Cuál es el movimiento más común que «accidentalmente lo rompe» al expandir ZFS?

Añadir un pequeño vdev mirror a un gran pool RAIDZ porque “solo necesitamos un poco más de espacio”. Terminas con un pool que asigna desproporcionadamente al mirror,
cambia patrones de desgaste y complica la planificación futura.

7) ¿Añadir más vdevs siempre aumenta el rendimiento?

Añadir más vdevs de nivel superior similares suele aumentar el throughput agregado y las IOPS. Añadir vdevs disimilares aumenta la imprevisibilidad.
Además, si tu carga está limitada por CPU, ARC, comportamiento sync o red, más vdevs no ayudarán.

8) ¿Cómo elegir entre mirrors y RAIDZ para crecer?

Los mirrors escalan mejor IOPS aleatorias de lectura/escritura y resilverean más rápido; RAIDZ es eficiente en capacidad pero más sensible a la ocupación y patrones de escritura.
Si ejecutas VMs o bases de datos con mucho I/O aleatorio, los mirrors suelen comportarse mejor operacionalmente.

9) ¿Qué frase debo recordar cuando alguien pide un «arreglo rápido de almacenamiento»?

Werner Vogels (idea parafraseada): “Todo falla, todo el tiempo”. Construye layouts y procedimientos suponiendo que el siguiente fallo ya está programado.

Conclusión: siguientes pasos que puedes hacer esta semana

Si recuerdas una cosa, que sea esta: zpool add no “extiende RAID”. Añade un vdev de nivel superior y no rebalancea datos antiguos.
Eso no es un bug. Es el modelo.

Pasos prácticos:

  1. Inventario tus pools: ejecuta zpool status y anota tipos de vdev, anchos y clases de dispositivo. Si es un lío, admítelo ahora.
  2. Mide carga por-vdev: captura zpool iostat -v 1 durante picos reales de carga. Encuentra el vdev caliente.
  3. Decide arquitectura: vdevs homogéneos en un pool, o múltiples pools por carga. No sigas improvisando.
  4. Planifica la salida: si ya añadiste un vdev inadecuado, programa una ruta de migración mientras el sistema aún está lo bastante sano para mover datos.

La fiabilidad del almacenamiento es, en gran parte, evitar la sofisticación innecesaria. ZFS te da herramientas poderosas. Úsalas como si fueras quien estará de guardia cuando fallen.

← Anterior
Tiempos de espera en contenedores Docker: ajusta los reintentos correctamente (no infinitos)
Siguiente →
Cortes por cable equivocado: Cómo un solo cable detiene un centro de datos

Deja un comentario