Los incidentes más costosos en ZFS rara vez comienzan con fallos de disco. Empiezan con confianza.
Alguien cambia un dataset “solo para arreglar esto”, y una semana después la mitad del parque está
más lento, los trabajos de backup explotan o falta un montaje al arrancar. Nada parece “roto” de forma
obvia. ZFS sigue sano. El pool está ONLINE. Y sin embargo el sistema se comporta de forma diferente.
El culpable suele ser la herencia de propiedades de ZFS: la funcionalidad que adoras cuando escalas
limpiamente—y la funcionalidad que hace que un dataset hijo cambie silenciosamente cuando no lo pretendías.
Si gestionas producción, trata la herencia como tablas de enrutamiento: potente, de impacto global,
y no algo que “simplemente pruebas”.
La herencia es la característica y la trampa
Los datasets de ZFS (filesystems y volúmenes) viven en un árbol. Las propiedades residen en nodos de ese árbol.
Algunas propiedades se heredan hacia abajo en el árbol a menos que sean anuladas. Eso es intencional: te permite
aplicar una política en un dataset padre y que todos los hijos la sigan.
La trampa es humana: nosotros pensamos en “este dataset” y ZFS piensa en “este subárbol”. Ajustas una
propiedad en tank/apps para resolver un problema de una aplicación, y acabas de cambiar el comportamiento de
tank/apps/postgres, tank/apps/redis y del olvidado tank/apps/tmp que un runner de CI ha estado usando durante dos años.
La herencia no es solo sobre “buenos valores por defecto”. Puede cambiar el comportamiento de montaje, el rendimiento, la visibilidad de snapshots,
la semántica de acceso, el manejo de claves de cifrado y las garantías de espacio. Eso significa que la herencia puede:
- arreglar cosas rápidamente cuando se usa de forma deliberada,
- provocar incidentes de “nadie tocó ese dataset” cuando se usa de manera casual,
- crear deriva de configuración que resiste la depuración (“¿se hereda desde dónde?”).
Una regla operativa que me gusta: si no puedes nombrar los datasets padre que se verán impactados por tu cambio,
no estás listo para ejecutar zfs set.
Broma #1: La herencia de ZFS es como el Wi‑Fi de la oficina: todos se benefician hasta que alguien “lo optimiza” y todo el piso
empieza a hacer buffering.
El único modelo mental que escala: local vs inherited vs default
Cuando consultas una propiedad, ZFS puede mostrar el valor y la fuente. La fuente es lo que te salva:
- local: establecido explícitamente en ese dataset
- inherited from <dataset>: heredado de un ancestro
- default: valor por defecto compilado para tu implementación/version de ZFS
- temporary: establecido en tiempo de ejecución (menos común; piensa en opciones de montaje en algunas pilas)
Aquí está por qué esto importa en producción: dos datasets pueden mostrar el mismo valor pero comportarse diferente
operativamente porque la fuente te dice qué ocurrirá en la próxima ventana de cambios.
Un valor por defecto permanece estable hasta que lo cambias. Un valor heredado puede cambiar cuando alguien modifica el padre.
Un valor local es “pegajoso” pero puede quedarse obsoleto respecto a la política.
Reglas de herencia que importan en la vida real
- La mayoría de las propiedades son heredables; no todas. ZFS tiene propiedades que son de solo lectura o
inherentemente locales (por ejemplo,used,available). - Los hijos heredan a menos que haya una anulación local. Si un hijo tiene un valor local, los cambios del padre
no lo afectarán. zfs inheritelimina la anulación local. No “pone al valor por defecto”. Elimina la
configuración local y el valor pasa a heredarse (o a default, si no hay nada que herede).- Los clones complican la historia. Los clones nacen como hijos de un snapshot de origen; todavía
existen en el árbol de datasets y por tanto heredan de padres como cualquier otro. - Las propiedades relacionadas con montaje pueden fallar de formas que parecen problemas de systemd. No lo son. Son
ZFS, negándose educadamente a montar lo que le dijiste que montara.
Una sola cita que puedes usar con tu equipo de almacenamiento
W. Edwards Deming (idea parafraseada): “Un sistema está perfectamente diseñado para obtener los resultados que obtiene.”
Las sorpresas por herencia son tu sistema funcionando exactamente como fue diseñado.
Propiedades que muerden fuerte (y por qué)
1) mountpoint, canmount, y readonly: el trío “¿dónde se fue mi filesystem?”
Si gestionas servidores, ya habrás depurado “se montó ayer” al menos una vez. El comportamiento de montaje de ZFS está guiado por propiedades.
Un dataset padre con canmount=off puede ser un límite administrativo limpio—hasta que se aplica en el nodo equivocado y los hijos dejan de montarse al arrancar.
mountpoint se hereda por defecto, lo cual es conveniente y peligroso. Si pones el mountpoint del padre en /srv,
podrías esperar que solo el padre se mueva. En su lugar, los hijos se montan bajo él a menos que tengan mountpoints locales. Genial cuando está planeado; caos cuando no.
2) compression: pequeño interruptor, gran radio de impacto
La compresión es heredable. Eso es una característica: puedes activar compression=lz4 en un padre y
beneficiarte ampliamente. La sorpresa: también puedes apagarla para un subárbol y aumentar silenciosamente la capacidad.
Peor aún, puedes cambiarla en un dataset que incluye “base de datos caliente” y “logs fríos” y ahora tu afinación es
un compromiso que no pretendías.
3) recordsize: tuning de rendimiento que se convierte en regresión
recordsize es heredable en filesystems. Ponlo mal en un padre y tus hijos heredan un tamaño de registro que puede ser terrible para su carga.
Un directorio de datos de PostgreSQL normalmente prefiere bloques pequeños para IO aleatorio; un archivo de medios prefiere bloques grandes.
Tu árbol de datasets no debería obligarlos a usar la misma “talla de ropa”.
4) atime: muerte por un millón de escrituras pequeñas
Las actualizaciones de tiempo de acceso (atime=on) pueden generar una escritura en lecturas. En datasets concurridos y con muchas lecturas
(buzones de correo, caches web, árboles con mucho metadata), atime=on heredado puede convertir “las lecturas son baratas”
en “las lecturas también son escrituras”, lo que afecta latencia y desgaste.
5) snapdir y visibilidad de snapshots
snapdir=visible hace que .zfs/snapshot aparezca. A veces es genial para restauraciones self-service,
y otras un riesgo operativo o de cumplimiento cuando aplicaciones lo recorren. La herencia significa que puedes exponer
snapshots en lugares que no pretendías. O esconderlos donde dependías de ellos operativamente.
6) Cuotas, reservaciones, refreservations: “espacio” es política
Las cuotas y reservas pueden heredarse dependiendo del tipo de propiedad y tu diseño. Incluso cuando no se heredan,
su interacción con la asignación padre/hijo es donde vive la sorpresa. La gestión de espacio no es intuitiva bajo presión.
Puedes acabar con espacio libre a nivel de pool pero “no queda espacio” para un dataset por reservas, o con un hijo que crece sin límite
porque asumiste que una cuota en el padre lo cubriría.
7) Propiedades de cifrado: herencia con aristas afiladas
El cifrado nativo de ZFS introduce propiedades heredables como encryption, keylocation,
keyformat y más. El comportamiento central: los hijos pueden heredar configuraciones de cifrado del padre al crearse.
Pero cambiar el cifrado no es como cambiar la compresión; no puedes simplemente activarlo para un dataset existente sin
una migración send/receive. La gente todavía lo intenta. La “sorpresa” aquí suele ser un fallo de proceso: esperar que la herencia
asegure datos retroactivamente.
Broma #2: Cambiar recordsize en el dataset padre es como comprarle a toda la compañía la misma silla—alguien va a abrir un ticket por dolor de espalda.
Datos interesantes y contexto histórico
- ZFS fue diseñado para sustituir una pila, no solo un filesystem. Combinó gestión de volúmenes y semántica de sistema de archivos, así que las “propiedades” se convirtieron en perillas de política del sistema.
- Las propiedades se construyeron para delegación. Despliegues tempranos de ZFS necesitaban una forma de permitir que los equipos se autogestionaran datasets de forma segura; la herencia fue el mecanismo de escala.
- Las elecciones por defecto de compresión evolucionaron. Muchas pilas modernas recomiendan
lz4ampliamente; las guías antiguas eran más cautelosas porque la CPU era más escasa y los algoritmos diferían. recordsizevino de un mundo de IO secuencial grande. ZFS optimizó para cargas de streaming; las bases de datos obligaron a la comunidad a disciplinarse sobre el tuning por dataset.- Los valores por defecto de
atimereflejan la tradición Unix, no la realidad de rendimiento. Los tiempos de acceso eran útiles para herramientas y políticas, pero los sistemas modernos a menudo pagan demasiado por ellos. - El comportamiento de montaje solía ser más “mágico”. Integraciones OS diferentes (herencia Solaris vs ZFS en Linux) moldearon cómo las propiedades interactúan con el arranque y los servicios de montaje.
- La visibilidad de snapshots es una guerra cultural. Los admins aman
.zfs/snapshotpara restauraciones; los dueños de apps odian directorios sorpresa. La propiedad existe porque ambos tienen razón. - Las fuentes de propiedad fueron un regalo de depuración agregado temprano. “local vs inherited vs default” es una de las mejores decisiones UX orientadas al operador en herramientas de almacenamiento.
Tareas prácticas (comandos, salidas, decisiones)
Estos son los chequeos que ejecuto cuando alguien dice “ZFS cambió algo”. Cada tarea incluye: el comando,
qué significa la salida y la decisión que tomas a continuación.
Task 1: Ver el valor de la propiedad y de dónde viene
cr0x@server:~$ zfs get -o name,property,value,source compression tank/apps/postgres
NAME PROPERTY VALUE SOURCE
tank/apps/postgres compression lz4 inherited from tank/apps
Significado: El dataset Postgres está comprimido porque su padre lo está. Si cambias tank/apps,
cambias Postgres. Decisión: si Postgres necesita un comportamiento especial, establece una anulación local en
tank/apps/postgres y deja de depender del padre para esa propiedad.
Task 2: Auditar un subárbol por una propiedad (captura deriva rápido)
cr0x@server:~$ zfs get -r -o name,property,value,source recordsize tank/apps
NAME PROPERTY VALUE SOURCE
tank/apps recordsize 128K local
tank/apps/postgres recordsize 128K inherited from tank/apps
tank/apps/redis recordsize 128K inherited from tank/apps
tank/apps/artifacts recordsize 128K inherited from tank/apps
Significado: Una configuración local en tank/apps dicta todo el subárbol.
Decisión: acepta eso como política, o separa datasets especiales con tamaños de record locales
(por ejemplo, Postgres a 16K o 8K dependiendo de tu almacenamiento y carga).
Task 3: Encontrar todo lo que hereda de un padre específico (radio de impacto)
cr0x@server:~$ zfs get -r -H -o name,source mountpoint tank/apps | awk '$2 ~ /tank\/apps/ {print $1, $2}'
tank/apps/postgres inherited from tank/apps
tank/apps/redis inherited from tank/apps
tank/apps/artifacts inherited from tank/apps
Significado: Esos datasets se moverán si cambias el mountpoint de tank/apps.
Decisión: no toques mountpoint en el padre a menos que tengas una ventana de mantenimiento
y un plan de rollback.
Task 4: Verificar qué se montará y qué no
cr0x@server:~$ zfs get -r -o name,canmount,mountpoint,mounted tank/apps
NAME CANMOUNT MOUNTPOINT MOUNTED
tank/apps on /srv/apps yes
tank/apps/postgres on /srv/apps/postgres yes
tank/apps/redis on /srv/apps/redis yes
Significado: Todos los datasets son elegibles para montarse y están montados.
Decisión: si falta algo, busca canmount=off o mountpoint=none
y traza de dónde se hereda.
Task 5: Detectar “cambiamos el mountpoint del padre” antes de que el reboot lo haga
cr0x@server:~$ zfs set mountpoint=/srv tank/apps
cr0x@server:~$ zfs mount -a
cannot mount 'tank/apps/postgres': mountpoint or dataset is busy
Significado: El nuevo layout de mounts entra en conflicto con montajes existentes o procesos en ejecución.
Decisión: revierte el mountpoint del padre inmediatamente, o detén servicios limpiamente y vuelve a montar
en una ventana controlada. No “forces” en caliente.
Task 6: Identificar anulaciones locales (los lugares donde la política no llega)
cr0x@server:~$ zfs get -r -H -o name,property,value,source compression tank | awk '$4=="local"{print}'
tank/apps compression lz4 local
tank/backups compression off local
Significado: Solo dos datasets tienen configuraciones explícitas de compresión; todo lo demás es heredado o por defecto.
Decisión: confirma que estas anulaciones locales son intencionales. Las anulaciones locales son donde las “normas”
se detienen silenciosamente.
Task 7: Restablecer un dataset hijo para que herede de nuevo (despersonalización controlada)
cr0x@server:~$ zfs get -o name,property,value,source atime tank/apps/postgres
NAME PROPERTY VALUE SOURCE
tank/apps/postgres atime off local
cr0x@server:~$ zfs inherit atime tank/apps/postgres
cr0x@server:~$ zfs get -o name,property,value,source atime tank/apps/postgres
NAME PROPERTY VALUE SOURCE
tank/apps/postgres atime on inherited from tank/apps
Significado: Eliminaste la anulación local, y ahora Postgres sigue al padre.
Decisión: haz esto solo si estás seguro de que la política del padre es correcta para la carga. “Inherit”
no es un botón de limpieza gratuito.
Task 8: Ver qué propiedades están establecidas localmente en un dataset (perfil rápido)
cr0x@server:~$ zfs get -s local all tank/apps/postgres | head
NAME PROPERTY VALUE SOURCE
tank/apps/postgres atime off local
tank/apps/postgres logbias latency local
tank/apps/postgres primarycache metadata local
tank/apps/postgres recordsize 16K local
Significado: Estas son desviaciones deliberadas de la política heredada/por defecto.
Decisión: si no esperabas esto, tienes deriva de configuración. Rastrea quién lo puso y por qué.
Si lo esperabas, documéntalo porque alguien intentará “limpiarlo” más tarde.
Task 9: Usar zfs diff para entender cambios visibles por snapshot (chequeo de la realidad ops)
cr0x@server:~$ zfs diff tank/apps/postgres@before-change tank/apps/postgres@after-change | head
M /srv/apps/postgres/postgresql.conf
+ /srv/apps/postgres/pg_wal/0000000100000000000000A1
- /srv/apps/postgres/tmp/old.sock
Significado: Esto es rastreo a nivel de archivo entre snapshots, no cambios de propiedad.
Decisión: si cambió el rendimiento pero los datos no, probablemente estás viendo herencia de propiedades o
comportamiento del kernel/módulo—no “alguien editó archivos”.
Task 10: Confirmar el layout de datasets y detectar hijos que no deberían existir
cr0x@server:~$ zfs list -r -o name,used,avail,refer,mountpoint tank/apps
NAME USED AVAIL REFER MOUNTPOINT
tank/apps 120G 2.1T 256K /srv/apps
tank/apps/postgres 80G 2.1T 80G /srv/apps/postgres
tank/apps/redis 2G 2.1T 2G /srv/apps/redis
tank/apps/tmp 35G 2.1T 35G /srv/apps/tmp
Significado: Un dataset tmp pesado dentro del subárbol apps es un peligro clásico de herencia.
Decisión: mueve cargas efímeras a su propio dominio de política padre (o establece anulaciones locales),
especialmente para recordsize, sync y atime.
Task 11: Comprobar si cuotas o reservas están limitando silenciosamente a los hijos
cr0x@server:~$ zfs get -r -o name,quota,reservation,refreservation,used,available tank/apps/postgres
NAME QUOTA RESERVATION REFRESERVATION USED AVAILABLE
tank/apps/postgres none 100G none 80G 20G
Significado: Reservaste 100G; el dataset tiene solo 20G “disponibles” incluso si el pool tiene más.
Decisión: si Postgres se acerca a 100G y no querías presión dura de espacio, ajusta o quita la reserva. Si lo querías, alerta sobre ello—las reservas son política, no trivialidad.
Task 12: Validar compresión y espacio lógico (¿está ayudando o perjudicando?)
cr0x@server:~$ zfs get -o name,compressratio,used,logicalused compression,compressratio tank/apps/artifacts
NAME PROPERTY VALUE SOURCE
tank/apps/artifacts compression lz4 inherited from tank/apps
tank/apps/artifacts compressratio 1.72x -
Significado: La compresión está activa y es efectiva. Si compressratio está ~1.00x, estás gastando
CPU con poco beneficio. Decisión: mantén la compresión para la mayoría de cargas mixtas; apágala solo con
evidencia, no por intuición.
Task 13: Detectar colisiones de mountpoint (modo común de fallo por herencia)
cr0x@server:~$ zfs get -r -H -o name,value mountpoint tank | sort -k2 | awk 'prev==$2{print "collision:", prev, "between", prevname, "and", $1} {prev=$2; prevname=$1}'
collision: /srv/apps between tank/apps and tank/apps/legacy
Significado: Dos datasets comparten el mismo mountpoint. ZFS no montará felizmente ambos.
Decisión: arregla mountpoints antes del día de reboot. Las colisiones suelen aparecer como “montajes faltantes aleatorios”
dependiendo del orden de montaje.
Task 14: Confirmar qué propiedades son heredables (sabe qué juego estás jugando)
cr0x@server:~$ zfs get -H -o property,values,source all tank/apps | head -n 8
type filesystem -
creation Wed Nov 13 10:12 2024 -
used 120G -
available 2.1T -
referenced 256K -
compressratio 1.23x -
mounted yes -
quota none default
Significado: Algunas propiedades muestran una fuente significativa; otras son dinámicas y no lo hacen.
Decisión: enfoca las auditorías de herencia en propiedades de comportamiento (compression, recordsize, atime,
mountpoint, canmount, snapdir, sync, logbias, primarycache/secondarycache, xattr, acltype, propiedades relacionadas con encryption).
Guion de diagnóstico rápido
Este es el flujo de triaje “está lento / no se monta / se quedó sin espacio” que te lleva al culpable de herencia
rápidamente. Ejecútalo como una lista de verificación. La velocidad importa; la corrección importa más.
Primero: define el alcance (¿un dataset o un subárbol?)
- Identifica el dataset(s) que respaldan la ruta: comprueba el mapeo de mountpoints y
zfs list. - Determina si los hermanos también están afectados (la herencia a menudo golpea a los hermanos).
- Pregunta “¿qué padre unificaría estos datasets?” Ahí es donde probablemente ocurrió el cambio.
Segundo: revisa las fuentes de propiedades para los sospechosos principales
- Problemas de montaje:
mountpoint,canmount,readonly,overlay(si aplica), y colisiones. - Regresión de rendimiento:
recordsize,compression,atime,sync,logbias,primarycache,secondarycache. - Sorpresas de espacio:
quota,refquota,reservation,refreservation.
Tercero: confirma que el cambio es política, no hardware
- Salud del pool:
zpool statusdebe estar limpio; si no, estás depurando el problema equivocado. - Diff del árbol de datasets: compara fuentes de propiedades entre datasets “buenos” y “malos”.
- Busca actividad administrativa reciente: historial de shell, notas de gestión de cambios, commits de automatización.
Cuarto: arregla con el menor radio de impacto
- Si un único hijo necesita un comportamiento diferente, establece una anulación local en el hijo.
- Si la política es incorrecta para muchos hijos, cambia el padre—pero solo después de auditar el subárbol.
- Si no puedes decidir con seguridad, restaura valores previos (revertir el cambio de propiedad) y revisa con datos.
Tres mini-historias corporativas desde las minas de herencia
Mini-historia 1: El incidente causado por una suposición equivocada
Una empresa SaaS de tamaño medio tenía un layout de ZFS ordenado: tank/apps para estado de aplicaciones, con hijos
para Postgres, Redis y un blob store. Un nuevo SRE recibió la tarea de “hacer que los backups sean más fáciles de encontrar” en un host
usado por varios equipos. Notó que el dataset padre estaba montado en /srv/apps y quiso que todo estuviera bajo /srv para “consistencia”.
Ejecutó un único comando: puso mountpoint=/srv en tank/apps. El sistema no explotó
de inmediato porque la mayoría de los montajes ya estaban activos. El cambio quedó allí como una piel de plátano.
Esa noche, un reboot rutinario por parcheo del kernel provocó remounts. Algunos datasets fallaron al montarse por
targets “busy”, otros colisionaron con directorios existentes, y unidades de systemd corrieron asumiendo que las rutas existían.
El ticket del incidente parecía una antología de horror: “Postgres no arranca”, “Redis datos faltantes”, “la aplicación
no encuentra uploads”. El pool estaba sano. El IO de disco se veía bien. El problema era que las rutas no estaban donde los servicios
las esperaban.
La solución fue sencilla pero humillante: revertir el mountpoint del padre, establecer mountpoints locales solo donde hacía falta,
y añadir una comprobación pre-reboot que compare los mountpoints actuales contra un inventario aprobado. La lección real
no fue “no toques mountpoint”. Fue “no asumas que las propiedades de dataset se comportan como configuraciones por directorio.”
La línea más afilada del postmortem: nadie revisó el radio de impacto. El comando era correcto. La suposición no lo fue.
Mini-historia 2: La optimización que salió mal
Otra organización almacenaba artefactos de build y capas de contenedores en ZFS. Luchaban por capacidad: el pool iba
acercándose a un uso incómodo. Alguien propuso activar la compresión en la raíz:
zfs set compression=lz4 tank. Es una recomendación común. También no es gratis.
El despliegue se hizo vía automatización. En horas, la monitorización mostró CPU en aumento en un subconjunto de nodos.
La latencia en una API concurrida también subió. El equipo supuso que el cambio en la API no estaba relacionado—hasta que correlacionaron los nodos
con el layout de datasets. La API tenía su dataset bajo tank también, pero nadie pensó que “compartiera”
algo con los artefactos de build.
La compresión no era la villana; era la interacción. Esa API manejaba muchos payloads pequeños ya comprimidos.
Los ratios de compresión rondaban 1.00x. Mientras tanto, los ciclos CPU extra competían con hilos de aplicación en horas punta.
En papel era “sobrecosto menor”. En realidad, fue sobrecosto en el lugar equivocado en el momento equivocado.
Se recuperaron poniendo compression=off localmente en el dataset de la API y dejándola encendida en otros lugares.
El fallo no fue que la compresión sea mala. Fue tratar el árbol de datasets como un archivador estático en vez de un dominio de rendimiento compartido.
La mejora a largo plazo fue política: herencia de alto nivel solo para propiedades seguras y ampliamente beneficiosas,
y datasets “excepción” explícitos documentados y probados. La compresión quedó para la mayoría. La API obtuvo sus propias reglas.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Una compañía del ámbito financiero usaba ZFS para servicios multi-tenant internos. Su lead de storage insistía en un ritual aburrido:
cada trimestre, ejecutar una auditoría recursiva de propiedades y subir la salida al repo de infraestructura como artifacto.
No como diagrama. La salida cruda de zfs get -r. Todo el mundo se quejaba de que era burocrático.
Entonces un nuevo despliegue de plataforma introdujo un cambio sutil: un dataset padre quedó con atime=on localmente debido a
una jugada equivocada de “restaurar valores por defecto”. Nadie lo notó durante desarrollo porque las cargas de prueba eran pequeñas.
En producción, servicios con muchas lecturas empezaron a generar escrituras extras. La latencia subió. La amplificación de escritura NVMe
aumentó. Nada catastrófico, solo ese burn lento y costoso.
El ingeniero on-call hizo las comprobaciones habituales. Pool sano. Sin errores. Los patrones de IO parecían “ocupados” pero no fallidos.
Entonces sacó la última instantánea trimestral de propiedades y la comparó con el estado actual. La diferencia saltó:
la fuente de atime cambió en un padre. Eso redujo inmediatamente el alcance a un subárbol y a una sola propiedad.
Solución: restaurar atime=off en el padre correcto, confirmar que los hijos lo heredaron, y añadir un guardrail en
la automatización para prevenir reactivaciones accidentales. Downtime: ninguno. Tiempo de debugging: corto. La práctica aburrida se pagó sola
en un incidente.
La moraleja es poco sexy: los inventarios de propiedades no son papeleo; son máquinas del tiempo. Cuando depuras,
una línea base conocida es un arma.
Errores comunes: síntomas → causa raíz → arreglo
1) “El dataset hijo cambió y nadie lo tocó”
Síntomas: cambios de comportamiento (rendimiento, rutas de montaje, visibilidad de snapshots), pero el dataset hijo no muestra acciones administrativas recientes.
Causa raíz: propiedad heredada de un padre que fue modificada.
Arreglo: ejecuta zfs get -o name,property,value,source en el hijo y traza la fuente. O bien revierte el cambio en el padre o establece una anulación local en el hijo.
2) “Después del reboot, faltan algunos datasets”
Síntomas: servicios fallan al arrancar, mountpoints vacíos, datasets ZFS muestran mounted=no.
Causa raíz: cambio heredado en mountpoint, colisiones de mountpoint, o canmount=off establecido en un padre.
Arreglo: revisa zfs get -r canmount,mountpoint,mounted. Elimina colisiones; establece mountpoints locales en hijos que necesiten rutas únicas; asegúrate de que los padres usados como contenedores tengan canmount=off solo cuando sea intencional.
3) “Regresión de rendimiento después de un tuning ‘inofensivo’”
Síntomas: mayor latencia, más IO, aumento de CPU; sin errores de pool.
Causa raíz: recordsize, atime o compression heredados aplicados a cargas que los detestan.
Arreglo: audita las fuentes de propiedades en el subárbol. Aplica anulaciones locales por carga de trabajo. Evita afinar en padres de alto nivel a menos que puedas justificarlo para cada hijo.
4) “No queda espacio, pero el pool tiene espacio”
Síntomas: la aplicación ve ENOSPC; zpool list muestra espacio libre.
Causa raíz: límites de quota/refquota/reservation/refreservation; a veces expectativas heredadas más que propiedades heredadas.
Arreglo: inspecciona zfs get quota,refquota,reservation,refreservation,available. Ajusta límites y alerta sobre “available” a nivel de dataset, no solo espacio libre del pool.
5) “Snapshots de repente visibles (o desaparecieron)”
Síntomas: aplicaciones recorren directorios .zfs, herramientas de backup se comportan raro, equipos de cumplimiento preguntan.
Causa raíz: snapdir heredado cambiado en un padre.
Arreglo: establece snapdir=hidden o snapdir=visible deliberadamente en el límite de política correcto; verifica recursivamente con zfs get -r snapdir.
6) “Activamos cifrado en el padre; ¿por qué los hijos existentes no están cifrados?”
Síntomas: datasets nuevos están cifrados, los antiguos no; la auditoría lo señala.
Causa raíz: las propiedades de cifrado influyen en la creación e herencia de datasets, pero no puedes cifrar datasets existentes in-place de forma retroactiva.
Arreglo: planifica una migración send/receive a un nuevo árbol cifrado; trata la “herencia” como una plantilla para datasets nuevos, no como una máquina del tiempo para datos viejos.
Listas de verificación / plan paso a paso
Plan A: Cambiar con seguridad una propiedad en un dataset padre
-
Identifica el subárbol. Lista hijos y mountpoints.
cr0x@server:~$ zfs list -r -o name,mountpoint tank/apps NAME MOUNTPOINT tank/apps /srv/apps tank/apps/postgres /srv/apps/postgres tank/apps/redis /srv/apps/redisDecisión: si no reconoces cada dataset, para. Los hijos desconocidos son donde se esconden las sorpresas.
-
Audita valores y fuentes de propiedades actuales recursivamente.
cr0x@server:~$ zfs get -r -o name,property,value,source compression,recordsize,atime,sync tank/apps NAME PROPERTY VALUE SOURCE tank/apps compression lz4 local tank/apps recordsize 128K local tank/apps atime on default tank/apps sync standard default tank/apps/postgres compression lz4 inherited from tank/apps tank/apps/postgres recordsize 16K local tank/apps/postgres atime on inherited from tank/apps tank/apps/postgres sync standard inherited from tank/appsDecisión: si hijos importantes tienen anulaciones locales, confirma que sigan correctas después del cambio en el padre.
-
Modela el impacto. Lista qué datasets heredan actualmente esa propiedad del padre.
cr0x@server:~$ zfs get -r -H -o name,source atime tank/apps | awk '$2 ~ /tank\/apps/ {print $1}' tank/apps/postgres tank/apps/redisDecisión: si la lista impactada contiene “workloads especiales”, considera anulaciones locales en lugar de cambiar el padre.
-
Haz el cambio en una ventana controlada (o al menos en condiciones controladas).
cr0x@server:~$ zfs set atime=off tank/appsDecisión: si esto está relacionado con mounts, planifica downtime; los cambios de mountpoint son ruidosos operativamente.
-
Verifica recursivamente y vigila outliers.
cr0x@server:~$ zfs get -r -o name,property,value,source atime tank/apps NAME PROPERTY VALUE SOURCE tank/apps atime off local tank/apps/postgres atime off inherited from tank/apps tank/apps/redis atime off inherited from tank/appsDecisión: si algún dataset siguió con
atime=on, tiene una anulación local; decide si eso es correcto.
Plan B: Hacer a un hijo inmune a futuros cambios del padre
-
Identifica qué propiedades deben fijarse localmente. Típico:
recordsize,logbias,primarycache,syncpara bases de datos (con cuidado), omountpoint. -
Establece anulaciones locales explícitas y documenta el motivo.
cr0x@server:~$ zfs set recordsize=16K tank/apps/postgres cr0x@server:~$ zfs set primarycache=metadata tank/apps/postgres cr0x@server:~$ zfs get -o name,property,value,source recordsize,primarycache tank/apps/postgres NAME PROPERTY VALUE SOURCE tank/apps/postgres recordsize 16K local tank/apps/postgres primarycache metadata localDecisión: las anulaciones locales son un contrato. Si no puedes justificarlas, solo estarás complicando el futuro.
Plan C: Prevenir sorpresas con una línea base de propiedades
-
Captura una instantánea recursiva de propiedades para datasets clave.
cr0x@server:~$ zfs get -r -o name,property,value,source all tank/apps > /var/tmp/zfs-props-tank-apps.txt cr0x@server:~$ wc -l /var/tmp/zfs-props-tank-apps.txt 842 /var/tmp/zfs-props-tank-apps.txtDecisión: almacénala en un lugar durable (artifacto del repo, store de gestión de configuración). Si vive solo en el host, morirá con el host.
-
Diff antes/después de los cambios.
cr0x@server:~$ diff -u /var/tmp/zfs-props-tank-apps-before.txt /var/tmp/zfs-props-tank-apps-after.txt | head --- /var/tmp/zfs-props-tank-apps-before.txt +++ /var/tmp/zfs-props-tank-apps-after.txt @@ -tank/apps atime on default +tank/apps atime off localDecisión: si el diff es mayor de lo esperado, para y reevalúa. Los diffs grandes son donde vienen los outages.
Preguntas frecuentes
1) ¿Cómo sé si una propiedad de dataset es heredada?
Usa zfs get y mira la columna source. Si dice “inherited from …”, esa es tu respuesta.
2) Si establezco una propiedad en un padre, ¿siempre afecta a todos los hijos?
Afecta a los hijos que no tienen una anulación local para esa propiedad, y solo a las propiedades que son heredables.
Los hijos con valores locales quedan aislados.
3) ¿Cuál es la diferencia entre zfs inherit y establecer una propiedad a su valor por defecto?
zfs inherit elimina la configuración local, causando que el dataset herede de su padre (o vuelva al default si ningún padre lo define).
Establecer un valor explícitamente lo hace local, incluso si coincide con el default.
4) ¿Por qué cambiar mountpoint rompió cosas aunque es “solo una ruta”?
Porque las rutas son contratos con servicios, configs y el orden de arranque. La herencia puede desplazar el layout de mounts de todo un subárbol.
No es solo renombrar; es un cambio de topología.
5) ¿Es seguro activar compression=lz4 en el dataset superior del pool?
A menudo sí, pero “seguro” depende de la carga y la holgura de CPU. Audita ratios de compresión y uso de CPU.
Si un dataset almacena mayormente datos ya comprimidos, configura una excepción local.
6) ¿Debo afinar recordsize en un padre de alto nivel?
Usualmente no. Usa recordsize a nivel padre solo cuando los hijos son homogéneos.
Para cargas mixtas, fija recordsize localmente por dataset que represente una carga de trabajo.
7) ¿Puedo cifrar retroactivamente datasets existentes poniendo propiedades de cifrado en el padre?
No. Normalmente necesitas una migración send/receive a un nuevo dataset cifrado.
Las propiedades de cifrado del padre guían la creación/herencia de datasets nuevos, no la conversión in-place de datos viejos.
8) ¿Cómo encuentro qué dataset cubre un directorio cuando los montajes confunden?
Revisa la tabla de mounts de ZFS con zfs mount y compara mountpoints, o usa las herramientas de montaje del SO.
Luego consulta propiedades en ese dataset específicamente.
9) ¿Por qué dos datasets muestran el mismo valor de propiedad pero se comportan diferente después?
Mismo valor, distinta fuente. Un valor de default no cambiará a menos que lo cambies.
Un valor heredado de un padre puede cambiar cada vez que el padre cambia.
10) ¿Cómo mantengo la herencia pero evito sorpresas?
Coloca límites de política donde están los límites organizacionales: un padre por clase de workload.
Luego ejecuta auditorías recursivas y guarda una línea base para poder demostrar qué cambió.
Conclusión: próximos pasos que puedes hacer
La herencia de propiedades de ZFS no es un detalle académico. Es un plano de control de producción. Puede evitarte la proliferación de configs,
y puede dejarte fuera de combate cuando un “cambio pequeño” resulta ser un desplazamiento de política en todo un subárbol.
Haz esto a continuación:
- Elige tres árboles de datasets críticos (bases de datos, backups y lo que llene discos) y ejecuta una auditoría recursiva de propiedades. Guárdala.
- Marca los datasets de workload explícitamente y establece anulaciones locales donde la carga realmente difiera (especialmente
recordsize,atime, propiedades de montaje). - Adopta el hábito del radio de impacto: antes de cambiar un padre, lista todos los hijos que heredan esa propiedad y toma una decisión consciente.
- Operationaliza la detección de deriva: haz diff de tu línea base guardada contra las propiedades actuales después de cada ventana de cambios.
Si solo recuerdas una cosa: nunca mires el valor de una propiedad ZFS sin mirar su fuente.
Ahí es donde se esconde la sorpresa.