La replicación solo parece aburrida cuando funciona. Cuando no funciona, se convierte en una película de terror en cámara lenta:
el CEO pregunta “¿Tenemos una copia?” y respondes “Sí”, mientras tu estómago presenta una declaración de quiebra en silencio.
ZFS facilita peligrosamente creer que estás a salvo porque algunas instantáneas existen en algún lado.
Este artículo trata sobre demostrar que la réplica es real: actual, consistente, descifrable, restaurable y usable
ante la falla exacta que realmente vas a sufrir.
Qué significa “replicación real” en producción
“Replicamos ZFS” puede significar al menos seis cosas diferentes, y cinco de ellas son “nos sentimos bien hasta que no”.
Una réplica real no es solo “los datos existen en el otro host.” Es:
- Suficientemente actual: tu RPO se cumple por observación, no por optimismo.
- Consistente: se deriva de una cadena de instantáneas coherente, no de un parcheo accidental.
- Completa: todos los datasets que te importan están incluidos (incluyendo hijos, propiedades y holds cuando proceda).
- Descifrable: si hay cifrado, realmente puedes cargar las claves y montarlo en el peor día del año.
- Restaurable: puedes promover, revertir, montar y entregar el resultado a una aplicación sin arqueología.
- Operativamente segura: la replicación no sabotea producción mediante picos de carga, instantáneas enormes o retropresión.
La replicación de ZFS se basa en instantáneas. Eso es tanto su superpoder como su trampa. Superpoder: es precisa y eficiente.
Trampa: si falta un eslabón en la cadena, el “incremental” que creías estar enviando deja de ser incremental.
Además, el stream de send es fiel a la instantánea desde la que se produjo; no le importa que tu base de datos necesitara
quiescencia y no la hiciste.
Regla guía: audita la ruta de restauración, no la ruta de envío. Enviar datos es fácil.
Restaurarlos bajo presión es donde tus suposiciones no examinadas vienen a morir.
Una verdad seca del mundo de operaciones: “Tenemos backups” no es un estado; es una afirmación que requiere evidencia.
Si no puedes responder “¿Cuál es la instantánea recuperable más nueva para el dataset X, y cuánto tardaría en ponerse en servicio?”
no tienes replicación, tienes vibes.
Idea parafraseada de John Allspaw (operaciones/confiabilidad): la fiabilidad proviene de aprender en el mundo real y desordenado, no de creer en el plan
.
Auditar la replicación es ese bucle de aprendizaje, aplicado al almacenamiento.
Datos interesantes y contexto histórico
- ZFS fue diseñado alrededor de checksums de extremo a extremo (datos y metadatos), haciendo la corrupción silenciosa detectable—si haces scrub y monitoreo.
- Las instantáneas son baratas en ZFS porque son referencias copy-on-write, no copias completas. Aun así pueden volverse costosas si las mantienes para siempre.
- El ZFS original salió de Sun Microsystems; su enfoque de replicación (send/receive) se construyó para mover deltas de instantáneas de forma segura y determinista.
- OpenZFS se volvió un esfuerzo multiplataforma después del declive de Sun, y la paridad de funciones varía según la distribución y versión del SO—importante para los tokens de resume y el comportamiento del cifrado.
- Los streams de replicación pueden incluir propiedades del dataset dependiendo de flags; la falta de propiedades es un clásico fallo de “se restauró pero está mal”.
- Los tokens de reanudación existen porque los envíos largos fallan en redes reales. No son decoración; son lo que te evita reenviar 40 TB tras un fallo de enlace de 3 segundos.
- El cifrado en ZFS es por dataset, no por pool, y la gestión de claves en el receptor es todo un problema operativo aparte.
- Los scrubs no son backups, pero son una herramienta de auditoría: te dicen si la réplica puede leer sus propios datos con fiabilidad.
Un modelo de auditoría que atrapa las mentiras habituales
Mentira #1: “La réplica existe” (pero no son los datasets correctos)
Replicaste tank/data pero no tank/data/mysql porque alguien lo creó después
y tu script enumeró datasets una vez, en 2022, y nunca más. El receptor parece sano. La restauración es una mirada en blanco.
Tu auditoría debe comparar “qué debería protegerse” vs “qué está protegido” y señalar la deriva.
Mentira #2: “Está actual” (pero el horario de instantáneas está roto)
La replicación no puede estar más actual que la instantánea más nueva en el emisor. Si las instantáneas se detienen, la replicación “tiene éxito” mientras
no mueve nada. Tu auditoría debe verificar la creación de instantáneas, el nombrado y que la instantánea más nueva llegue al receptor.
Mentira #3: “Es consistente” (pero la cadena está fracturada)
La replicación incremental asume que ambos lados comparten una instantánea común. Elimina una en el receptor, y tu siguiente send incremental falla.
O peor: parece tener éxito porque recurriste a un send completo sin notarlo, generando picos de ancho de banda y tiempo de ejecución.
Auditar significa verificar la instantánea base común y rastrear envíos completos inesperados.
Mentira #4: “Se puede restaurar” (pero el receptor no es usable en DR)
El receptor puede ser de solo lectura, puede tener canmount=off, puede necesitar claves de cifrado, puede requerir promoción de clones,
puede carecer de puntos de montaje que esperas, o puede restaurar con recordsize/compression equivocados y destrozar el rendimiento.
Una auditoría real incluye una prueba de restauración no destructiva a un host de staging, más un runbook documentado de “promover y montar”.
Mentira #5: “Tenemos ancho de banda” (pero no tenemos tiempo)
Tu replicación puede “terminar eventualmente” pero aún así incumplir RPO y RTO porque el delta creció más rápido de lo que el enlace puede mover.
Auditar significa medir tamaños de send, tasas de receive y crecimiento de instantáneas a lo largo del tiempo. Sin conjeturas. Sin “debería estar bien.”
Broma #1: Los planes de replicación son como paraguas—todo el mundo los recuerda justo después de que empieza a llover.
Guía de diagnóstico rápido
Esta es la secuencia de “son las 02:13 y la replicación está atrasada”. El objetivo es encontrar el cuello de botella en minutos, no horas.
Comprueba esto en orden; para cuando encuentres la primera restricción seria.
Primero: ¿Hay algo que replicar?
- ¿Se está ejecutando la creación de instantáneas en el emisor?
- ¿La instantánea más nueva en el emisor es más reciente que la más nueva en el receptor?
- ¿Estás atascado por una instantánea común faltante (cadena incremental rota)?
Segundo: ¿El proceso de replicación está moviendo datos realmente?
- ¿Se está ejecutando
zfs receivey consumiendo el stream? - ¿Estás bloqueado en un resume token?
- ¿La red está saturada o es inestable?
Tercero: ¿El receptor puede escribir?
- Salud del pool: ¿degradado, suspendido o sin espacio?
- Latencia IOPS: ¿los discos se están ahogando o falta/un mal uso de SLOG?
- ¿Hay scrub/resilver intensos compitiendo con la replicación?
Cuarto: ¿Es el emisor el cuello de botella?
- ¿Sobrecarga por compresión/cifrado durante el send?
- ¿Deltas de instantánea enormes por errores de retención?
- ¿Diseño de datasets (recordsize, sync) causando IO patológico?
Quinto: ¿Estás perdiendo tiempo por reprocesos evitables?
- ¿Accidentalmente haces full sends porque la instantánea base no coincide?
- ¿No usas resume tokens sobre enlaces poco fiables?
- ¿Reenvías propiedades y fuerzas churn innecesario?
Tareas prácticas de auditoría (comandos, salidas, decisiones)
Esto no es “bueno saberlo.” Son los comandos que ejecutas cuando quieres dejar de creer y empezar a saber.
Asumiré un host emisor zfs-prod-01 con pool tank, y un receptor zfs-dr-01 con pool backup.
Tarea 1: Confirmar salud del pool en ambos lados
cr0x@server:~$ zpool status -x
all pools are healthy
Qué significa: sin fallos conocidos, sin vdevs degradados, sin IO suspendido.
Si ves otra cosa, la corrección de la replicación es secundaria a la supervivencia del pool.
Decisión: Si no está sano, pausa la replicación agresiva y arregla el pool primero; un receptor degradado puede extender silenciosamente el RTO.
Tarea 2: Verificar que el receptor tiene suficiente espacio libre para los peores deltas
cr0x@server:~$ zfs list -o name,used,avail,refer,mountpoint backup
NAME USED AVAIL REFER MOUNTPOINT
backup 62.1T 8.40T 128K /backup
Qué significa: solo 8.4T libres. Si tu próximo delta es 10T por un error de retención, receive fallará a mitad de stream.
Decisión: Establece una política rígida de “espacio libre mínimo” (y alerta sobre ello). Si está por debajo del umbral, recorta instantáneas o amplía capacidad antes del próximo envío grande.
Tarea 3: Enumerar lo que crees replicar (emisor)
cr0x@server:~$ zfs list -r -o name,type,mountpoint tank/data
NAME TYPE MOUNTPOINT
tank/data filesystem /tank/data
tank/data/mysql filesystem /tank/data/mysql
tank/data/pg filesystem /tank/data/pg
tank/data/home filesystem /tank/data/home
Qué significa: este es tu alcance previsto. Tu herramienta de replicación debe cubrirlo todo (o excluir partes explícitamente).
Decisión: Si tus scripts replican solo una lista fija, reemplázalos por descubrimiento recursivo de datasets más reglas allow/deny que vivan en control de versiones.
Tarea 4: Enumerar lo que realmente existe en el receptor
cr0x@server:~$ zfs list -r -o name,type,mountpoint backup/data
NAME TYPE MOUNTPOINT
backup/data filesystem /backup/data
backup/data/mysql filesystem none
backup/data/home filesystem none
Qué significa: falta backup/data/pg. Además, los mountpoints son none (lo que podría ser intencional para DR).
Decisión: Los datasets faltantes son un problema que para la línea. Arregla el alcance de replicación antes de discutir rendimiento.
Tarea 5: Comprobar marcas de tiempo de las instantáneas más nuevas en emisor y receptor (realidad RPO)
cr0x@server:~$ zfs list -t snapshot -o name,creation -s creation -r tank/data/mysql | tail -n 3
tank/data/mysql@rep_2026-02-04_0000 Tue Feb 4 00:00 2026
tank/data/mysql@rep_2026-02-04_0030 Tue Feb 4 00:30 2026
tank/data/mysql@rep_2026-02-04_0100 Tue Feb 4 01:00 2026
cr0x@server:~$ zfs list -t snapshot -o name,creation -s creation -r backup/data/mysql | tail -n 3
backup/data/mysql@rep_2026-02-04_0000 Tue Feb 4 00:00 2026
backup/data/mysql@rep_2026-02-04_0030 Tue Feb 4 00:30 2026
backup/data/mysql@rep_2026-02-04_0100 Tue Feb 4 01:00 2026
Qué significa: el receptor coincide con el emisor hasta 01:00. El RPO para ese dataset es actualmente “minutos”, no “quizás”.
Decisión: Monitorea esto automáticamente. Si la instantánea más nueva del receptor se retrasa más allá de la política, avisa al on-call. Este es el punto entero.
Tarea 6: Confirmar que la instantánea base incremental existe en ambos lados
cr0x@server:~$ zfs list -t snapshot -o name -r tank/data/mysql | grep rep_2026-02-03_2300
tank/data/mysql@rep_2026-02-03_2300
cr0x@server:~$ zfs list -t snapshot -o name -r backup/data/mysql | grep rep_2026-02-03_2300
backup/data/mysql@rep_2026-02-03_2300
Qué significa: la base común existe. Si no existe, el siguiente send con -i fallará con “incremental source does not exist.”
Decisión: Si la base falta, decide entre (a) restaurar la instantánea faltante (si está disponible), (b) hacer un send completo, o (c) reconstruir el dataset del receptor.
Tarea 7: Detectar envíos completos inesperados estimando tamaños de delta
cr0x@server:~$ zfs send -nPv -i tank/data/mysql@rep_2026-02-04_0000 tank/data/mysql@rep_2026-02-04_0100
size 2.31G
incremental tank/data/mysql@rep_2026-02-04_0000 tank/data/mysql@rep_2026-02-04_0100
no-op bytes 132K
Qué significa: el delta esperado es ~2.31G. Si ejecutas esto y ves “size 4.8T,” estás a punto de encontrar la religión.
Decisión: Si el tamaño del delta está muy por encima de lo normal, para e investiga retención de instantáneas, archivos huida, o una cadena incremental rota antes de saturar enlaces durante días.
Tarea 8: Comprobar tokens de reanudación en el receptor (replicación atascada)
cr0x@server:~$ zfs get -H -o name,value receive_resume_token backup/data/mysql
backup/data/mysql 1-EMyVd...AAAB
Qué significa: existe un token: un receive fue interrumpido. Tu automatización puede estar iniciando nuevos sends que no pueden adjuntarse.
Decisión: O reanuda el stream usando el token o aborta y limpia el estado explícitamente (con cuidado). Trata los tokens como un log transaccional, no como basura.
Tarea 9: Reanudar un envío interrumpido de forma segura
cr0x@server:~$ TOKEN=$(zfs get -H -o value receive_resume_token backup/data/mysql)
cr0x@server:~$ ssh zfs-prod-01 "zfs send -t $TOKEN" | zfs receive -s -F backup/data/mysql
cr0x@server:~$ echo $?
0
Qué significa: código de salida 0: la reanudación del receive completó.
Decisión: Añade lógica consciente de tokens a tu herramienta de replicación. Si tu proceso no lo soporta, estás a un enlace inestable de repetir terabytes.
Tarea 10: Validar replicación de propiedades (la comprobación de “se restauró pero está mal”)
cr0x@server:~$ zfs get -H -o name,property,value compression,recordsize,atime,canmount tank/data/mysql
tank/data/mysql compression zstd
tank/data/mysql recordsize 16K
tank/data/mysql atime off
tank/data/mysql canmount on
cr0x@server:~$ zfs get -H -o name,property,value compression,recordsize,atime,canmount backup/data/mysql
backup/data/mysql compression zstd
backup/data/mysql recordsize 128K
backup/data/mysql atime on
backup/data/mysql canmount noauto
Qué significa: el receptor difiere: recordsize y atime no coinciden, y canmount es deliberadamente distinto.
La discrepancia de recordsize puede destrozar el rendimiento de la base de datos tras el failover.
Decisión: Decide qué propiedades deben coincidir para DR. Replica intencionalmente esas propiedades (o hazlas cumplir mediante una política post-receive).
No “optimices” accidentalmente la réplica hasta dejarla inutilizable.
Tarea 11: Confirmar estado de cifrado y disponibilidad de claves (no lo descubras durante el DR)
cr0x@server:~$ zfs get -H -o name,property,value encryption,keystatus,keylocation tank/secure/hr
tank/secure/hr encryption aes-256-gcm
tank/secure/hr keystatus available
tank/secure/hr keylocation prompt
cr0x@server:~$ zfs get -H -o name,property,value encryption,keystatus,keylocation backup/secure/hr
backup/secure/hr encryption aes-256-gcm
backup/secure/hr keystatus unavailable
backup/secure/hr keylocation prompt
Qué significa: el receptor tiene datos cifrados pero sin clave cargada. Eso puede ser correcto (seguridad) pero debe estar planificado operativamente.
Decisión: Implementa un proceso de custodia de claves y un runbook para cargar claves en DR. Si nadie puede cargar claves a las 03:00, no tienes réplica.
Tarea 12: Realizar una prueba de montaje no destructiva en el receptor (demuestra la ruta de restauración)
cr0x@server:~$ zfs clone backup/data/mysql@rep_2026-02-04_0100 backup/test-restore/mysql
cr0x@server:~$ zfs set canmount=noauto mountpoint=/mnt/restore-mysql backup/test-restore/mysql
cr0x@server:~$ zfs mount backup/test-restore/mysql
cr0x@server:~$ zfs list -o name,mountpoint backup/test-restore/mysql
NAME MOUNTPOINT
backup/test-restore/mysql /mnt/restore-mysql
Qué significa: puedes materializar una vista punto en el tiempo sin tocar el dataset replicado principal.
Decisión: Haz de esto un ejercicio programado. Si montar requiere “conocimiento tribal”, escríbelo y repásalo.
Tarea 13: Verificar integridad de datos con estado de scrub en el receptor
cr0x@server:~$ zpool status backup
pool: backup
state: ONLINE
scan: scrub repaired 0B in 05:14:02 with 0 errors on Sun Feb 2 03:12:44 2026
config:
NAME STATE READ WRITE CKSUM
backup 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
Qué significa: scrub completado sin errores. Esa es evidencia de que tu réplica puede leerse a sí misma.
Decisión: Si los scrubs muestran errores, trata el receptor como sospechoso hasta que se repare; replicar a medios dañados no es “seguridad”.
Tarea 14: Buscar deriva en la retención de instantáneas (emisor y receptor deben coincidir)
cr0x@server:~$ zfs list -t snapshot -o name -r tank/data/mysql | wc -l
336
cr0x@server:~$ zfs list -t snapshot -o name -r backup/data/mysql | wc -l
92
Qué significa: el receptor tiene muchas menos instantáneas. Quizá intencional. Quizá alguien corre limpieza solo en DR y rompió la cadena incremental.
Decisión: Alinea la política de retención con el método de replicación. Si podas en el receptor, debes asegurar que la instantánea base necesaria para incrementales se preserve.
Tarea 15: Verificar que la replicación no compite con resilver/scrub en el momento equivocado
cr0x@server:~$ zpool iostat -v backup 5 2
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
backup 62.1T 8.40T 120 980 9.2M 112M
raidz2-0 62.1T 8.40T 120 980 9.2M 112M
sda - - 30 240 2.3M 28M
sdb - - 28 260 2.1M 29M
sdc - - 32 250 2.4M 28M
sdd - - 30 230 2.4M 27M
Qué significa: las escrituras son altas; si la latencia también es alta, receive puede arrastrarse. Si ves scrubs/resilver en marcha, espera que empeore.
Decisión: Programa ventanas de replicación o limita la tasa de send cuando el pool ya está ocupado con trabajo de curación.
Tarea 16: Validar qué instantáneas están reteniendo espacio (por qué los deltas son enormes)
cr0x@server:~$ zfs holds -r tank/data/mysql@rep_2026-02-01_0000
NAME TAG TIMESTAMP
tank/data/mysql@rep_2026-02-01_0000 keep Sun Feb 1 00:00 2026
Qué significa: un hold evita la eliminación. Los holds son útiles, pero también pueden fijar meses de churn.
Decisión: Audita los holds y hazlos intencionales. Si “keep” lo puso una persona, registra por qué y por cuánto tiempo.
Broma #2: Un resume token es como ese cajón de cables misteriosos—inútil hasta que de repente es lo único que salva tu fin de semana.
Tres micro-historias corporativas (cómo falla esto)
1) Incidente causado por una suposición errónea: “La replicación tuvo éxito porque el job dijo OK”
Una empresa mediana ejecutaba replicación ZFS desde producción a un sitio DR. El job de replicación era un script limpio
ejecutado por cron. Escribía logs. Incluso enviaba un correo de “éxito” si el código de salida de la tubería era cero.
Todos dormían bien. Demasiado bien.
La trampa: las instantáneas las creaba otro job en el host de producción. Ese job falló silenciosamente tras una actualización de paquete
que cambió la ruta de un script. La replicación siguió ejecutándose, no encontró nuevas instantáneas y no hizo nada—de forma limpia.
Los logs mostraban ejecuciones exitosas porque el paso de send envió correctamente nada y el paso de receive recibió correctamente nada.
Meses después, una caída de producción requirió conmutación por error. Los datasets en DR existían. Se montaron. Los servicios arrancaron.
Y los datos tenían la edad suficiente para votar.
Nadie había estado midiendo “instantánea más nueva en receptor vs emisor”, así que no saltó ninguna alerta. El sistema de replicación estaba “verde”
de exactamente la forma que debería asustarte.
La solución fue aburrida y efectiva: añadieron una comprobación rígida de RPO que comparaba marcas de tiempo de instantáneas por dataset, más una alerta si
la “edad de la última instantánea replicada” excedía la política. También hicieron que la creación de instantáneas fuera parte de la misma unidad de trabajo:
una orquestación, un log, un conjunto de métricas.
2) Optimización que salió mal: “Reduciremos el número de instantáneas en DR para ahorrar espacio”
Otra organización tenía un pool DR ajustado. Alguien miró el recuento de instantáneas y decidió que el lado DR no necesitaba tantas.
El host DR ejecutaba una política de limpieza que eliminaba instantáneas antiguas más agresivamente que producción.
En papel, era eficiente: menos instantáneas, menos espacio, menos sobrecarga de metadatos.
Entonces los incrementales empezaron a fallar intermitentemente. La herramienta de replicación intentaba enviar desde la última instantánea común,
pero esa instantánea había sido borrada en el receptor. La herramienta reaccionó cayendo a un envío completo—porque el autor del script
pensó “mejor replicar despacio que fallar.” Funcionó, hasta que no.
Los envíos completos duraron días. Colisionaron con otros IO. Saturaron la WAN. Incrementaron la edad de las instantáneas, lo que aumentó los deltas,
y empeoró los siguientes envíos. Bucle de retroalimentación positivo clásico. El sistema no colapsó por un bug. Colapsó por una
“optimización” y tres salvaguardas faltantes.
La solución eventual: unificar la lógica de retención entre emisor y receptor, y proteger explícitamente las N instantáneas más recientes en el
receptor con holds para que los incrementales siempre tuvieran una base. El equipo también dejó de permitir la caída automática a envíos completos sin
una alerta ruidosa y decisión humana.
3) Práctica aburrida pero correcta que salvó el día: “Drills trimestrales de restauración en una caja de staging”
Un negocio regulado (piensa: papeleo de cumplimiento que se reproduce por la noche) tenía una costumbre que los ingenieros inicialmente se burlaron:
cada trimestre realizaban un drill de restauración. No un ejercicio de mesa. Una restauración real a un host de staging, con la misma familia de SO,
las mismas banderas de características ZFS, y una versión recortada de la aplicación.
La lista de verificación del drill era simple. Confirmar que la instantánea más reciente existe. Clonarla. Montarla. Ejecutar una comprobación ligera de integridad.
Iniciar el servicio apuntando al dataset restaurado. Validar comportamiento básico de la app. Documentar el tiempo de ejecución.
Luego destruir el clone. Sin heroísmos, sin “lo recordaremos luego.”
Cuando más tarde sufrieron un incidente de ransomware en producción, el equipo de almacenamiento no tuvo que inventar un plan de recuperación en la sala de reuniones.
Ya tenían memoria muscular para: “identificar la última instantánea buena, clonar en DR, promover si es requerido, levantar el servicio.”
La restauración siguió sin ser divertida—nada es divertido durante un ransomware—pero fue ejecutable.
La mayor ganancia fue psicológica: porque habían practicado, no perdieron horas discutiendo si la réplica era válida.
Estaban debatiendo qué instantánea usar, no si existían instantáneas.
Eso es lo que parece la competencia: poco glamorosa, ensayada y rápida.
Errores comunes (síntoma → causa raíz → solución)
La replicación “tiene éxito” pero los datos de DR están obsoletos
- Síntoma: los jobs reportan éxito; la instantánea más reciente en el receptor tiene horas/días de antigüedad.
- Causa raíz: la creación de instantáneas se detuvo; la replicación corrió sin nuevas instantáneas; no existía chequeo de RPO.
- Solución: alerta sobre “edad de la instantánea más reciente por dataset” y “edad de la última instantánea replicada.” Trata “no hay nuevas instantáneas” como fallo salvo que esté explícitamente esperado.
Los sends incrementales fallan: “does not exist” o errores de “incremental source”
- Síntoma: errores de send/receive mencionando instantáneas faltantes; la automatización reintenta indefinidamente.
- Causa raíz: receptor podó instantáneas o nunca recibió la base; mismatch en nombres; replicación iniciada a mitad de cadena.
- Solución: hacer cumplir la simetría de retención para la “ventana base”; usar holds en el receptor para las instantáneas base requeridas; reconstruir dataset con un full send cuando la cadena esté irremediablemente rota.
La replicación es lenta a pesar de tener mucho ancho de banda
- Síntoma: la red no está saturada, pero receive va muy lento; latencia del pool alta.
- Causa raíz: los discos del receptor son el cuello de botella; scrub/resilver compitiendo; datasets con recordsize pequeño causando IOPS altos.
- Solución: programa o limita la replicación durante la curación; mide con
zpool iostat; considera ajustar propiedades en producción (con cuidado) en lugar de “arreglar” DR por separado.
La réplica se monta, pero el rendimiento de la aplicación es terrible tras el failover
- Síntoma: el servicio en DR arranca pero está lento; métricas DB parecen melaza.
- Causa raíz: propiedades divergieron (recordsize, compression, atime, logbias); la replicación no incluyó propiedades o el receptor impuso defaults distintos.
- Solución: define un conjunto de propiedades “que deben coincidir”; replica propiedades intencionalmente; valida mediante diffs periódicos de propiedades y drills de restauración.
Datasets cifrados replican, pero DR no puede montarlos
- Síntoma: los datasets existen en DR;
keystatus=unavailable; los montajes fallan durante el incidente. - Causa raíz: gestión de claves no integrada en DR; las claves requieren entrada manual de alguien que está dormido o no disponible.
- Solución: establece custodia de claves y procedimiento break-glass; prueba la carga de claves en DR trimestralmente; asegura compatibilidad de banderas de funciones.
La replicación se reinicia aleatoriamente desde cero
- Síntoma: transferencias grandes se repiten; el tiempo de job crece; las facturas WAN se ponen interesantes.
- Causa raíz: streams interrumpidos sin reanudación; tokens de reanudación ignorados; la automatización inicia sends completos nuevos.
- Solución: usa
zfs receive -sy reanudación consciente de tokens; alerta cuando un receive_resume_token exista más tiempo del umbral; evita “fallback silencioso a full.”
La replicación parece bien, pero la jerarquía de datasets en DR es incorrecta
- Síntoma: faltan algunos hijos; mountpoints extraños; cuotas/reservas ausentes.
- Causa raíz: el script de replicación no usó modo recursivo; datasets creados después nunca se añadieron; propiedades no incluidas.
- Solución: replica recursivamente con exclusiones explícitas; audita la deriva del listado de datasets; aplica una jerarquía esperada usando un archivo de “estado deseado”.
Listas de verificación / plan paso a paso
Checklist semanal (15–30 minutos)
- Salud del pool: comprueba
zpool status -xen emisor y receptor; cualquier estado no sano es prioridad. - Chequeo RPO: compara marcas de tiempo de las instantáneas más recientes en ambos lados para datasets de mayor prioridad (bases de datos, homes, stores de objetos).
- Chequeo de alcance: verifica que el árbol de datasets coincida con lo que crees proteger; busca hijos nuevos no cubiertos.
- Chequeo de retención: compara conteos de instantáneas y asegura que las instantáneas base necesarias para incrementales existan en el receptor.
- Chequeo de resume token: asegúrate de que no exista ningún
receive_resume_tokende larga vida sin acción. - Chequeo de capacidad: verifica espacio libre del receptor frente a tu delta peor caso y margen de seguridad.
Checklist mensual (1–2 horas)
- Diff de propiedades: muestrea datasets críticos y compara propiedades que afectan comportamiento en runtime (recordsize, compression, atime, sync, logbias, cuotas).
- Revisión de scrub: confirma que el scrub del receptor completó sin errores; investiga inmediatamente cualquier checksum o error de lectura.
- Dimensionado de deltas: ejecuta
zfs send -nPvpara algunos incrementales representativos y registra tamaños; vigila cambios por saltos. - Inyección de fallos: interrumpe intencionalmente una replicación y confirma que la lógica de resume token funciona.
Drill trimestral de restauración (medio día, pero paga el alquiler)
- Seleccionar un dataset: elige uno de alto valor (BD o compartición de archivos clave) y uno “amplio” (muchos archivos pequeños).
- Clonar la última instantánea en DR: móntala en una ruta controlada.
- Validación a nivel de aplicación: arranca un servicio mínimo o realiza una comprobación de integridad que se asemeje a la realidad.
- Medir tiempo: registra cuánto tardó cada paso; eso es evidencia de tu RTO.
- Destruir clones: limpia; asegúrate de que el drill no infle el uso de espacio por semanas.
- Actualizar runbook: si alguien tuvo que “saberlo”, pasa al procedimiento documentado.
Reglas rígidas (las que previenen la revisión de incidente a las 03:00)
- No a envíos completos silenciosos. Si los incrementales fallan y eliges un full, eso es una decisión humana con alerta.
- No replicación sin monitoreo de RPO. “Job tuvo éxito” es una métrica vacía por sí sola.
- No cifrado sin procedimientos de clave en DR. “Seguro pero irrecuperable” es una forma de pérdida de datos.
- No podas en el receptor que rompan cadenas. Si debes podar, debes preservar las instantáneas base requeridas para incrementales.
Preguntas frecuentes (FAQ)
1) ¿Cuál es la mejor métrica única para la salud de la replicación?
La edad de la instantánea recuperable más nueva en el receptor, por dataset crítico. No “éxito del job”, no throughput.
La edad de la instantánea se mapea directamente al RPO.
2) ¿Es suficiente “la instantánea existe en DR”?
No. Necesitas probar que puedes usar esa instantánea: clonarla, montarla, cargar claves si está cifrada y validar datos a nivel de aplicación.
3) ¿El dataset de DR debe montarse automáticamente?
Usualmente no. La práctica común es canmount=noauto o mountpoint=none en DR para evitar uso accidental.
Pero debes documentar exactamente cómo montar en DR y probarlo.
4) ¿Puedo replicar datasets cifrados de forma segura?
Sí, pero “seguro” incluye manejo de claves. Decide si en DR deben tener claves cargadas por defecto. Muchos entornos mantienen claves no disponibles
y usan un proceso break-glass. En cualquier caso, prueba la carga de claves y el montaje.
5) ¿Por qué a veces los incrementales se inflan en tamaño?
Causas comunes: churn grande (bases de datos, imágenes VM), errores de retención (mantener instantáneas antiguas fija bloques cambiados),
o un cambio de workload (por ejemplo, un nuevo directorio de cache). Usa zfs send -nPv para medir antes de enviar.
6) ¿Qué rompe más a menudo la replicación incremental?
Instantáneas comunes faltantes. Normalmente causado por poda en el receptor, borrado manual de instantáneas o políticas/nombres desajustados.
Protege la ventana de instantáneas base.
7) ¿Son necesarios los scrubs en el pool DR?
Si te importan los datos, sí. La replicación puede copiar corrupción fielmente si ocurrió antes de la instantánea, y los discos pueden degradarse en silencio.
Los resultados de scrub son evidencia de que la réplica puede leerse.
8) ¿Cómo sé si el receptor es el cuello de botella?
Comprueba IO del pool con zpool iostat -v durante el receive, y observa latencia (vía tus métricas OS).
Si la red no está llena pero las escrituras son lentas y los discos saturados, el receptor es tu limitador.
9) ¿Está bien comprimir el stream de envío?
Los streams ZFS ya reflejan la compresión del dataset; la compresión externa adicional rara vez ayuda y puede consumir CPU.
Mide. Si el CPU en emisor/receptor está saturado, fallarás el RPO incluso con un enlace gordo.
10) ¿Debo replicar propiedades?
Replica lo que importa para corrección y rendimiento. Luego audítalo.
Si deliberadamente diferencias en DR (como canmount), haz esa divergencia explícita para que no derive accidentalmente.
Próximos pasos que puedes hacer esta semana
-
Implementa una comprobación de RPO por dataset. Scriptealo si es necesario: compara tiempos de creación de la instantánea más nueva en emisor vs receptor.
Alerta cuando exceda la política. -
Haz un drill de restauración. Elige un dataset, clona la instantánea más nueva en DR, móntala y valida a nivel de aplicación.
Mide el tiempo. Anota cada paso que tuviste que “recordar”. -
Audita la deriva del alcance. Enumera datasets recursivamente en emisor y confirma que existen en el receptor.
Si descubres hijos faltantes, trátalo como un incidente real. -
Haz de los resume tokens algo de primera clase. Si tienes enlaces poco fiables, no manejar tokens es elegir dolor.
Alerta sobre tokens de larga vida y construye un flujo de reanudación seguro. -
Alinea la retención. Asegúrate de que la poda de instantáneas en el receptor no pueda eliminar instantáneas necesarias como bases incrementales.
Usa holds cuando proceda y haz socialmente inaceptable los “envíos completos silenciosos”. - Decide cómo funciona el cifrado en DR. ¿Quién puede cargar claves? ¿Cómo? ¿Bajo qué aprobación? Practícalo.
El objetivo no es tener replicación que “corre”. El objetivo es tener replicación que puedas probar que es recuperable,
y una ruta de restauración que funcione cuando estés cansado, estresado y observando.
Eso es lo que “la réplica que creías tener” debería ser: no una creencia, sino una capacidad demostrada.