ZFS zfs send: La forma más rápida de mover terabytes (si lo haces bien)

¿Te fue útil?

Hay dos tipos de migraciones de datos: las que planificas y las que haces mientras todo el mundo te observa. ZFS zfs send/zfs receive es una de las pocas herramientas que puede manejar ambas: mover terabytes con integridad, reproducibilidad y un modelo claro de lo que cambió. Pero también es una de las formas más fáciles de producir una transferencia “exitosa” que es sutilmente errónea, dolorosamente lenta o imposible de reanudar.

Esta es una guía de campo desde el extremo operativo: cómo replicar rápido, cómo saber qué está pasando realmente, qué se rompe bajo presión y qué prácticas aburridas te evitan convertirte en una anécdota en el postmortem del próximo trimestre.

Por qué zfs send es diferente (y por qué eso importa)

La mayoría de las “herramientas de copia” mueven archivos. ZFS mueve la verdad: una vista consistente en un punto en el tiempo de un dataset representada por una snapshot. Un stream de zfs send no es “lo que había en el directorio mientras rsync corría.” Es “exactamente los bloques referenciados por la snapshot X,” opcionalmente más “exactamente los bloques cambiados de X a Y.” Esa diferencia es la razón por la que la replicación ZFS puede ser tanto más rápida como más correcta que la migración a nivel de archivos.

El intercambio es que debes respetar el modelo de ZFS. Si no lo haces, acabarás intentando replicar un objetivo en movimiento, replicando desde la base equivocada o replicando datos cifrados de una forma que parece correcta hasta el día de la restauración. En el mundo ZFS, una migración es una cadena de snapshots y supuestos. Tu trabajo es mantener la cadena intacta y los supuestos explícitos.

Un chiste para marcar el tono: la replicación ZFS es como mudarse enviando la cimentación: eficiente y consistente, pero probablemente deberías medir primero el camino de entrada.

Hechos interesantes y contexto histórico

  • ZFS fue diseñado para integridad de extremo a extremo: los checksums se validan al leer, lo que significa que replicación más scrub te da una historia de detección de corrupción que la mayoría de las herramientas de archivos no pueden igualar.
  • Las snapshots son intencionalmente baratas: las snapshots ZFS son referencias copy-on-write, no copias completas, lo que hace que la replicación incremental sea práctica a escala.
  • Los streams de send son deterministas: dado el mismo dataset/snapshot, el stream es lo bastante estable como para soportar replicación reanudable y receives fiables.
  • El envío incremental es una característica de primera clase: no es “diferir archivos,” es enviar solo los bloques cambiados entre snapshots.
  • Los datasets cifrados cambiaron las reglas de la replicación: los envíos “raw” permiten replicar datos cifrados sin exponer texto plano en la red o en el receptor.
  • El manejo de propiedades en el receptor es deliberado: ZFS te da controles para sobrescribir mountpoints, evitar montajes automáticos sorpresa y gestionar propiedades separadas de los datos.
  • Los tokens de reanudación fueron añadidos porque las redes mienten: los streams largos sobre enlaces inestables necesitaban un mecanismo oficial y seguro para reanudar en lugar de “empezar de nuevo y esperar.”
  • La replicación ZFS se usa como DR de bajo coste (y a menudo bueno): porque puede programarse, ser incremental y verificable, muchas organizaciones construyen DR con ella antes de comprar algo sofisticado.

Modelo mental: snapshots, streams y confianza

1) No estás replicando “un dataset”, estás replicando “un grafo de snapshots”

Los datasets ZFS tienen estado que cambia constantemente. Las snapshots congelan ese estado. Un stream de send referencia snapshots. Los envíos incrementales referencian pares de snapshots: una base y un objetivo.

La implicación práctica: si la snapshot base falta en el receptor, un receive incremental no puede aplicarse. Si alguien borra “snapshots viejas e inútiles” en el destino, acabas de romper la replicación de mañana. Esto no es un caso teórico; es un patrón de incidentes recurrente.

2) “Completo” vs “incremental” no es sobre tamaño, es sobre linaje

Un envío completo (zfs send pool/ds@snap) proporciona el estado completo del dataset en esa snapshot. Un envío incremental (-i o -I) depende de que el receptor ya tenga una snapshot relacionada.

  • -i envía cambios de exactamente una snapshot a otra (“de A a B”).
  • -I envía una cadena incremental incluyendo snapshots intermedias (“de A hasta B, incluyendo snapshots entre medias”).

3) Tu verdadero enemigo no es la velocidad—es la divergencia silenciosa

La velocidad es fácil de obsesionar porque es visible. La divergencia es lo que arruina tu fin de semana: un receive que “funcionó” pero se montó en la ruta equivocada; un dataset recibido con propiedades que cambiaron la semántica de la aplicación; un dataset cifrado recibido de forma que hace que las claves y clones sean problemáticos más tarde.

4) Los streams incluyen más que el contenido de los archivos

Los streams ZFS pueden incluir propiedades, snapshots, clones (con las banderas adecuadas) y —en modo raw— metadatos de cifrado. Eso es poder. También es una trampa si no decides intencionalmente qué preservar.

Tareas prácticas (comandos + interpretación)

Los comandos abajo asumen un host origen (src) y un host destino (dst) con pools ZFS ya creados. Reemplaza nombres de pool y dataset según corresponda. Las salidas son representativas; tu sistema variará.

Tarea 1: Confirmar la salud del pool antes de replicar

cr0x@src:~$ zpool status
  pool: tank
 state: ONLINE
status: Some supported features are not enabled on the pool.
action: Enable all features using 'zpool upgrade'. Once this is done,
        the pool may no longer be accessible by software that does not support the features.
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0

errors: No known data errors

Interpretación: Si ves DEGRADED, resilvering o errores de checksum, arregla eso primero. La replicación mueve la corrupción también; los checksums de ZFS la detectan, pero no reparan mágicamente el origen.

Tarea 2: Estimar tamaño del dataset y huella de snapshots

cr0x@src:~$ zfs list -o name,used,avail,refer,mountpoint -r tank/prod
NAME           USED  AVAIL  REFER  MOUNTPOINT
tank/prod      8.2T  12.1T   128K  /tank/prod
tank/prod/db   6.9T  12.1T   6.9T  /tank/prod/db
tank/prod/log  1.3T  12.1T   1.3T  /tank/prod/log

Interpretación: USED incluye snapshots y descendientes. REFER es los datos referenciados por el dataset mismo. Si USED es mucho mayor que REFER, tienes retención significativa de snapshots; la replicación incremental será tu amiga.

Tarea 3: Crear una snapshot de replicación con una convención de nombres

cr0x@src:~$ zfs snapshot -r tank/prod@replica-2025-12-25-0001

Interpretación: Usa un prefijo de snapshot predecible (replica-) para que puedas automatizar selección y retención sin tocar snapshots creadas por humanos.

Tarea 4: Estimación de tamaño en modo dry-run (para planificar)

cr0x@src:~$ zfs send -nP -R tank/prod@replica-2025-12-25-0001
size	9.11T

Interpretación: -nP estima el tamaño sin enviar. -R replica el dataset más descendientes y propiedades. La estimación te ayuda a decidir si necesitas incremental, compresión o una ventana de mantenimiento.

Tarea 5: Hacer la primera replicación completa (valores seguros por defecto)

cr0x@src:~$ zfs send -R tank/prod@replica-2025-12-25-0001 | ssh dst sudo zfs receive -u -o mountpoint=/tank/restore tank/recv

Interpretación: -u evita el montaje inmediato en el destino, evitando montajes sorpresa en sistemas ocupados. Sobrescribir mountpoint asegura que no te montes accidentalmente en rutas de producción. Recibir en tank/recv crea un namespace limpio; luego puedes promover o renombrar.

Tarea 6: Verificar que las snapshots recibidas existan y coincidan en nombres

cr0x@dst:~$ zfs list -t snapshot -r tank/recv | head
NAME                                              USED  AVAIL  REFER  MOUNTPOINT
tank/recv/prod@replica-2025-12-25-0001             0B      -   6.9T  -
tank/recv/prod/db@replica-2025-12-25-0001          0B      -   6.9T  -
tank/recv/prod/log@replica-2025-12-25-0001         0B      -   1.3T  -

Interpretación: La presencia de la snapshot es tu prueba de linaje. Si no ves la snapshot que esperas, no procedas con incrementales hasta entender por qué.

Tarea 7: Ejecutar un send incremental (paso único)

cr0x@src:~$ zfs snapshot -r tank/prod@replica-2025-12-25-0600
cr0x@src:~$ zfs send -R -i tank/prod@replica-2025-12-25-0001 tank/prod@replica-2025-12-25-0600 | ssh dst sudo zfs receive -u tank/recv

Interpretación: Esto envía solo los bloques cambiados entre esas dos snapshots, pero aún usa -R para cubrir descendientes. Fallará si el destino no tiene la snapshot base.

Tarea 8: Incremental con intermedios (-I) para ejecuciones perdidas

cr0x@src:~$ zfs send -R -I tank/prod@replica-2025-12-25-0001 tank/prod@replica-2025-12-25-1800 | ssh dst sudo zfs receive -u tank/recv

Interpretación: Si tu trabajo de replicación perdió varias snapshots, -I puede enviar una cadena incluyendo snapshots intermedias—frecuentemente haciendo la retención y rollback más fácil en el destino.

Tarea 9: Enviar con compresión en la red (pragmático)

cr0x@src:~$ zfs send -R tank/prod@replica-2025-12-25-0001 | gzip -1 | ssh dst "gunzip | sudo zfs receive -u tank/recv"

Interpretación: Esto es simple y a menudo efectivo en datasets con muchos logs o texto. En datos ya comprimidos (backups, medios), puede desperdiciar CPU con poco beneficio. Mide, no adivines.

Tarea 10: Replicar un dataset cifrado de forma segura (raw send)

cr0x@src:~$ zfs get -o name,property,value -s local encryptionroot,keystatus -r tank/secure
NAME         PROPERTY      VALUE
tank/secure  encryptionroot  tank/secure
tank/secure  keystatus       available

cr0x@src:~$ zfs snapshot -r tank/secure@replica-0001
cr0x@src:~$ zfs send -w -R tank/secure@replica-0001 | ssh dst sudo zfs receive -u tank/recv-secure

Interpretación: -w envía el stream cifrado raw. El destino recibe bloques cifrados; no expones texto plano durante la transferencia. Esto es a menudo lo que los equipos de seguridad realmente quieren, siempre que también tengas un plan de gestión de claves en el receptor.

Tarea 11: Manejar interrupciones con tokens de reanudación (no empieces de nuevo)

cr0x@dst:~$ zfs get -H -o value receive_resume_token tank/recv/prod
1-ED8f3a9c0c-...

cr0x@src:~$ zfs send -t 1-ED8f3a9c0c-... | ssh dst sudo zfs receive -u tank/recv

Interpretación: Si un receive fue interrumpido, ZFS puede almacenar un token de reanudación en el dataset destino. Enviar con -t continúa desde donde se quedó, asumiendo que el token es válido y el stream coincide. Esta característica es la diferencia entre “perdimos el enlace por 30 segundos” y “re-enviamos 40 TB”.

Tarea 12: Evitar caos de mountpoints accidental en el receive

cr0x@dst:~$ sudo zfs receive -u -o mountpoint=/mnt/quarantine -o canmount=off tank/recv < /tmp/stream.zfs

Interpretación: Si estás recibiendo desde un archivo o área de staging, forzar canmount=off y un mountpoint de cuarentena previene montajes inesperados, especialmente cuando -R trae múltiples descendientes con propiedades heredadas.

Tarea 13: Confirmar que el destino está leyendo datos sanos

cr0x@dst:~$ zpool scrub tank
cr0x@dst:~$ zpool status tank
  pool: tank
 state: ONLINE
status: Scrub in progress since Thu Dec 25 09:10:52 2025
        1.23T scanned at 6.10G/s, 180G issued at 900M/s, 9.11T total
        0B repaired, 1.98% done, 0:28:10 to go
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
errors: No known data errors

Interpretación: Un scrub después de un receive mayor no es paranoia; es validación. Si tu copia DR está corrupta, quieres saberlo ahora, no durante una caída.

Tarea 14: Medir rendimiento de send/receive sin adivinar

cr0x@src:~$ zfs send -nP tank/prod@replica-2025-12-25-0001
size	9.11T

cr0x@src:~$ time zfs send -R tank/prod@replica-2025-12-25-0001 | ssh dst sudo zfs receive -u tank/recv
real	154m12.331s
user	2m10.912s
sys	12m48.776s

Interpretación: La salida de time más el tamaño estimado te permiten calcular el rendimiento efectivo. No optimices a ciegas: si el tiempo de CPU es bajo pero el tiempo real es enorme, estás esperando en la red o en los discos.

Rendimiento: cómo hacerlo realmente rápido

“La forma más rápida de mover terabytes” es verdad, pero con condiciones. ZFS puede empujar datos a la velocidad de línea cuando las estrellas se alinean: lecturas secuenciales, sobrecarga CPU mínima, un pool receptor que puede absorber escrituras y un camino de red que no se derrite si estornudas. En la vida real, siempre hay uno de esos como cuello de botella.

Empieza por la restricción aburrida: la replicación es una canalización

Un send/receive es una canalización: lectura del pool fuente → creación del stream de send → transporte (a menudo SSH) → parsing del stream en el receptor → escritura en el pool destino → montaje y post-procesamiento opcional. La canalización corre a la velocidad de la etapa más lenta. Tu trabajo es identificar esa etapa rápido y eliminarla o aceptarla.

SSH: conveniente, seguro, y a veces tu cuello de botella

SSH es el transporte por defecto porque está en todas partes y es seguro por defecto. Pero el cifrado puede ser el techo en enlaces rápidos. Si mueves decenas de terabytes por 10/25/40/100GbE, prueba la capacidad CPU para el cifrado SSH. Si las CPUs están al 100%, tienes opciones:

  • Elegir cifrados que estén acelerados por hardware en tus CPUs (a menudo AES-GCM en x86 moderno).
  • Paralelizar dividiendo datasets (múltiples sends independientes) si tu almacenamiento puede manejarlo.
  • Usar sends raw para datasets cifrados (aún cifrados en el nivel ZFS), pero todavía tendrás cifrado SSH a menos que cambies el transporte.

Segundo chiste, y volvemos al trabajo: Lo único más rápido que un enlace 100Gb saturado es un ingeniero diciendo “probablemente es DNS” cuando claramente es CPU.

Compresión: una palanca, no una religión

La compresión en tránsito puede ser mágica en bases de datos con muchas escrituras repetitivas o ceros, y un desperdicio total en JPEGs y Parquet. Usa zfs send -nP para dimensionar, luego haz una muestra temporal (dataset pequeño o incremental reciente) con y sin compresión. Decide según el rendimiento medido y el consumo de CPU, no por intuiciones.

Recordsize, volblocksize y por qué las bases de datos se comportan diferente

El recordsize del dataset ZFS impacta cómo se disponen los datos y puede influir indirectamente en el comportamiento del send. Las bases de datos que usan muchas escrituras aleatorias pequeñas a menudo se benefician de bloques más pequeños (o datasets separados), pero la replicación sigue siendo por bloques. Para zvols (dispositivos de bloque), volblocksize es fijo al crearlo; si replicás zvols grandes con un volblocksize pequeño, te inscribes para más metadatos y potencialmente peor rendimiento.

Ingesta del pool destino: el asesino silencioso

Puedes tener un origen y una red rapidísimos, y aun así arrastrarte porque el pool destino no puede confirmar escrituras. Razones comunes:

  • SLOG pequeño o mal configurado (para escrituras sync en ciertos workloads; receive no es puramente sync pero puede verse afectado por el comportamiento del pool).
  • Discos giratorios con ancho de vdev insuficiente para ingest sostenida.
  • Mismatch de ashift, discos fallando o un pool ya ocupado con otras cargas.
  • Presión de RAM causando mal comportamiento del ARC y IO extra.

Usa receives de staging cuando necesites proteger producción

Un patrón común: recibir en pool/recv con canmount=off y un mountpoint seguro, luego hacer un cutover controlado: renombrar datasets, ajustar mountpoints, cargar claves, montar y arrancar servicios. Esto no hace la transferencia más rápida, pero la hace sobrevivible.

Tres mini-historias del mundo corporativo

Mini-historia #1: El incidente causado por un supuesto equivocado

El plan de migración parecía limpio: replicación completa el viernes por la noche, incrementales cada hora, cutover el domingo. El equipo asumió que “incremental” significaba “el destino solo necesita el nombre del dataset.” No lo necesitaba. Necesitaba la snapshot base exacta.

El sábado por la mañana, alguien ordenó el destino porque “tenía demasiadas snapshots.” Borraron una snapshot de hace una semana que no coincidía con el esquema de nombres replica-—porque no lo hacía. Era una snapshot automática de una ejecución anterior de prueba, y resultó ser la base incremental que el trabajo había estado usando desde el viernes.

A las 10:00, el incremental horario empezó a fallar con un mensaje que parecía un problema de receive transitorio. El runbook decía “reintentar,” así que reintentó. Entonces reintentó otra vez. Mientras tanto, nuevos escritos seguían ocurriendo en el origen, ampliando la brecha.

Cuando alguien notó que el patrón de error era consistente, el equipo se encontró con una elección desagradable: re-seed un envío completo de varios terabytes durante horas laborales o aceptar una ventana de outage más larga. Hicieron el re-seed durante la noche y cortaron tarde. Nadie fue despedido, pero la canalización de replicación ganó una nueva regla: la snapshot base es sagrada, y las políticas de retención deben ser automáticas y conscientes del prefijo.

Lección: La replicación incremental trata sobre linaje de snapshots, no sobre “el estado más reciente.” Si el destino pierde la base, no tienes replicación—tienes una bomba de tiempo.

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

Un equipo distinto quería velocidad. Tenían un enlace de 40Gb y una acumulación de datasets para replicar a un sitio DR. Alguien sugirió comprimir todo “porque siempre es más rápido.” Envuelvieron zfs send en compresión agresiva y declararon victoria tras una prueba en un dataset de texto.

Luego lo aplicaron a archivos multimedia e imágenes de VM. La CPU se fue al techo en ambos extremos, los hilos SSH compitieron por ciclos y el rendimiento cayó por debajo de lo que el stream sin comprimir podía hacer. Peor: el proceso de receive empezó a quedarse atrás lo suficiente para provocar ruido operacional—timeouts, alertas de monitorización y una cascada de mensajes “¿el sitio DR está caído?”.

La solución no fue heroica. Ejecutaron benchmarks cortos por clase de dataset y establecieron una política: comprimir en la red para logs y volcados de bases de datos; no comprimir para blobs ya comprimidos; preferir sends raw para datasets cifrados; y limitar la concurrencia según la capacidad de escritura del pool destino, no la velocidad de la red. La canalización se volvió estable y predeciblemente rápida—menos emocionante, más efectiva.

Lección: Optimizar una etapa de la canalización puede dejar sin recursos a otra. La compresión es un intercambio: red ahorrada, CPU gastada. Gasta CPU solo donde te compra tiempo real.

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

La mejor historia de replicación que puedo contar es la donde no pasó nada. Un equipo de almacenamiento tenía una rutina: antes de cualquier receive mayor, usaban -u para evitar montajes, forzaban un mountpoint de cuarentena y corrían un scrub dentro de las 24 horas. También etiquetaban snapshots de replicación con un prefijo estricto y se negaban a “limpieza manual de snapshots” en el destino.

Un trimestre, el sitio DR sufrió un evento de energía desordenado. La mayoría de los sistemas regresaron, pero una bahía de discos tuvo un problema de controlador que causó errores IO intermitentes. El pool quedó online, pero no estaba bien. El trabajo de replicación siguió corriendo y declaró éxito para algunos datasets—hasta que el scrub detectó errores de checksum en bloques recibidos recientemente.

Puesto que hacían scrubs rutinarios, detectaron el problema antes de una verdadera catástrofe. Pausaron la replicación, arreglaron el hardware, re-enviaron los incrementales afectados usando la línea de snapshots preservada y validaron de nuevo. Cuando un incidente de producción no relacionado ocurrió semanas después, la copia DR fue realmente utilizable—porque el equipo trató la “validación” como parte de la replicación, no como un lujo.

Lección: Las prácticas aburridas—no permitir montajes automáticos sorpresa, nombres consistentes de snapshots, scrubs periódicos—no son ceremonia. Son la diferencia entre DR como diapositiva y DR como sistema funcional.

Guion de diagnóstico rápido

Cuando una replicación es lenta o falla, no tienes tiempo para danza interpretativa. Aquí está la ruta más rápida al cuello de botella, en el orden que usualmente da resultado.

Primero: confirma que realmente progresa (y no está reintentando)

cr0x@dst:~$ ps aux | egrep 'zfs receive|ssh' | grep -v egrep
root     21844  2.1  0.1  24548  9120 ?        Ss   09:20   0:01 zfs receive -u tank/recv
root     21843  0.9  0.0  16720  6340 ?        Ss   09:20   0:00 ssh -oBatchMode=yes src zfs send -R tank/prod@replica-2025-12-25-0600

Interpretación: Si ves procesos de corta vida repetidos, estás flapping debido a un error. Si los procesos son de larga duración, probablemente estás lento, no fallando.

Segundo: verifica si el destino es el que limita (pool ocupado o no sano)

cr0x@dst:~$ zpool iostat -v tank 2 3
                                              capacity     operations     bandwidth
pool                                        alloc   free   read  write   read  write
------------------------------------------  -----  -----  -----  -----  -----  -----
tank                                        9.40T  8.90T     10    980   12M   820M
  mirror-0                                  9.40T  8.90T     10    980   12M   820M
    sda                                         -      -      5    490    6M   410M
    sdb                                         -      -      5    490    6M   410M
------------------------------------------  -----  -----  -----  -----  -----  -----

Interpretación: Si las escrituras están cerca del techo físico del pool, ese es tu cuello de botella. Si las escrituras son bajas pero el send es lento, busca en otro lado (CPU SSH, lecturas en origen, red).

Tercero: comprobar saturación CPU por SSH

cr0x@src:~$ top -b -n 1 | head -n 15
top - 09:24:11 up 31 days,  6:02,  2 users,  load average: 18.22, 16.91, 14.77
Tasks: 312 total,   2 running, 310 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.2 us,  1.1 sy,  0.0 ni,  7.4 id, 88.0 wa,  0.0 hi,  0.3 si,  0.0 st
MiB Mem : 257982.3 total,  11230.5 free,  78112.9 used, 170638.9 buff/cache

Interpretación: Alto wa significa espera IO (lecturas origen probablemente cuello de botella). Alto user/system con procesos ssh en top sugiere sobrecarga por criptografía.

Cuarto: comprobar errores de linaje de snapshots (clase “falló instantáneamente”)

cr0x@dst:~$ sudo zfs receive -u tank/recv
cannot receive incremental stream: destination tank/recv/prod has been modified

Interpretación: Esto usualmente significa que el dataset destino divergio (hubo escrituras, snapshots borradas o recibiste en el lugar equivocado). Arregla el linaje, no reintentos a fuerza bruta.

Quinto: comprobar si mismatch de modo de cifrado te bloquea

cr0x@dst:~$ zfs get -o name,property,value -r tank/recv-secure | egrep 'encryptionroot|keystatus'
tank/recv-secure/secure  encryptionroot  tank/recv-secure/secure
tank/recv-secure/secure  keystatus       unavailable

Interpretación: keystatus unavailable es normal si las claves no están cargadas. Se vuelve un problema si tu flujo espera montar inmediatamente. Decide si quieres replicación raw (claves gestionadas por separado) o replicación descifrada (claves cargadas en el origen y datos enviados en texto dentro del stream ZFS).

Errores comunes (síntomas + fixes)

Error 1: Recibir en la ruta de dataset equivocada

Síntoma: El receive completa, pero los datasets aparecen bajo una jerarquía inesperada (o sobrescriben un dataset de prueba).

Solución: Siempre haz staging de receives en un namespace claramente nombrado (p. ej., tank/recv) y usa -u. Verifica con zfs list -r antes de montar o renombrar.

Error 2: Borrar snapshots en el destino “para ahorrar espacio”

Síntoma: Los receives incrementales fallan con “snapshot faltante” o “destination has been modified.”

Solución: La retención debe ser dirigida por políticas y consciente de la replicación. Solo borra snapshots de replicación según un calendario que preserve la base requerida por el siguiente incremental.

Error 3: No usar -u y sufrir montajes sorpresa

Síntoma: Tras el receive, nuevos filesystems se montan automáticamente, a veces en rutas usadas por servicios de producción.

Solución: Usa zfs receive -u y sobrescribe mountpoint y/o establece canmount=off durante el receive. Monta de forma intencional más tarde.

Error 4: Asumir que la compresión siempre ayuda

Síntoma: CPU al máximo, rendimiento peor de lo esperado, receive retrasado.

Solución: Ejecuta benchmarks. Usa compresión solo donde los datos sean compresibles. Considera niveles de compresión más ligeros si la CPU es el limitador.

Error 5: Ignorar flags de características del pool y compatibilidad de versiones

Síntoma: Receive falla en sistemas más antiguos, o el pool replicado no se puede importar donde lo necesitas.

Solución: Alinea versiones/características de ZFS entre sitios antes de construir supuestos de DR. Si debes interoperar con ZFS antiguo, evita habilitar características que rompan compatibilidad.

Error 6: Olvidar que -R trae propiedades y descendientes

Síntoma: Mountpoints, cuotas, reservas u otras propiedades aparecen inesperadamente en el destino.

Solución: Usa overrides con -o en el receive donde corresponda y audita propiedades críticas después del receive con zfs get. Mantén un “contrato de propiedades” para datasets replicados.

Error 7: No planificar la reanudación

Síntoma: Una caída de red transitoria fuerza un reenvío completo y rompe la ventana.

Solución: Usa tokens de reanudación de ZFS donde estén soportados. Diseña trabajos para detectar tokens y reanudar automáticamente en lugar de reiniciar desde cero.

Listas de verificación / plan paso a paso

Checklist A: Seed único (replicación completa) que no te hará daño después

  1. Verifica la salud del pool en ambos extremos (zpool status), arregla errores primero.
  2. Elige una convención de nombres para snapshots de replicación (replica-YYYY-MM-DD-HHMM).
  3. Crea una snapshot recursiva en el origen para el seed inicial.
  4. Estima tamaño con zfs send -nP -R para entender tiempo y ancho de banda necesarios.
  5. Recibe en un namespace de staging con -u y propiedades seguras (mountpoint/canmount).
  6. Verifica que las snapshots existan en el destino y que el árbol de datasets coincida con expectativas.
  7. Scrub del pool destino dentro de las 24 horas del seed para confianza.

Checklist B: Replicación incremental continua (la rutina diaria)

  1. Crea nuevas snapshots en un calendario (hora/día), siempre con el prefijo de replicación.
  2. Envía incrementales usando una snapshot base conocida; prefiere -I si quieres incluir snapshots intermedias.
  3. Usa -u para mantener receives no disruptivos; monta solo durante cutovers controlados o pruebas.
  4. Monitorea tokens de reanudación y reanuda automáticamente si un trabajo se interrumpe.
  5. Aplica políticas de retención a snapshots de replicación en ambos extremos, preservando linaje para el siguiente incremental.

Checklist C: Plan de cutover (porque “simplemente lo cambiamos” no es un plan)

  1. Poner en pausa las escrituras de la aplicación (modo mantenimiento, flush/checkpoint de BD o detener servicios—lo que sea correcto para tu carga).
  2. Toma snapshots finales y ejecuta un último envío incremental.
  3. En el destino: ajusta mountpoints finales, carga claves de cifrado si es necesario y valida que los datasets existan.
  4. Monta datasets y arranca servicios; valida con comprobaciones a nivel de aplicación, no solo montajes de filesystem.
  5. Mantén el origen en solo-lectura y conserva snapshots hasta que estés seguro del nuevo estado.

Preguntas frecuentes

1) ¿Es zfs send | zfs receive siempre más rápido que rsync?

No, pero a escala suele ser más rápido y normalmente más consistente. Brilla cuando puedes usar incrementales y cuando las semánticas del sistema de archivos (permisos, hardlinks, xattrs) importan. Si solo necesitas un subconjunto pequeño de archivos o no puedes usar snapshots, rsync puede ser más simple.

2) ¿Debo usar siempre -R?

No siempre. -R es estupendo para replicar un árbol de datasets con propiedades y snapshots. Si solo necesitas un dataset sin descendientes o quieres un control más estricto sobre qué propiedades viajan, un send no recursivo puede ser más seguro.

3) ¿Cuál es la diferencia entre -i y -I otra vez?

-i envía de una snapshot a otra (un único delta). -I envía una cadena incremental e incluye snapshots intermedias entre base y objetivo. Usa -I cuando quieras que el destino retenga la misma historia de snapshots.

4) ¿Cómo sé qué snapshot usar como base incremental?

Usa la snapshot de replicación más reciente que exista en ambos, origen y destino. Operativamente eso significa: la última snapshot recibida con éxito con tu prefijo de replicación. No adivines—lista snapshots en ambos lados y empareja nombres.

5) ¿Puedo reanudar una transferencia interrumpida de forma segura?

Sí, si tu ZFS soporta tokens de reanudación y no has destruido el estado parcial del receive. Revisa receive_resume_token en el dataset destino; si está presente, envía con zfs send -t <token> y recibe de nuevo.

6) ¿Un send raw (-w) significa que los datos viajan cifrados?

Send raw significa que ZFS envía bloques cifrados y metadatos de cifrado, por lo que el stream ZFS no contiene texto plano. Mucha gente todavía usa SSH, que cifra el transporte también—bien, pero no es necesario para confidencialidad cuando el stream es raw-encrypted.

7) ¿Por qué mi receive incremental falló con “destination has been modified”?

Porque lo está. Eso puede significar escrituras reales al dataset destino, un rollback, un cambio de propiedad que implique divergencia en ciertos contextos, o snapshots faltantes en el linaje esperado. La solución es reestablecer una base común de snapshot—a veces haciendo rollback o con un nuevo envío completo.

8) ¿Cómo evito que datasets replicados se monten automáticamente?

Usa zfs receive -u. Para mayor seguridad, establece canmount=off y un mountpoint de cuarentena durante el receive, luego cambia propiedades y monta intencionalmente después.

9) ¿Es seguro replicar bases de datos de producción con snapshots ZFS?

Es seguro para consistencia del sistema de archivos, no automáticamente para consistencia a nivel de aplicación. Para bases de datos, coordina snapshots con el mecanismo de consistencia de la base de datos (flush/checkpoint/modo de backup) si necesitas garantías crash-consistent vs application-consistent.

10) ¿Cómo valido que la copia DR es utilizable?

Al mínimo: asegúrate de que las snapshots estén presentes, corre scrubs periódicos y realiza restores o montajes de prueba en un entorno aislado. Un plan DR sin ejercicios de restauración es solo una bonita historia.

Conclusión

zfs send/zfs receive es una de las pocas herramientas que puede mover terabytes rápidamente sin convertir la corrección en un ejercicio de esperanza. Pero ZFS es estricto en la forma en que los buenos sistemas son estrictos: el linaje de snapshots importa, las propiedades importan y el receptor no es un cubo donde verter streams.

Si lo quieres rápido, trata la replicación como una canalización y mide dónde estás limitado. Si lo quieres fiable, considera el nombrado de snapshots, la retención, el comportamiento de reanudación y la validación post-receive como parte del sistema—no como extras opcionales. Hazlo bien y obtendrás algo raro: velocidad en la que puedes confiar.

← Anterior
OpenVPN en Windows: problemas con el controlador TAP y cómo repararlos
Siguiente →
Tailscale: VPN sin dolor y los errores de acceso que aún duelen

Deja un comentario