Simulacro DR con ZFS send/receive: practicar la restauración, no solo la copia de seguridad

¿Te fue útil?

Su panel de copias de seguridad está en verde. Sus snapshots están “corriendo”. Sus auditores están tranquilos.
Entonces ocurre un incidente real—ransomware, un rm -rf accidental, un controlador falla, o alguien “amablemente” destruye un dataset—y de repente aprende en público qué significa realmente “restaurar”.

Un simulacro DR con ZFS send/receive es donde cambia la teoría reconfortante por la realidad medida: cuánto tarda, qué se rompe, qué falta y qué desearía haber probado el trimestre pasado.
Las copias de seguridad son un proceso. La restauración es una actuación.

Por qué importan los simulacros DR con ZFS (y qué prueban realmente)

La replicación ZFS send/receive resulta seductora porque parece determinista: entra el snapshot A, sale el snapshot A, y hasta puede medir los bytes en tránsito.
Eso fomenta un hábito peligroso: tratar la replicación como sinónimo de recuperación.

Un simulacro DR no es “¿podemos copiar datos?”. Es “¿podemos restaurar un servicio?”, con todos los detalles molestos adjuntos:

  • Identidad: ¿están correctos los nombres de datasets, los puntos de montaje y los permisos en el destino?
  • Dependencias: ¿también necesita WAL de PostgreSQL, configuración de la aplicación, secretos, claves TLS o metadatos de VM?
  • Tiempo: ¿realmente cumple el RPO y RTO cuando la red está ocupada y el responsable está cansado?
  • Seguridad: ¿puede hacerlo sin destruir la última copia buena?
  • Personas: ¿alguien además de “la persona ZFS” sabe qué escribir?

No ejecuta simulacros DR para demostrar que ZFS funciona. ZFS funciona. Ejecuta simulacros DR para detectar las partes que no funcionan: sus suposiciones, su automatización, la gestión de claves,
sus convenciones de nombres y su costumbre de no escribir la cosa extraña que hizo hace tres años a las 2 a.m.

Una cita operativa que envejeció bien: La esperanza no es una estrategia. — James Cameron.
Se aplica a las restauraciones de copias de seguridad con precisión embarazosa.

Broma #1: Las copias de seguridad son como los paracaídas—si no ha probado uno, está a punto de aprender mucho muy rápido.

Datos interesantes y breve historia

Un poco de contexto ayuda porque ZFS send/receive no es “una copia de archivos”. Es un flujo del estado del sistema de archivos, y las decisiones de diseño detrás explican
muchos de los bordes afilados que encontrará en los simulacros.

  1. La replicación por snapshots precede la moda cloud: send/receive existe desde los primeros días de ZFS, diseñado para flujos de trabajo de replicación empresariales reales.
  2. Los streams ZFS son transaccionales: está enviando bloques y metadatos del dataset tal como estaban en un snapshot, no re-reproduciendo “cambios de archivo” como rsync.
  3. Los incrementales dependen de la línea de descendencia: un stream incremental requiere que el receptor tenga el snapshot base exacto (misma GUID lineage), no solo un snapshot con el mismo nombre.
  4. Las propiedades viajan con el stream (a veces): con -p y flags relacionados, las propiedades del dataset pueden replicarse, lo cual es salvación o caos según sus convenciones destino.
  5. El cifrado cambió las reglas: el cifrado nativo de ZFS introdujo realidades de gestión de claves en la replicación—especialmente los envíos “raw” y la carga de claves en el receptor.
  6. La compresión no es “ancho de banda gratis”: el tamaño del stream ZFS varía mucho según recordsize, compressratio y si envía datos comprimidos o raw.
  7. Receive no siempre es idempotente: recibir repetidamente puede fallar o divergir si se podaron snapshots, se hicieron rollbacks o un receive previo dejó estado parcial.
  8. La replicación no es retención de backups: send/receive refleja el historial de snapshots que elija; no resuelve automáticamente retención, legal hold o copias offline.
  9. ZFS siempre fue sobre integridad: las checksums son end-to-end, pero los simulacros aún atrapan partes del mundo real: cables malos, RAM inestable, arc mal dimensionado o un pool receptor abrumado.

Definir el simulacro: alcance, RPO/RTO y qué significa “hecho”

No empiece con comandos. Empiece con definiciones, porque los simulacros DR fallan con más frecuencia por fallos de gestión de proyecto con apariencia técnica.
Defina tres cosas antes de tocar una shell:

1) ¿Qué está restaurando?

Elija un servicio con suficiente complejidad para ser honesto: una app web con base de datos, un recurso compartido de archivos con ACLs, una carga de trabajo VM o una canalización analítica.
Incluya “todo lo necesario para operar” ese servicio, no solo su dataset principal.

2) ¿Cuál es el RPO y RTO que medirá?

  • RPO: el snapshot más reciente aceptable que puede restaurar (p. ej., “no más de 15 minutos de pérdida de datos”).
  • RTO: el tiempo desde declarar el desastre hasta que los usuarios vuelven a tener servicio (p. ej., “menos de 2 horas”).

Para la replicación ZFS, a menudo puede controlar el RPO con la cadencia de snapshots y la frecuencia de replicación. El RTO es donde la realidad muerde:
ancho de banda de red, rendimiento del pool receptor, carga de claves, orden de montaje, comprobaciones de servicio, DNS y recuperación a nivel de aplicación.

3) ¿Cómo se ve “hecho”?

“Dataset recibido con éxito” no es “hecho”. “La aplicación pasa pruebas básicas” está más cerca.
Su definición de hecho debería incluir:

  • Datasets montados como se espera (o intencionalmente no montados hasta el corte).
  • Servicios iniciados y saludables.
  • Permisos y propiedad correctos.
  • Al menos una ruta real de lectura/escritura verificada (login funciona, consulta funciona, creación de archivo funciona).
  • Tiempos medidos para cada fase (duración del receive, duración del montaje, duración de recuperación de la aplicación).

Preparación: qué construir antes de practicar

Un simulacro DR debería sentirse ensayado. No porque sea teatro, sino porque cuando es real no tendrá tiempo para inventar la coreografía.
Construya esto con antelación.

Nombres que no le harán daño después

Decida cómo el lado DR nombra pools y datasets. Si replica tank/prod/app a backup/prod/app, está tomando una decisión sobre
puntos de montaje, expectativas de fstab y herramientas.

Recomiendo recibir en un pool y un espacio de nombres dedicados en el host DR, como drpool/recv/prod/app, y luego usar promoción/renombrado controlado en el corte.
Evita montajes accidentales sobre rutas de producción durante un simulacro.

Gestión de claves (si usa cifrado nativo)

Si sus datasets están cifrados, la replicación no es solo “datos”. Son claves, ubicaciones de claves y quién puede cargarlas bajo presión.
Decida si el host DR almacena claves (más arriesgado pero más rápido) o requiere carga manual de claves (más seguro pero más lento).
En cualquier caso, pruébelo.

Automatización aburrida a propósito

La automatización de DR debe ser predeciblemente aburrida: nombres de snapshot explícitos, objetivos de receive explícitos, registro en archivo y fallo inmediato ante desajustes.
No se ponga creativo con lógica implícita de “último snapshot” a menos que también construya salvaguardas.

Realidad de capacidad y fragmentación

La replicación no le salvará de un almacenamiento DR infra-dimensionado. A ZFS no le impresiona el optimismo.
Dimensione el pool DR para contener: datasets replicados, retención de snapshots y espacio para que el receive respire (necesita holgura).
Si está cerca del lleno, su simulacro debería incluir el momento en que el receive se detiene a mitad de stream.

Tareas prácticas (comandos, salidas, decisiones)

Estas tareas están pensadas para ejecutarse durante un simulacro DR (o mientras se prepara uno). Cada una incluye:
el comando, salida realista, qué significa y la decisión que toma a partir de ello.
Los nombres de host y pools son ejemplos: prod1 envía desde tank, dr1 recibe en drpool.

Tarea 1: Confirmar salud del pool antes de replicar

cr0x@prod1:~$ zpool status -x
all pools are healthy

Qué significa: No hay errores conocidos en los vdev. Esta es su línea base.

Decisión: Si esto no está sano, deténgase. No replique corrupción y llámelo DR.

Tarea 2: Comprobar espacio libre y fragmentación (emisor y receptor)

cr0x@prod1:~$ zpool list -o name,size,alloc,free,frag,cap,health
NAME   SIZE  ALLOC   FREE  FRAG  CAP  HEALTH
tank  7.25T  5.10T  2.15T   28%  70%  ONLINE
cr0x@dr1:~$ zpool list -o name,size,alloc,free,frag,cap,health
NAME    SIZE  ALLOC   FREE  FRAG  CAP  HEALTH
drpool  9.06T  3.40T  5.66T   12%  37%  ONLINE

Qué significa: El receptor tiene mucho espacio libre; la fragmentación es moderada.

Decisión: Si CAP es > 80% o la frag es alta, espere receives más lentos y posible ENOSPC durante retención de snapshots. Arregle capacidad antes del simulacro.

Tarea 3: Verificar la lista de datasets y propiedades críticas

cr0x@prod1:~$ zfs list -r -o name,used,avail,recordsize,compression,encryption,mountpoint tank/prod/app
NAME                USED  AVAIL  RECORDSIZE  COMPRESS  ENCRYPTION  MOUNTPOINT
tank/prod/app       820G  1.20T  128K        zstd     aes-256-gcm /srv/app
tank/prod/app/db    540G  1.20T  16K         zstd     aes-256-gcm /var/lib/postgresql
tank/prod/app/logs  120G  1.20T  128K        zstd     aes-256-gcm /srv/app/logs

Qué significa: Recordsize difiere entre app y DB, compresión es zstd, cifrado habilitado.

Decisión: Asegúrese de que el destino DR sea compatible con estas propiedades. Para simulacros DR, evite heredar puntos de montaje que podrían colisionar con rutas locales.

Tarea 4: Confirmar la cadencia de snapshots y el “punto seguro más reciente” (RPO)

cr0x@prod1:~$ zfs list -t snapshot -o name,creation -s creation -r tank/prod/app | tail -n 5
tank/prod/app@dr-2025-12-26_1000  Fri Dec 26 10:00 2025
tank/prod/app@dr-2025-12-26_1015  Fri Dec 26 10:15 2025
tank/prod/app@dr-2025-12-26_1030  Fri Dec 26 10:30 2025
tank/prod/app@dr-2025-12-26_1045  Fri Dec 26 10:45 2025
tank/prod/app@dr-2025-12-26_1100  Fri Dec 26 11:00 2025

Qué significa: Snapshots cada 15 minutos. Su RPO está limitado por este horario más la latencia de replicación.

Decisión: Si los snapshots no son regulares, la replicación no podrá cumplir un RPO que no haya diseñado.

Tarea 5: Medir la latencia de replicación comparando los snapshots más recientes en emisor y receptor

cr0x@dr1:~$ zfs list -t snapshot -o name,creation -s creation -r drpool/recv/prod/app | tail -n 3
drpool/recv/prod/app@dr-2025-12-26_1030  Fri Dec 26 10:30 2025
drpool/recv/prod/app@dr-2025-12-26_1045  Fri Dec 26 10:45 2025
drpool/recv/prod/app@dr-2025-12-26_1100  Fri Dec 26 11:00 2025

Qué significa: El receptor tiene el snapshot más reciente. La latencia es efectivamente cero ahora mismo.

Decisión: Si el receptor está retrasado, decida si su simulacro usa el snapshot recibido más reciente (realista) o pausa la producción para forzar alineación (menos realista).

Tarea 6: Estimación en seco del tamaño de un send incremental

cr0x@prod1:~$ zfs send -nPv -I tank/prod/app@dr-2025-12-26_1045 tank/prod/app@dr-2025-12-26_1100
send from @dr-2025-12-26_1045 to tank/prod/app@dr-2025-12-26_1100 estimated size is 14.2G
total estimated size is 14.2G

Qué significa: Aproximadamente 14.2G cambiaron entre snapshots. Ese es su trabajo de replicación para ese intervalo.

Decisión: Compárelo con el ancho de banda disponible y su ventana de replicación. Si no puede enviar el delta a tiempo, no cumplirá el RPO bajo carga.

Tarea 7: Realizar una replicación inicial completa en un espacio de nombres seguro

cr0x@prod1:~$ zfs send -w tank/prod/app@dr-2025-12-26_1100 | ssh dr1 "zfs receive -u -o mountpoint=none -d drpool/recv"
receiving full stream of tank/prod/app@dr-2025-12-26_1100 into drpool/recv/tank/prod/app@dr-2025-12-26_1100

Qué significa: -w envía un stream cifrado raw (las claves permanecen en el emisor; se preservan las propiedades de cifrado). -u evita el montaje automático al recibir. -o mountpoint=none evita colisiones accidentales de montaje.

Decisión: Si necesita que el host DR monte y sirva datos, debe planear la carga de claves y ajustes de mountpoint. Si esto es solo una réplica, manténgala desmontada por defecto.

Tarea 8: Replicación incremental (el trabajo diario real)

cr0x@prod1:~$ zfs send -w -I tank/prod/app@dr-2025-12-26_1100 tank/prod/app@dr-2025-12-26_1115 | ssh dr1 "zfs receive -u -dF drpool/recv"
receiving incremental stream of tank/prod/app@dr-2025-12-26_1115 into drpool/recv/tank/prod/app@dr-2025-12-26_1115

Qué significa: -I envía todos los snapshots intermedios. -F fuerza rollback del dataset objetivo al snapshot más reciente para aceptar el stream.

Decisión: Use -F solo cuando esté seguro de que el dataset receptor es una réplica y acepta perder cambios locales. En simulacros, documente cuándo lo usó.

Tarea 9: Confirmar estado en el lado receptor y que los datasets no estén montados

cr0x@dr1:~$ zfs get -r -o name,property,value mounted,mountpoint drpool/recv/tank/prod/app | head
NAME                          PROPERTY   VALUE     SOURCE
drpool/recv/tank/prod/app      mounted    no        -
drpool/recv/tank/prod/app      mountpoint none      local
drpool/recv/tank/prod/app/db   mounted    no        -
drpool/recv/tank/prod/app/db   mountpoint /var/lib/postgresql  received

Qué significa: El dataset superior está desmontado con mountpoint none, pero un hijo tiene un mountpoint recibido. Esa es una clásica situación de “montaje sorpresa después”.

Decisión: Normalice mountpoints en el lado receptor. Para un espacio de réplica, prefiero forzar mountpoint none en todo el árbol y luego establecer mountpoints explícitos solo en la instancia restaurada.

Tarea 10: Sobrescribir mountpoints heredados/recibidos de forma segura

cr0x@dr1:~$ zfs set -r mountpoint=none drpool/recv/tank/prod/app
cr0x@dr1:~$ zfs get -r -o name,property,value mountpoint drpool/recv/tank/prod/app | head -n 6
NAME                          PROPERTY   VALUE  SOURCE
drpool/recv/tank/prod/app      mountpoint none   local
drpool/recv/tank/prod/app/db   mountpoint none   local
drpool/recv/tank/prod/app/logs mountpoint none   local

Qué significa: Ha neutralizado mountpoints en todo el árbol de réplica.

Decisión: Mantenga las réplicas inactivas. Cree una instancia “restaurada” separada (clone o promote) con mountpoints reales para iniciar servicios.

Tarea 11: Crear una copia restaurada escribible sin alterar la réplica

cr0x@dr1:~$ zfs clone drpool/recv/tank/prod/app@dr-2025-12-26_1115 drpool/restore/prod/app
cr0x@dr1:~$ zfs set mountpoint=/srv/app drpool/restore/prod/app
cr0x@dr1:~$ zfs mount drpool/restore/prod/app
cr0x@dr1:~$ zfs list -o name,mountpoint,origin drpool/restore/prod/app
NAME                    MOUNTPOINT  ORIGIN
drpool/restore/prod/app /srv/app    drpool/recv/tank/prod/app@dr-2025-12-26_1115

Qué significa: Ahora tiene un clone escribible para el simulacro mientras mantiene la réplica intacta.

Decisión: Siempre restaure en un dataset nuevo cuando sea posible. Es una válvula de seguridad para errores humanos y para simulacros repetidos.

Tarea 12: Cargar claves de cifrado en el lado DR (si es necesario)

cr0x@dr1:~$ zfs get -o name,property,value keystatus,encryptionroot drpool/restore/prod/app
NAME                    PROPERTY        VALUE
drpool/restore/prod/app  keystatus       unavailable
drpool/restore/prod/app  encryptionroot  drpool/recv/tank/prod/app
cr0x@dr1:~$ zfs load-key -L file:///etc/zfs/keys/prod_app.key drpool/restore/prod/app
cr0x@dr1:~$ zfs get -o name,property,value keystatus drpool/restore/prod/app
NAME                    PROPERTY   VALUE
drpool/restore/prod/app  keystatus  available

Qué significa: Las claves estaban indisponibles hasta que las cargó. Sin esto, los montajes y el inicio de servicios fallarán.

Decisión: En su runbook, haga la carga de claves explícita, con quién tiene acceso y dónde se almacenan las claves. Si la respuesta es “en el home de alguien”, arréglelo antes del próximo simulacro.

Tarea 13: Medir el rendimiento real de un pipeline send/receive

cr0x@prod1:~$ zfs send -w tank/prod/app@dr-2025-12-26_1115 | pv -brt | ssh dr1 "zfs receive -u -d drpool/recv"
2.10GiB 0:00:18 [ 119MiB/s] [   <=>  ]

Qué significa: El throughput de extremo a extremo es ~119MiB/s. Eso incluye CPU, red y disco en ambos extremos.

Decisión: Compárelo con sus tamaños delta y la ventana RPO. Si necesita 14G cada 15 minutos, 119MiB/s está bien; si necesita 200G, está soñando.

Tarea 14: Identificar si el cuello de botella es I/O de disco, CPU o red

cr0x@dr1:~$ iostat -xz 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.42    0.00    6.11   21.37    0.00   54.10

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1         12.0   10240.0     0.0   0.00    1.20   853.3    410.0   92800.0     2.0   0.49    9.80   226.3    4.10  92.0

Qué significa: Alto %util y carga de escritura sugieren que el disco del receptor está cerca de saturación. iowait es significativo.

Decisión: Si el disco está saturado, ajustar la red no ayudará. Considere vdevs más rápidos, alinear recordsize o usar un pool de staging más rápido.

Tarea 15: Confirmar la línea de snapshots (por qué fallan los incrementales)

cr0x@prod1:~$ zfs get -o name,property,value guid tank/prod/app@dr-2025-12-26_1100
NAME                           PROPERTY  VALUE
tank/prod/app@dr-2025-12-26_1100  guid      1638672096235874021
cr0x@dr1:~$ zfs get -o name,property,value guid drpool/recv/tank/prod/app@dr-2025-12-26_1100
NAME                                      PROPERTY  VALUE
drpool/recv/tank/prod/app@dr-2025-12-26_1100  guid      1638672096235874021

Qué significa: La GUID coincide; el receptor tiene la instancia de snapshot exacta necesaria para incrementales.

Decisión: Si las GUIDs difieren, su incremental no se aplicará. Necesitará un nuevo send completo o corregir la cadena de replicación.

Tarea 16: Validar propiedades del dataset que pueden romper restauraciones (ACLs, xattrs, sensibilidad de mayúsculas)

cr0x@prod1:~$ zfs get -o name,property,value acltype,xattr,casesensitivity -r tank/prod/app
NAME                PROPERTY         VALUE
tank/prod/app        acltype          posixacl
tank/prod/app        xattr            sa
tank/prod/app        casesensitivity  sensitive
tank/prod/app/db     acltype          posixacl
tank/prod/app/db     xattr            sa
tank/prod/app/db     casesensitivity  sensitive

Qué significa: Propiedades que afectan la semántica de la aplicación están establecidas.

Decisión: Asegúrese de que el receive las preserve (replicación de propiedades) o que las establezca explícitamente en el dataset restaurado. El almacenamiento de xattr mismatched puede convertirse en una sorpresa de rendimiento.

Manual de diagnóstico rápido: qué comprobar primero/segundo/tercero

Durante un simulacro (o un corte real), el pipeline de replicación se vuelve lento y la gente empieza inmediatamente a cambiar cosas.
No lo haga. Diagnostique en un orden estricto para no “arreglar” la capa equivocada y añadir variables.

Primero: ¿está bloqueado en el pool receptor?

  • Comprobar: zpool status por resilver/scrub en progreso; iostat -xz por %util cerca de 100% y await alto.
  • Interpretación: Si el receptor está ocupado (resilver, scrub, dolor SMR, vdevs saturados), el throughput del receive se colapsa independientemente de la velocidad del emisor.
  • Acción: Pause el simulacro, reprograme alrededor de scrub/resilver o reciba temporalmente en un pool de staging más rápido.

Segundo: ¿es la red o la sobrecarga de SSH?

  • Comprobar: throughput observado con pv; CPU en emisor/receptor; errores y drops de la NIC con herramientas del SO.
  • Interpretación: Si la CPU está al máximo en un solo núcleo durante el cifrado SSH, ha encontrado su techo.
  • Acción: Use cifrados más rápidos, habilite offload hardware donde esté disponible o replique sobre una red confiable con transporte alternativo (según política). No “optimice” la seguridad en un pánico.

Tercero: ¿es comportamiento a nivel ZFS (recordsize, compresión, sync, vdevs especiales)?

  • Comprobar: propiedades del dataset, zvol vs filesystem y si la carga es escrituras aleatorias pequeñas o secuenciales grandes.
  • Interpretación: Un dataset DB con recordsize=16K se comporta diferente que un dataset de medios con recordsize=1M.
  • Acción: Alinee recordsize y considere separar datasets por carga de trabajo. En DR, la ruta de restauración a menudo revela desajustes que ignoró en producción.

Cuarto: ¿es un problema de cadena de replicación?

  • Comprobar: snapshots base faltantes, GUID mismatch o rollbacks forzados que podaron historial necesario.
  • Interpretación: “El send incremental falla” suele ser “el historial de snapshots no es lo que usted cree”.
  • Acción: Deje de probar flags aleatorios. Confirme la línea de descendencia y luego decida: reconstruir desde un send completo o reconstruir la cadena de snapshots esperada.

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

1) Síntoma: receive incremental falla con “does not exist” o “incremental source … is not found”

Causa raíz: el receptor no tiene el snapshot base exacto (nombre equivocado, podado o diferente GUID lineage después de una restauración/rollback).

Solución: liste snapshots en ambos lados; compare GUIDs; haga un nuevo send completo para restablecer una cadena limpia. Evite eliminar snapshots manualmente en el árbol de réplica receptor.

2) Síntoma: receive “succeeds” pero los datasets se montan en rutas de producción en el host DR

Causa raíz: se aplicaron propiedades mountpoint recibidas, y zfs mount -a (o el arranque) los montó.

Solución: recibir con -u; forzar mountpoint=none en el árbol réplica; solo establecer mountpoints en clones de restauración.

3) Síntoma: el simulacro de restauración se atasca en el paso “cargar claves” y nadie las encuentra

Causa raíz: la gestión de claves de cifrado se trató como “problema de alguien”, no como una dependencia operativa.

Solución: defina custodia de claves, ubicación de almacenamiento y procedimiento de acceso; pruebe la carga de claves en cada simulacro; documente acceso de emergencia con aprobaciones.

4) Síntoma: el throughput de replicación está bien de noche pero es terrible en horario laboral

Causa raíz: enlaces de red compartidos, política QoS, cargas competidoras en emisor/receptor o scrubs programados durante el pico.

Solución: mida durante el pico; programe scrubs fuera de ventanas de replicación; implemente shaping de tráfico si es necesario; considere targets de staging o enlaces dedicados.

5) Síntoma: “cannot receive: failed to read from stream” a mitad de transferencia

Causa raíz: sesión SSH rota, mismatch de MTU, caídas de red o falta de espacio que abortan y se muestran como errores de stream.

Solución: compruebe espacio libre en el receptor; revise logs por desconexiones; vuelva a ejecutar con monitorización; prefiera streams reanudables si su versión de ZFS los soporta, y mantenga suficiente holgura.

6) Síntoma: la aplicación arranca, pero los datos están sutilmente equivocados o antiguos

Causa raíz: restauró el snapshot equivocado (ambigüedad de nombres) o la app necesita estado adicional (WAL, almacenamiento de objetos, secretos) no cubierto por la replicación del dataset.

Solución: haga cumplir reglas de nombrado y selección de snapshots; agregue dependencias no-ZFS al alcance DR; valide con una transacción de negocio real en el simulacro.

7) Síntoma: receive es lento y CPU está al máximo, pero los discos están inactivos

Causa raíz: sobrecarga de cifrado SSH, coste de compresión/descompresión o cuello de botella single-thread en el pipeline.

Solución: perfilar CPU; ajustar selección de cifrados; considerar replicación en una red aislada y de confianza con decisiones de transporte apropiadas; evite apilar compresión en múltiples capas a ciegas.

8) Síntoma: la restauración funciona una vez y luego los siguientes simulacros son más desordenados y lentos

Causa raíz: el host DR se convierte en cajón de cosas semi-restauradas, montajes y réplicas modificadas; “solo un arreglo rápido” se vuelve estado.

Solución: trate el entorno DR como código: reconstruya o reinicie entre simulacros; mantenga réplicas de solo lectura; restaure vía clone y destruya después del simulacro.

Tres minihistorias corporativas del mundo real

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

Una empresa mediana tenía dos sitios y una configuración de replicación ZFS ordenada. Producción enviaba snapshots horarios a un host DR.
El equipo estaba confiado porque los logs de receive mostraban “success” durante meses.

Entonces un problema de controlador de almacenamiento dejó fuera al pool primario. No fue catastrófico—exactamente para lo que sirve DR.
Promovieron el dataset DR e intentaron levantar servicios. El filesystem se montó. La app arrancó. La base de datos se negó.

La suposición equivocada: “Replicamos el dataset de la app, así que replicamos todo el sistema.” En realidad, los logs de la base de datos vivían en un dataset separado que se añadió después.
No se incluyó en el trabajo de replicación. Nadie lo notó porque nada falló durante la replicación; simplemente omitió silenciosamente la dependencia nueva.

Bajo presión de la avería, alguien intentó “arreglarlo” copiando el dataset faltante desde una copia de una VM obsoleta. La base de datos arrancó, pero con un desfase temporal.
Ahora tenían un servicio en funcionamiento pero lógicamente inconsistente—peor que la caída porque la integridad de los datos estaba en duda.

La mejora post-incidente no fue exótica: un inventario de datasets por servicio, comprobaciones de cobertura de replicación y un simulacro DR que exigía una ruta de escritura de extremo a extremo.
La lección no fue “ZFS falló”. La lección fue “nuestro modelo mental falló, y ZFS no nos detuvo”.

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

Otra organización quería replicación más rápida. Alguien notó uso de CPU durante la replicación por SSH y decidió optimizar.
Cambiaron el pipeline, añadieron compresión extra y ajustaron algunos flags. Parecía genial en una prueba rápida: streams más pequeños, mayor throughput pico.

Un mes después, los receives DR empezaron a quedarse atrás en horas punta. No por minutos—por horas. La cola de replicación creció.
El equipo persiguió la red, luego los discos, luego el scheduler.

El problema: la capa extra de compresión dejó al emisor limitado por CPU bajo carga real, y aumentó la varianza de latencia.
Las pruebas se hicieron en un sistema inactivo con ARC caliente, no bajo la caótica realidad de escrituras de producción.
La replicación se volvió frágil: a veces rápida, a veces glacial, siempre impredecible.

La solución fue casi embarazosa: eliminar la capa de compresión extra, confiar en las características de compresión nativas de ZFS y medir con una ventana de carga representativa.
También limitaron la concurrencia y programaron replicación para no interferir con ventanas de scrub.

La mejor optimización es la que puede explicar al siguiente on-call en dos frases. Si requiere una pizarra y una oración, no es una optimización; es un rasgo de personalidad.

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

Un equipo de servicios financieros tenía fama de ser dolorosamente metódico. Realizaban simulacros DR trimestrales con una lista de verificación, marcas de tiempo y la costumbre de destruir y reconstruir los datasets de restauración cada vez.
No era glamuroso, así que a menudo lo ridiculizaban equipos que preferían “innovar”.

Llegó un incidente real durante una ventana de mantenimiento rutinaria: un ingeniero ejecutó un comando destroy contra el host equivocado.
Fue el tipo de error que sucede cuando el entorno se parece y sus pestañas de terminal superan su capacidad de atención.

El equipo DR no improvisó. Siguieron el runbook: identificar el último snapshot bueno en el receptor, clonar en el espacio de restauración, cargar claves, montar, iniciar servicios y ejecutar pruebas básicas.
Como habían practicado los mismos comandos repetidamente, nadie discutió qué snapshot usar ni dónde debían aterrizar los mountpoints.

La recuperación no fue instantánea, pero fue controlada. Lo más importante: no contaminaban la réplica haciendo cambios ad-hoc para “hacerla funcionar”.
La réplica permaneció como fuente de la verdad, y el clone restaurado fue descartable.

Más tarde, los ejecutivos lo llamaron “buena suerte”. No lo fue. Fue un hábito aburrido que evitó que los humanos empeoraran la situación.

Listas de verificación / plan paso a paso

Lista de diseño del simulacro (antes del día del simulacro)

  • Elija un servicio y liste cada dataset del que depende (datos, BD, logs, configs, secretos si aplica).
  • Defina objetivos RPO y RTO en minutos/horas, no en sensaciones.
  • Decida el namespace objetivo de restauración (drpool/recv para réplicas, drpool/restore para restauraciones escribibles).
  • Decida el método de gestión de claves para datasets cifrados; verifique rutas de acceso para on-call.
  • Defina convención de nombres de snapshots usada para DR (p. ej., dr-YYYY-MM-DD_HHMM).
  • Ponga de acuerdo qué constituye “servicio restaurado”: pruebas básicas, transacción sintética o endpoints de salud específicos.
  • Programe el simulacro durante una ventana de carga realista al menos una vez al año (el pico revela mentiras).

Lista de preparación de replicación (día del simulacro, pre-vuelo)

  • Sender y receiver pools saludables (zpool status -x).
  • El receptor tiene espacio libre suficiente para snapshots + holgura.
  • Los datasets de réplica están desmontados por defecto (-u, política mountpoint=none).
  • Confirmar presencia y frescura de snapshots en ambos lados.
  • Confirmar su política de rollback (aceptable zfs receive -F o no para esta réplica).

Plan de ejecución de restauración (el simulacro en sí)

  1. Declare la hora de inicio del simulacro. Anótela. DR sin marcas de tiempo es solo cosplay.
  2. Seleccione el snapshot. Use el snapshot más nuevo realmente presente en DR que satisfaga su RPO.
  3. Creé un clone de restauración. Nunca restaure modificando el árbol de réplica directamente a menos que le guste vivir peligrosamente.
  4. Establezca mountpoints explícitamente. Evite mountpoints recibidos heredados; están optimizados para sorpresas.
  5. Cargue claves (si está cifrado). Valide keystatus=available.
  6. Monte datasets en el orden correcto. BD antes de la app si su servicio lo espera.
  7. Inicie servicios. Use comandos del gestor de servicios estándar y capture logs.
  8. Ejecute pruebas básicas. Una escritura real, una ruta de lectura, una consulta o un inicio de sesión de usuario.
  9. Registre el RTO. Pare el cronómetro cuando el servicio pase los criterios de prueba, no cuando un dataset se monte.
  10. Limpie. Destruya clones de restauración y mountpoints temporales; mantenga la réplica intacta.
  11. Anote lo que le sorprendió. Ese es todo el propósito.

Broma #2: Un simulacro DR es la única reunión donde “deberíamos destruir todo y empezar de nuevo” es a la vez correcto y alentado.

Preguntas frecuentes

1) ¿La replicación ZFS es una copia de seguridad?

Puede ser parte de una estrategia de backups, pero por sí sola a menudo es un espejo de sus errores. Si el ransomware cifra archivos y replica esos cambios rápidamente,
felicitaciones: tiene dos copias del problema. Aún querrá retención, controles de inmutabilidad y, idealmente, al menos una copia offline o lógicamente aislada.

2) ¿Debería recibir directamente en los mountpoints finales?

Para simulacros DR, no. Reciba en un namespace inerte con -u y mountpoints neutrales, luego clone a un namespace de restauración con mountpoints explícitos.
Reduce la posibilidad de montar sobre algo importante o iniciar servicios contra el dataset equivocado.

3) ¿Cuándo es apropiado zfs receive -F?

Cuando el dataset receptor es una réplica pura y está cómodo con revertirla para aceptar streams entrantes.
Es inapropiado cuando el receptor tiene cambios locales que le importan o cuando no entiende por qué es necesario.

4) ¿Cómo elijo qué snapshot restaurar?

Use el snapshot más reciente que esté realmente presente en el receptor y cumpla su RPO. Luego valide la integridad de la aplicación.
El nombrado de snapshots debe codificar el propósito (DR vs ad-hoc) para que “último” no signifique accidentalmente “el experimento de alguien”.

5) ¿Y los datasets cifrados—necesito claves en el host DR?

Si envía streams cifrados raw (zfs send -w), el receptor puede almacenar datos cifrados sin claves, pero no podrá montar para uso hasta que se carguen las claves.
Decida si las claves se almacenan en DR (más rápido) o se recuperan durante el incidente (más seguro). Pruebe la opción que elija.

6) ¿Debo replicar propiedades con -p?

A veces. La replicación de propiedades puede preservar comportamientos importantes (compresión, recordsize, ajustes ACL), pero también puede arrastrar mountpoints y convenciones locales que no desea.
Un compromiso común: replicar la mayoría de las propiedades, pero aplicar la política de mountpoint en el receive y restaurar vía clone.

7) ¿Por qué mi receive es lento cuando el emisor es rápido?

Porque el rendimiento del receive suele estar limitado por el receptor: IOPS de escritura, fragmentación, scrubs/resilvers y CPU para checksum/descompresión.
Mida el throughput extremo a extremo y luego observe primero iostat y la actividad del pool receptor.

8) ¿Necesito probar restauraciones si los logs de replicación muestran éxito?

Sí. Los logs le dicen “un stream se aplicó”. No le dicen “el servicio es recuperable”, “se pueden cargar las claves”, “los mountpoints son seguros” o “el RTO es alcanzable”.
Los simulacros DR detectan brechas operativas, no errores del sistema de archivos.

9) ¿Con qué frecuencia deberíamos ejecutar un simulacro DR?

Trimestral si puede, semestral si debe, anual solo si su entorno cambia muy lentamente (no cambia).
Ejecute al menos un simulacro al año bajo carga realista y ejercicios de mesa menores siempre que cambie personal clave o arquitectura.

10) ¿El host DR debe tener hardware idéntico a producción?

No siempre, pero necesita previsibilidad de rendimiento. Si DR es materialmente más lento, su RTO aumentará.
Si DR usa disposiciones de disco o rutas de red diferentes, los simulacros deben medir la velocidad real de restauración, no una teórica.

Conclusión: próximos pasos que puede programar esta semana

Si solo “prueba copias de seguridad” viendo que existen snapshots, está probando papeleo. ZFS send/receive es potente, pero no es magia.
La ruta de restauración es donde su diseño del sistema se encuentra con la realidad humana: nombres, acceso a claves, mountpoints y presión de tiempo.

Pasos prácticos a seguir:

  1. Elija un servicio e inventaríe sus datasets y dependencias no-ZFS.
  2. Defina RPO/RTO y anote qué significa “hecho” en la capa de aplicación.
  3. Construya un namespace de recepción seguro con réplicas desmontadas y mountpoints neutrales.
  4. Ejecute un simulacro de restauración completo usando restauraciones basadas en clones y carga explícita de claves.
  5. Registre tiempos y cuellos de botella, luego arregle el mayor—capacidad, claves o throughput.
  6. Repita hasta que el simulacro sea aburrido. Aburrido es confiable.
← Anterior
Perfiles de Docker Compose: pilas Dev/Prod sin YAML duplicado
Siguiente →
Fallos de passthrough PCI en Proxmox: Grupos IOMMU y las trampas clásicas

Deja un comentario