Programación de scrub de ZFS: cómo evitar dolores en horas pico

¿Te fue útil?

Programas un scrub porque eres responsable. ZFS hace lo que le pediste porque es obediente.
Entonces llega el lunes por la mañana y tu bonita gráfica de latencia estable se convierte en un peine: el almacenamiento de las VM se bloquea,
las consultas de la base de datos se ralentizan y alguien dice las palabras “problema de red” con cara seria.

El scrub no “rompió” nada. Simplemente te mostró—con estruendo—qué tan estrechos eran tus márgenes de rendimiento
y cuánto control tenías sobre la I/O en segundo plano. Este es el manual para ejecutar scrubs en producción
sin convertir tus horas más ocupadas en un ejercicio de fuego real.

Qué hace realmente un scrub (y por qué duele)

Un scrub de ZFS recorre el pool y verifica la integridad de los datos leyendo bloques y validando checksums.
Si existe redundancia y ZFS detecta un bloque malo, lo repara leyendo una copia buena y reescribiendo
el bloque defectuoso. Scrub no es un “escaneo de sistema de archivos” en el sentido antiguo; es una auditoría sistemática de los datos almacenados.
Esa auditoría tiene un costo: lecturas sostenidas, algunas escrituras y mucha presión sobre las colas de los dispositivos.

El dolor viene por la contención. Tu carga de producción quiere I/O aleatorio de baja latencia. El scrub quiere
lecturas con alto rendimiento y algo secuenciales (pero no perfectamente secuenciales: metaslabs, fragmentación y
el tamaño de registro lo complican). Ambos golpean los mismos vdevs, comparten las mismas colas y luchan por
el margen. Si tienes HDDs, espera que el scrub aumente el tiempo medio de búsqueda. Si tienes SSDs, el scrub aún puede comerse el presupuesto de IOPS y el ancho de banda del controlador, y puede agravar la recolección de basura.

Una cosa más: scrub compite con resilver, y resilver no es opcional. Scrub es cirugía electiva; resilver es la
ambulancia. Si programas scrubs tan agresivamente que los resilvers están constantemente “lentos pero constantes”,
estás expandiendo tu ventana de riesgo. Un resilver lento no es solo molesto.
Es tiempo expuesto a una segunda falla.

“Pero ZFS es copy-on-write, así que no debería interferir mucho.” Esa es una frase reconfortante, no un plan.
CoW cambia el comportamiento de escritura y la semántica de consistencia; no te da carriles de I/O infinitos.

Broma #1: Un scrub durante horas pico es como pasar la aspiradora durante una Zoom—técnicamente productivo, socialmente catastrófico.

Scrub vs. resilver vs. pruebas SMART largas

Estos tres a menudo se agrupan bajo “mantenimiento” y luego se programan como una cita con el dentista.
No son intercambiables:

  • Scrub: Lee todo el pool para verificar checksums; repara la corrupción silenciosa usando redundancia.
  • Resilver: Reconstruye datos en un dispositivo de reemplazo; la prioridad es la seguridad de los datos, no la conveniencia.
  • SMART long test: Auto-prueba del dispositivo; puede detectar problemas del disco pero no valida la redundancia ni los checksums de ZFS.

Una postura sensata en producción usa los tres, pero nunca finge que uno reemplaza a otro. Scrub te dice si
tus datos almacenados siguen legibles y correctos. SMART te dice si un dispositivo parece honesto hoy.
Resilver te dice qué tan rápido puedes dejar de preocuparte tras un intercambio de disco.

Hechos e historia que vale la pena conocer

Esto no es trivia para la noche de preguntas. Cambia cómo programas y cómo interpretas los resultados.

  1. Los scrubs existen porque la corrupción silenciosa es real. “Bit rot” no es un mito; los checksums end-to-end de ZFS fueron diseñados para detectarlo.
  2. ZFS popularizó el checksumming en almacenamiento de propósito general. Cuando ZFS llegó a Sun (mediados de los 2000), la integridad end-to-end no era estándar en sistemas de archivos de consumo.
  3. Scrub es una operación a nivel de pool, no de dataset. No puedes scrappear solo “el dataset importante” y llamarlo cobertura.
  4. Las lecturas del scrub aún pueden convertirse en escrituras. Si ZFS encuentra datos malos y puede repararlos, reescribirá bloques corregidos.
  5. Las características de reconstrucción de RAIDZ difieren de los mirrors. Los mirrors suelen reparar usando una copia alternativa sencilla; RAIDZ necesita cálculos de paridad y puede comportarse distinto bajo carga.
  6. Los pools grandes convierten “scrub mensual” en una mentira. Si tu scrub tarda 10 días, “mensual” realmente significa “siempre”. Eso es una falla de programación, no del calendario.
  7. Scrub compite con ARC y el comportamiento de prefetch. La caché puede ayudar o perjudicar según la presión de memoria y la carga; los scrubs pueden desplazar datos calientes de la aplicación.
  8. La disposición de vdev domina el comportamiento del scrub. Añadir vdevs añade paralelismo; añadir discos más grandes aumenta la duración. “Pool del mismo tamaño” no implica “mismo tiempo de scrub”.
  9. Algunas ralentizaciones del scrub son en realidad efectos secundarios de amplificación de escritura. Escrituras intensas durante scrub pueden amplificar la fragmentación y hacer que futuros scrubs sean más lentos.

Idea parafraseada de John Allspaw: “La confiabilidad viene de diseñar pensando en la falla, no de fingir que no ocurrirá.”
Los scrubs son una de las herramientas que mantienen la falla honesta. La programación es cómo evitas que la herramienta te haga daño.

Elige una política de scrub: frecuencia, ventanas y expectativas

Frecuencia: deja de copiar “mensual” de internet

“Scrub mensual” es un buen valor por defecto para muchos pools, y una regla terrible para otros. La frecuencia debe fijarse por:
(1) qué tan rápido puedes hacer el scrub, (2) cuán rápido quieres detectar corrupción latente, y (3) cuánto riesgo incurres al ejecutar scrubs bajo carga.

Orientación práctica que funciona en producción:

  • HDD RAIDZ, gran capacidad: mensual puede estar bien si los scrubs terminan en uno o dos días. Si no, considera cada 6–8 semanas e invierte en reducir la duración del scrub (más vdevs, mejor disposición) en lugar de convertir tu pool en un trabajo de fondo perpetuo.
  • Mirrors en HDD para cargas sensibles a latencia: cada 2–4 semanas suele ser realista porque los mirrors scrubean más rápido y la detección rápida importa.
  • Pools todo-flash: la frecuencia puede ser mayor, pero no lo hagas “porque los SSD son rápidos.” Los controladores se saturan y tus horas pico siguen importando.
  • Pools de archivo con poco churn: menos frecuencia puede ser aceptable, pero solo si toleras más tiempo para detectar corrupción silenciosa.

Ventanas: elige la hora observando, no adivinando

La mejor ventana para scrub es la que tus usuarios no notan. Eso no siempre es “2 AM domingo.” En empresas globales,
el domingo a las 2 AM es lunes a las 10 AM en algún lugar. En empresas con procesos por lotes, la noche puede ser más ocupada que el día. En entornos con muchas copias de seguridad, los fines de semana tienen su propia violencia.

Elige la ventana como eliges una ventana de mantenimiento para una base de datos: midiendo latencia de lectura,
profundidad de cola y steal de CPU, luego escogiendo el periodo menos malo. Si tu telemetría es débil, empieza con:
(a) la hora con menor p95 de latencia de disco, (b) la hora con menor carga de escrituras síncronas,
(c) la hora menos probable de ser ocupada por backups.

Fijar expectativas: define el “dolor permitido”

Si no defines el impacto aceptable, el scrub lo definirá por ti. Pon números:

  • Aumento máximo aceptable en latencia de lectura (p. ej., +3 ms en p95 para HDD, +0.5 ms para SSD).
  • Reducción máxima aceptable en IOPS (p. ej., no por debajo del 70% de la línea base para pools críticos).
  • Condiciones de aborto (p. ej., cancelar scrub si la latencia del pool supera el umbral durante 15 minutos).

Esto importa porque ZFS scrubea felizmente a través del fuego a menos que le digas lo contrario.

Monitorización que realmente predice el dolor

“El scrub es lento” no es una métrica. Scrub es una carga de trabajo. Necesitas las mismas señales que querrías para cualquier carga:
rendimiento, latencia, concurrencia y saturación. Y necesitas contexto específico de ZFS: salud de vdev,
errores de checksum y si el pool está reparando.

Qué vigilar durante un scrub

  • Tasa de escaneo y ETA del pool desde zpool status.
  • Utilización a nivel de vdev (profundidad de colas, await, svctm cuando aplique) vía iostat o zpool iostat.
  • Latencia de la aplicación (p95/p99 de la base de datos, latencia del almacenamiento de VM).
  • Comportamiento del ARC: cambios en ratio de aciertos y presión de memoria pueden convertir un scrub en una fiesta de expulsión de caché.
  • Contadores de error: errores de lectura/escritura/checksum, además de indicios SMART.

La trampa: vigilar solo el throughput. Muchos MB/s pueden seguir significando latencia terrible para I/O síncrono pequeño.
Tus usuarios no experimentan MB/s; experimentan espera.

Guion de diagnóstico rápido

Cuando alguien dice “todo está lento” y sospechas de un scrub, necesitas una ruta de 3 minutos hacia una respuesta creíble.
Este es el orden que uso en producción porque converge rápido.

Primero: confirmar scrub/resilver y si está reparando

  • Comprueba si hay un scrub en ejecución y su tasa de escaneo.
  • Verifica si está encontrando errores (trabajo de reparación incrementa la carga de escritura).
  • Comprueba si hay un resilver en curso (eso cambia prioridades).

Segundo: identificar el recurso saturado (disco, CPU o algo que pretende ser disco)

  • Si los discos muestran await/cola alta y poco tiempo idle, estás limitado por I/O.
  • Si la CPU está al máximo en hilos del kernel (o iowait domina), el sistema lucha por alimentar I/O.
  • Si hay rutas de almacenamiento en red involucradas (iSCSI/NFS), confirma que no estés depurando la capa equivocada.

Tercero: encontrar el vdev más lento

El rendimiento de ZFS lo marca el vdev más lento cuando necesitas progreso uniforme. Un disco marginal puede
alargar el tiempo de scrub y aumentar el tiempo en riesgo. Usa estadísticas por vdev y contadores de errores; no adivines.

Cuarto: decidir “estrangular, mover o abortar”

  • Estrangular si el pool está sano y solo necesitas menor impacto.
  • Mover la ventana si esto es una colisión predecible con trabajos por lotes/backups.
  • Abortar si la latencia está causando impacto al cliente y puedes reanudar de forma segura después.
  • No abortar un resilver a menos que estés absolutamente seguro de lo que estás sacrificando.

Tareas prácticas: comandos, salidas y decisiones

Estos son comandos reales que puedes ejecutar. Cada tarea incluye: comando, qué significa la salida y la decisión que tomas.
Asumo OpenZFS típico en Linux o FreeBSD. Algunos ajustes difieren por plataforma; la lógica diagnóstica no.

Task 1: Confirmar si un scrub está en ejecución y cuánto ha avanzado

cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 01:10:12 2025
        3.12T scanned at 612M/s, 1.74T issued at 341M/s, 21.4T total
        0B repaired, 8.12% done, 17:21:33 to go
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0
            sde     ONLINE       0     0     0
            sdf     ONLINE       0     0     0

errors: No known data errors

Significado: Scrub está activo, las tasas de scanned e issued difieren (algunas lecturas encoladas pero no emitidas aún),
la ETA es larga. No hay reparaciones. Errores limpios.
Decisión: Si esto solapa horas pico, estrangula o reprograma en lugar de entrar en pánico. Si la ETA es absurda,
sospecha cuello de botella en vdev o carga competidora.

Task 2: Obtener tasas de I/O por vdev del scrub para detectar un rezagado

cr0x@server:~$ zpool iostat -v tank 5 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        10.2T  11.2T  3.10K   210   320M  22.1M
  raidz2-0                  10.2T  11.2T  3.10K   210   320M  22.1M
    sda                         -      -    520    35  54.1M  3.7M
    sdb                         -      -    515    34  53.8M  3.6M
    sdc                         -      -    518    36  54.0M  3.8M
    sdd                         -      -    110    33  11.2M  3.6M
    sde                         -      -    521    35  54.3M  3.7M
    sdf                         -      -    516    37  54.0M  3.8M
--------------------------  -----  -----  -----  -----  -----  -----

Significado: Un disco (sdd) está leyendo mucho menos que sus pares. Ese es el ladrón del tiempo de scrub.
Decisión: Extrae estadísticas SMART y logs de errores para sdd. Si está lento pero sin errores, aún puede estar fallando
(timeouts, sectores reubicados pendientes o problemas de firmware). Considera reemplazarlo de forma proactiva.

Task 3: Comprobar contadores de error del dispositivo y si ZFS está “rindiéndose” y reintentando

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 01:10:12 2025
        3.30T scanned at 585M/s, 1.92T issued at 340M/s, 21.4T total
        0B repaired, 8.95% done, 16:58:01 to go
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       2     0     0
            sde     ONLINE       0     0     0
            sdf     ONLINE       0     0     0

errors: No known data errors

Significado: sdd tiene errores de lectura contados. Incluso si ZFS reparó de forma transparente, esto es una señal.
Decisión: Trátalo como un evento de fiabilidad. No esperes a que “empeore”. Planea el reemplazo,
y vigila el incremento de contadores durante el scrub.

Task 4: En Linux, comprobar nombres udev y mapear discos correctamente antes de reemplazar nada

cr0x@server:~$ ls -l /dev/disk/by-id/ | grep -E 'sdd$'
lrwxrwxrwx 1 root root  9 Dec 26 10:02 ata-WDC_WD120EDAZ-11F3RA0_9GJ3K2AA -> ../../sdd
lrwxrwxrwx 1 root root 10 Dec 26 10:02 wwn-0x50014ee2b6d12345 -> ../../sdd

Significado: Tienes identificadores estables para sdd. Bien. Deja de usar /dev/sdX en procedimientos de reemplazo
si quieres dormir por las noches.
Decisión: Usa la ruta by-id en zpool replace y en la documentación para on-call.

Task 5: Verificar si la latencia de la aplicación se correlaciona con el scrub

cr0x@server:~$ iostat -x 5 2
Linux 6.8.0 (server) 	12/26/2025 	_x86_64_	(32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           6.30    0.00    3.10   18.40    0.00   72.20

Device            r/s     w/s   rMB/s   wMB/s  rrqm/s  wrqm/s  %util  await
sda             92.0    6.0    9.6     0.7      0.0     0.3   74.0   14.8
sdb             91.5    6.1    9.6     0.7      0.0     0.2   73.1   15.2
sdc             92.2    6.0    9.6     0.7      0.0     0.2   73.9   15.0
sdd             19.3    5.9    2.1     0.7      0.0     0.1   99.0   87.4
sde             91.8    6.0    9.6     0.7      0.0     0.2   74.2   14.9
sdf             91.6    6.2    9.6     0.7      0.0     0.2   73.5   15.1

Significado: sdd está al 99% de utilización con un await enorme. Los demás están relativamente bien. iowait está elevado.
Esto es clásico “un disco arruina la fiesta.”
Decisión: Estrangular el scrub podría reducir el dolor, pero no arreglará sdd. Prioriza la triage de salud del dispositivo.

Task 6: Validar si el pool está limitado por un vdev o por el ancho de banda total

cr0x@server:~$ zpool iostat tank 1 5
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        10.2T  11.2T  3.05K   230    318M  24.0M
tank        10.2T  11.2T  3.12K   240    326M  25.0M
tank        10.2T  11.2T  2.98K   225    310M  23.1M
tank        10.2T  11.2T  1.40K   980    145M  96.2M
tank        10.2T  11.2T  3.10K   235    320M  24.3M

Significado: Un intervalo muestra picos de escritura; probablemente escrituras de la aplicación colisionando con el scrub.
Decisión: Si trabajos intensos en escritura se solapan (backups, compactación, rotación de logs hacia disco), mueve la ventana
del scrub o estrangula el scrub para proteger la latencia.

Task 7: Comprobar si la presión del ARC está causando daños colaterales

cr0x@server:~$ arcstat 5 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  size     c
10:14:05  812K  124K     15  108K  87%   10K   8%    6K   5%  96.0G  110G
10:14:10  790K  210K     27  198K  94%    8K   4%    4K   2%  96.0G  110G
10:14:15  820K  260K     32  250K  96%    7K   3%    3K   1%  96.0G  110G

Significado: El porcentaje de misses aumenta durante el scrub; los misses por demanda dominan. El scrub probablemente está desplazando datos calientes.
Decisión: Si tu carga depende de caché, estrangula el scrub, considera programarlo cuando el churn de caché sea bajo,
y evalúa si el dimensionamiento del ARC es apropiado para el pico.

Task 8: Revisar los logs de eventos de ZFS alrededor del inicio del dolor

cr0x@server:~$ zpool events -v | tail -n 12
TIME                           CLASS
Dec 26 2025 10:02:11.123456789 ereport.fs.zfs.io
    pool = tank
    vdev_path = /dev/disk/by-id/wwn-0x50014ee2b6d12345
    vdev_guid = 1234567890123456789
    errno = 5
    io_priority = scrub
Dec 26 2025 10:02:14.987654321 ereport.fs.zfs.io
    pool = tank
    vdev_path = /dev/disk/by-id/wwn-0x50014ee2b6d12345
    errno = 5
    io_priority = scrub

Significado: Errores de I/O en un vdev específico durante el scrub. errno 5 es error de I/O.
Decisión: Deja de debatir sobre la programación y empieza a planear el reemplazo. Un scrub está haciendo su trabajo al encontrar enlaces débiles.

Task 9: Verificar el historial de scrubs y si se está completando regularmente

cr0x@server:~$ zpool status tank | sed -n '1,20p'
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 01:10:12 2025
        3.45T scanned at 610M/s, 2.05T issued at 362M/s, 21.4T total
        0B repaired, 9.30% done, 16:21:02 to go

Significado: Obtienes el progreso actual del scrub, pero para saber si se completa históricamente debes registrarlo externamente
o consultar las herramientas de la plataforma. Muchos equipos solo notan que los scrubs “tardan una eternidad” después de un año de fallos silenciosos.
Decisión: Empieza a registrar inicio/fin/bytes reparados en la monitorización. Si los scrubs no completan entre ventanas,
cambia la frecuencia o la arquitectura del pool.

Task 10: Iniciar un scrub intencionalmente (y entender qué significa “iniciar”)

cr0x@server:~$ sudo zpool scrub tank
cr0x@server:~$ zpool status tank | head -n 8
  pool: tank
 state: ONLINE
  scan: scrub in progress since Thu Dec 26 10:20:55 2025
        0B scanned at 0B/s, 0B issued at 0B/s, 21.4T total
        0B repaired, 0.00% done, no estimated completion time

Significado: Scrub encolado e iniciado; las tasas iniciales pueden mostrar 0 hasta que suba y las estadísticas se refresquen.
Decisión: Si estás probando la programación, inicia scrubs manualmente en una ventana controlada y mide el impacto.

Task 11: Pausar/detener un scrub cuando sea necesario (y aceptar el intercambio)

cr0x@server:~$ sudo zpool scrub -s tank
cr0x@server:~$ zpool status tank | head -n 8
  pool: tank
 state: ONLINE
  scan: scrub canceled on Thu Dec 26 10:27:12 2025
        3.62T scanned at 600M/s, 2.11T issued at 350M/s, 21.4T total
        0B repaired, 9.86% done

Significado: Scrub cancelado; no “reanudará donde quedó” como un proceso continuo. El próximo scrub vuelve a escanear.
Decisión: Cancela solo cuando el impacto al cliente lo exija. Luego reprograma un nuevo scrub en una ventana más segura pronto.
Si cancelaste por un disco fallando, el reemplazo viene primero.

Task 12: En Linux, comprobar y establecer parámetros del módulo relacionados con scrub (ejemplo: delay)

cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_scan_idle
0
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_scan_min_time_ms
1000

Significado: Estos parámetros influyen en el comportamiento de scan. Los valores varían por distro/version, pero verificas
si el sistema está configurado para ceder o correr agresivamente.
Decisión: Si los scrubs aplastan la latencia, aumenta el comportamiento de ceder/estrangulamiento (cuando sea soportado) en lugar de solo mover la programación.

Task 13: Confirmar ajustes de TRIM/autotrim para que los scrubs no sean culpados por mantenimiento de SSD

cr0x@server:~$ zpool get autotrim tank
NAME  PROPERTY  VALUE     SOURCE
tank  autotrim  off       default

Significado: Autotrim está off; la recuperación de espacio libre en SSD puede ocurrir de otras maneras y en momentos inconvenientes.
Decisión: Si manejas pools SSD, decide deliberadamente: habilita autotrim si es apropiado para tu entorno,
y evita coincidir trims intensos con scrubs salvo que lo hayas probado.

Task 14: Medir patrones reales de I/O de la aplicación durante el scrub (no solo estadísticas de ZFS)

cr0x@server:~$ pidstat -d 5 2
Linux 6.8.0 (server) 	12/26/2025 	_x86_64_	(32 CPU)

10:33:10      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
10:33:15      999     18321  20480.00   5120.00      0.00  postgres
10:33:15        0      1287      0.00  86016.00      0.00  z_wr_iss
10:33:15        0      1288  327680.00      0.00      0.00  z_rd_iss

10:33:15      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
10:33:20      999     18321  19456.00   6144.00      0.00  postgres
10:33:20        0      1287      0.00  90112.00      0.00  z_wr_iss
10:33:20        0      1288  335872.00      0.00      0.00  z_rd_iss

Significado: Puedes ver hilos del kernel del scrub haciendo lecturas intensas, mientras la base de datos también está activa.
Decisión: Si la carga es sensible a latencia (bases de datos, discos de VM), programa scrubs en ventanas de menor demanda o estrangula.
Si debes correr durante horas laborales, necesitas cortafuegos.

Estrangulamiento y afinado: cómo hacer que los scrubs se comporten

La programación es necesaria, pero no suficiente. En muchos entornos no puedes encontrar una ventana verdaderamente ociosa.
Aun así necesitas que los scrubs se ejecuten. Así que controlas el radio de impacto.

Principio: proteger la latencia, aceptar mayor duración

A tus usuarios no les importa que el scrub termine en 9 horas en vez de 14. Les importa que la API
deje de tirar timeouts. Un scrub más largo está bien si se mantiene dentro de tu ventana de riesgo aceptable y
no se solapa con demasiada frecuencia. Si tus scrubs se vuelven “siempre activos”, eso no es un problema de estrangulamiento.
Es un problema de capacidad y arquitectura.

Prioridad de I/O a nivel de SO: tosca, pero a veces suficiente

En Linux, a menudo puedes mejorar la equidad ejecutando el inicio del scrub con menor prioridad de I/O para el
proceso iniciador. Esto no re-prioriza mágicamente la I/O del kernel en todos los casos, pero puede ayudar
en algunas configuraciones. Úsalo como una palanca, no como religión.

cr0x@server:~$ sudo ionice -c3 zpool scrub tank
cr0x@server:~$ zpool status tank | head -n 6
  pool: tank
 state: ONLINE
  scan: scrub in progress since Thu Dec 26 11:02:01 2025
        158G scanned at 510M/s, 92.4G issued at 298M/s, 21.4T total

Significado: Scrub en ejecución; intentaste iniciarlo como I/O de clase idle.
Decisión: Si esto reduce el impacto en latencia de forma medible, mantenlo. Si no hace nada, no pierdas tiempo fingiendo.

Tunables de scan de ZFS: usar con moderación, probar agresivamente

OpenZFS expone perillas de comportamiento de scan (nombres y disponibilidad varían por plataforma/version). Algunas influyen
en cómo el trabajo de scan cede a otra I/O, cuánto tiempo los hilos de scan corren antes de dormir, y cuán agresivamente
el sistema intenta usar el ancho de banda disponible.

Debes tratarlas como perillas de base de datos: el valor por defecto es razonable, los cambios tienen efectos secundarios, y
solo las modificas con medición y un plan de reversión.

Lo que suele funcionar:

  • Aumentar el ceder/comportamiento idle para que el scrub se retire bajo carga.
  • Reducir la intensidad del scan si la latencia es el SLO principal.
  • Mantener la prioridad del resilver más alta que la del scrub; no ralentices accidentalmente tu vía de recuperación.

Lo que suele salir mal:

  • Aumentar la agresividad del scan para “terminar más rápido” y luego descubrir que no puedes ejecutar scrubs durante la semana laboral.
  • Cambiar perillas sin entender que el pool en realidad está bloqueado por un disco moribundo (ningún ajuste arregla la física).

Programación consciente de la carga vence a afinados ingeniosos

Si tu carga tiene picos previsibles—ETL a la 01:00, backups a las 02:00, compactación a las 03:00—no tunes ZFS
para pelear con esos picos. Mueve el scrub lejos de ellos. El tuning sirve para suavizar bordes, no para ignorar patrones de tráfico.

Mecánica de programación: cron, timers systemd y cortafuegos

Programar no es “ejecutar el domingo.” Programar es “ejecutar cuando es seguro y detener cuando no lo es.” Eso significa que necesitas:
(1) un disparador automatizado, (2) una comprobación de seguridad y (3) observabilidad cuando se ejecuta.

Cron: simple, fiable y brutalmente honesto

Cron está bien si añades un script wrapper que compruebe la salud del pool, la carga actual y si ya hay un scan en ejecución.
El wrapper es donde vive el profesionalismo.

cr0x@server:~$ cat /usr/local/sbin/zfs-scrub-guard
#!/usr/bin/env bash
set -euo pipefail

POOL="${1:-tank}"

# Refuse if a scrub/resilver is already running
if zpool status "$POOL" | grep -qE "scan: (scrub|resilver) in progress"; then
  echo "$(date -Is) $POOL: scan already in progress; exiting"
  exit 0
fi

# Refuse if pool is degraded
if ! zpool status "$POOL" | grep -q "state: ONLINE"; then
  echo "$(date -Is) $POOL: pool not ONLINE; exiting"
  exit 1
fi

# Refuse if 1-minute load is too high (example threshold)
LOAD1=$(cut -d' ' -f1 /proc/loadavg)
LOAD1_INT=${LOAD1%.*}
if [ "$LOAD1_INT" -ge 20 ]; then
  echo "$(date -Is) $POOL: load too high ($LOAD1); exiting"
  exit 0
fi

echo "$(date -Is) $POOL: starting scrub"
exec zpool scrub "$POOL"
cr0x@server:~$ sudo crontab -l
# Scrub on the first Sunday of the month at 01:30
30 1 1-7 * 0 /usr/local/sbin/zfs-scrub-guard tank >> /var/log/zfs-scrub.log 2>&1

Significado: El script evita escaneos superpuestos, evita scrubs en pools degradados y salta durante alta carga.
Decisión: Ajusta los umbrales a tu entorno. Si no tienes un umbral basado en SLO, estás adivinando—empieza a medir.

timers systemd: mejor estado, mejor reporte

Los timers systemd brillan cuando quieres comportamiento de catch-up para ejecuciones perdidas, logging estandarizado y controles fáciles de desactivar/activar.
En producción, esto importa porque eventualmente tendrás un freeze de mantenimiento o un incidente donde quieras pausar scrubs.

cr0x@server:~$ cat /etc/systemd/system/zfs-scrub@.service
[Unit]
Description=Guarded ZFS scrub for pool %i

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/zfs-scrub-guard %i
cr0x@server:~$ cat /etc/systemd/system/zfs-scrub@.timer
[Unit]
Description=Monthly ZFS scrub timer for pool %i

[Timer]
OnCalendar=Sun *-*-01..07 01:30:00
Persistent=true

[Install]
WantedBy=timers.target
cr0x@server:~$ sudo systemctl enable --now zfs-scrub@tank.timer
Created symlink /etc/systemd/system/timers.target.wants/zfs-scrub@tank.timer → /etc/systemd/system/zfs-scrub@.timer.
cr0x@server:~$ systemctl list-timers | grep zfs-scrub
Sun 2026-01-04 01:30:00 UTC  1 week 1 day left  Sun 2025-12-01 01:30:00 UTC  zfs-scrub@tank.timer  zfs-scrub@tank.service

Significado: Tienes una programación predecible con persistencia (las ejecuciones perdidas se ejecutan tras downtime).
Decisión: Si “Persistent=true” causaría que un scrub arranque inmediatamente después de un reboot en horas pico,
desactiva la persistencia o añade un guard para “horas hábiles”.

Cortafuegos que previenen heridas autoinfligidas

  • No inicies un scrub si hay un resilver activo. Deja que la recuperación termine.
  • No inicies un scrub en un pool degradado a menos que tengas una razón. El scrub añadirá carga en un estado vulnerable.
  • No ejecutes scrubs simultáneamente en todos los pools del mismo host. Escalónalos; tu HBA y backplane también tienen sentimientos.
  • Haz los scrubs observables. Escribe eventos de inicio/fin en un log que realmente leas, y alerta si “el scrub no terminó en N días”.

Broma #2: Si no puedes decir cuándo corrió el scrub, básicamente es el mantenimiento de Schrödinger—tanto hecho como no hecho hasta el outage.

Tres microhistorias corporativas desde las trincheras del scrub

1) Incidente causado por una suposición equivocada: “scrub son solo lecturas”

Una compañía SaaS mediana ejecutaba almacenamiento de VM respaldado por ZFS en un conjunto de pools HDD RAIDZ2. Tenían un scrub mensual
programado a las 02:00 hora local, porque eso hacía el administrador anterior. Funcionó—hasta que la compañía
movió un grupo de clientes a otra región y las “horas tranquilas” dejaron de ser tranquilas.

El on-call vio picos de latencia y asumió que el scrub no podía ser culpable porque “scrub es solo lectura.”
Persiguieron gráficas de red, ajustaron pools de conexiones a la base de datos e incluso revertieron un deploy de la aplicación.
Mientras tanto, ZFS estaba reparando un pequeño número de bloques encontrados durante el scrub. Esa reparación creó escrituras,
que colisionaron con escrituras síncronas de invitados VM. El resultado fue una tormenta perfecta de profundidad de cola y latencia de cola.

La pista estaba a la vista: zpool status mostraba bytes reparados no nulos y un dispositivo lento.
Pero nadie había entrenado al equipo para interpretar “issued vs scanned” o para tratar la reparación como presión de escritura.
Así que el scrub siguió, los clientes seguían con timeouts y el incidente duró más de lo necesario.

La solución fue poco glamurosa: mover la ventana del scrub, añadir un guard basado en carga y fijar un umbral de aborto ligado a la latencia de almacenamiento.
También añadieron alertas cuando los bytes reparados son no cero durante un scrub, porque “scrub son lecturas” dejó de ser una historia reconfortante.

2) Optimización que salió mal: “acabar más rápido haciéndolo agresivo”

Un equipo de analítica empresarial tenía un pool ZFS todo-flash alimentando un clúster de motores de consulta. Los scrubs tardaban más
a medida que el dataset crecía, y alguien decidió “acelerarlos” con un comportamiento de scan más agresivo. El plan: usar tanto
ancho de banda como fuera posible para que los scrubs terminasen antes del ETL del lunes.

Sí, terminó más rápido. También golpeó los controladores SSD con tal fuerza que la recolección de basura de fondo se volvió parte visible del perfil de rendimiento. Los picos de latencia aparecieron no durante el scrub en sí, sino inmediatamente después,
cuando los controladores intentaron limpiar. Peor aún, el ARC se contaminó con lecturas del scrub y los motores de consulta perdieron localidad de caché.

La primera conclusión del equipo fue que el motor de consultas era “inestable.” Intentaron tunear la aplicación, luego el SO,
luego la red. La historia real era más simple: optimizaron para tiempo de finalización del scrub, no para la experiencia de usuario.
Ganaron una carrera que nadie les pidió correr.

El rollback fue instructivo: devolver las perillas de scan a valores por defecto, reducir la intensidad del scrub y programar scrubs en porciones diarias más cortas en lugar de una sesión agresiva. El tiempo de finalización aumentó, pero la latencia cola se aplanó.
La producción recuperó sus modales.

3) Práctica aburrida pero correcta que salvó el día: scrubs escalonados más alertas de “un disco malo”

Un equipo de infraestructura de servicios financieros ejecutaba múltiples pools ZFS por host, mezclando mirrors para bases de datos y RAIDZ para archivos.
Su política de scrubs parecía aburrida: mirrors semanales, RAIDZ mensuales, siempre escalonados por pool y nunca solapados con backups.
También tenían una alerta estándar: “cualquier vdev con await 3x mayor que los pares durante un scrub” desencadena investigación.

Un trimestre, durante un scrub rutinario de un mirror, la alerta saltó para un único SSD que seguía “ONLINE” con cero errores ZFS.
El dispositivo no fallaba de forma ruidosa. Fallaba en silencio, atrapándose ocasionalmente lo suficiente como para crear un pico de latencia de cola.
Porque el equipo miraba el comportamiento por vdev durante el scrub, lo vieron temprano—antes de que la carga de base de datos se convirtiera en daño colateral.

Reemplazaron el dispositivo en una ventana de mantenimiento planificada. Una semana después, los informes SMART empezaron a mostrar indicadores en empeoramiento.
En otras palabras: la práctica aburrida detectó el problema antes de que se graduara a incidente.

Nadie ganó un trofeo. Nadie escribió un postmortem. Ese es el punto.

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

1) Síntoma: “Cada scrub tarda más que el anterior”

Causa raíz: Fragmentación y crecimiento del pool; el aumento de datos incrementa el conjunto de escaneo; un disco envejece; o los scrubs se solapan con cargas más pesadas a medida que el uso cambia.

Solución: Identifica rezagados por vdev con zpool iostat -v. Reemplaza dispositivos lentos de forma proactiva. Reevalúa la ventana. Si los scrubs no completan entre ventanas, cambia la frecuencia o expande el paralelismo de vdevs.

2) Síntoma: “Picos de latencia solo durante el scrub; el throughput parece bien”

Causa raíz: Saturación de profundidad de colas. El scrub consume tiempo de servicio del disco, perjudicando I/O pequeño síncrono.

Solución: Estrangula el scrub (tunables de plataforma, menor prioridad I/O), mueve la ventana y fija umbrales de aborto basados en p95/p99 de latencia en lugar de MB/s.

3) Síntoma: “La ETA del scrub salta mucho”

Causa raíz: Cargas competidoras o un vdev que intermitentemente se queda bloqueado; a veces problemas de controlador/HBA.

Solución: Correlaciona con iostat -x y zpool iostat -v por vdev. Revisa zpool events -v para errores/timeouts de I/O. Investiga cableado/backplane si los stalls son bursty en múltiples discos.

4) Síntoma: “El scrub encuentra errors de checksum, pero el pool permanece ONLINE”

Causa raíz: ZFS reparó usando redundancia; existe media o ruta subyacente con problemas.

Solución: Trátalo como incidente de hardware/ruta de todas formas. Extrae SMART, revisa logs del controlador y planifica reemplazo si los errores se repiten. “Scrub lo arregló” no es lo mismo que “problema resuelto”.

5) Síntoma: “Los scrubs están siempre en ejecución; nunca hay un periodo quieto”

Causa raíz: La duración del scrub excede la frecuencia, a menudo debido a enorme capacidad con poco paralelismo de vdev o carga constante.

Solución: Incrementa paralelismo (más vdevs), reduce churn donde sea posible, acepta scrubs menos frecuentes con monitorización dirigida y asegura que el sistema tenga margen de rendimiento para mantenimiento.

6) Síntoma: “Tras habilitar ajustes agresivos, scrubs son más rápidos pero al día siguiente todo está lento”

Causa raíz: Contaminación de caché, efectos secundarios de GC de SSD o patrones de I/O perturbados. Moviste el dolor, no lo eliminaste.

Solución: Revierte a valores por defecto, ejecuta scrubs en porciones más pequeñas y mide la latencia durante 24 horas—no solo durante la ventana del scrub.

7) Síntoma: “El scrub arranca justo después del reboot y afecta tráfico pico”

Causa raíz: Persistencia del timer systemd o comportamiento de catch-up que se dispara inmediatamente tras el arranque.

Solución: Desactiva la persistencia o añade comprobaciones de hora/días laborales. Haz de “tiempo de estabilización post-reboot” una regla.

8) Síntoma: “Scrub es lento solo en un pool, en un host”

Causa raíz: Problemas específicos de HBA/lane/backplane, un único disco marginal, o geometría de vdev distinta a la asumida.

Solución: Compara geometrías de vdev, confirma velocidades de enlace y usa iostat por vdev para detectar el outlier. Reemplaza hardware, no esperes esperanza.

Listas de verificación / plan paso a paso

Paso a paso: establecer una programación de scrub que no dañe las horas pico

  1. Mide la latencia y utilización base durante una semana. Necesitas p95/p99 de latencia de disco y utilización por vdev, no solo throughput del pool.
  2. Haz una prueba de scrub controlada en una ventana de bajo riesgo. Registra deltas de latencia y tasa de escaneo.
  3. Escoge una ventana basada en datos, no en tradición. Si tu “off-peak” es en realidad hora de backups, no te obstines.
  4. Define umbrales de aborto (latencia, picos de error, violaciones de SLO que impacten clientes). Decide quién puede cancelar un scrub y cuándo.
  5. Añade cortafuegos: saltar si pool no está ONLINE, saltar si hay resilver, saltar si la carga es alta, saltar si hay backups activos.
  6. Escalona pools en el mismo host y entre clústeres. Si todo scrubea a la vez, inventaste una nueva hora pico.
  7. Registra y alerta en la finalización. “Scrub no terminó en N días” es una señal operativa, no una curiosidad.
  8. Revisa resultados mensualmente: tendencia de duración, errores encontrados, bytes reparados, comportamiento del vdev más lento.

Checklist: antes de iniciar un scrub en producción

  • Estado del pool es ONLINE; no hay resilver activo.
  • No hay discos con fallos conocidos; SMART y contadores ZFS están estables.
  • Backups/ETL/trabajos por lotes no están programados para colisionar.
  • Puedes ver I/O y latencia por vdev en la monitorización.
  • Tienes criterios claros de “cancelar” y un plan para reprogramar.

Checklist: después de que el scrub termine

  • Confirma “errors: No known data errors” y revisa bytes reparados.
  • Compara duración y tasa de escaneo con la ejecución anterior; investiga regresiones.
  • Identifica outliers por vdev (lentos o con errores) y abre un ticket de seguimiento.
  • Anota si la ventana causó impacto visible a usuarios. Si sí, ajusta.

Preguntas frecuentes

1) ¿Debería ejecutar scrubs de ZFS semanalmente o mensualmente?

Mensual es un buen punto de partida, pero fija la frecuencia por la duración del scrub y la tolerancia al riesgo. Si los scrubs tardan muchos días,
mensual se convierte en carga continua—o haces scrubs menos frecuentes o rediseñas para más paralelismo.

2) ¿Es seguro scrubar durante horas laborales?

Puede serlo, si estrangulas y tienes margen. Si no conoces tu margen, asume que no lo tienes.
Para cargas sensibles a latencia, programa fuera de pico o implementa cortafuegos por carga/latencia.

3) ¿Por qué el scrub afecta escrituras si es una operación de lectura?

Porque cuando ZFS encuentra datos malos y la redundancia permite reparar, reescribe datos corregidos. Además, el scrub compite por
tiempo de servicio del disco, lo que indirectamente ralentiza escrituras.

4) ¿Cuál es la diferencia entre “scanned” y “issued” en zpool status?

“Scanned” refleja trabajo recorrido; “issued” refleja I/O realmente enviado. Una gran diferencia puede indicar estrangulamiento,
contención o stalls. Usa estadísticas por vdev para encontrar dónde está atascada la tubería.

5) ¿Debería cancelar un scrub cuando el rendimiento cae en picado?

Si los clientes están afectados y el pool está sano, cancelar y reprogramar es razonable.
Si hay un resilver activo, sé mucho más cauto—la velocidad de recuperación importa más que la conveniencia.

6) ¿Los scrubs desgastan los SSD?

Los scrubs son mayormente lecturas, pero las reparaciones causan escrituras, y las lecturas sostenidas consumen ancho de banda del controlador y pueden
disparar mantenimiento de fondo. El riesgo mayor no es el “desgaste”, sino el impacto de rendimiento y enmascarar un dispositivo fallando.

7) ¿Puedo scrubar solo parte de un pool?

El scrub opera a nivel de pool. No puedes scrubar de forma fiable “solo este dataset” para cobertura de integridad. Planifica ventanas
e intensidad asumiendo trabajo a nivel de pool.

8) Mi pool es enorme. ¿Cómo evito un scrub que nunca termina?

Primero averigua por qué es lento: outliers por vdev, colisiones con cargas o paralelismo insuficiente. Si el pool simplemente
no puede ser scrubeado en una ventana razonable, necesitas un cambio arquitectónico (más vdevs, disposición distinta) o una postura de riesgo revisada con monitorización fuerte.

9) ¿Es necesario un scrub si tengo backups?

Las copias de seguridad no verifican la integridad del almacenamiento primario. Scrub detecta y repara corrupción antes de que la descubras
durante una restauración—cuando el tiempo ya juega en tu contra.

10) ¿Cómo sé si un disco está “lento pero no fallando”?

Compara utilización por vdev y await durante el scrub. Si un disco consistentemente muestra await/%util mucho mayor que sus pares,
trátalo como sospechoso aun con cero errores ZFS. Las fallas silenciosas adoran ser ignoradas.

Próximos pasos que no arruinarán tu semana

Los scrubs de ZFS son innegociables si te importa la integridad de los datos. Pero no tienes que dejar que te intimiden en horas pico.
Haz que los scrubs sean aburridos: ventanas predecibles, intensidad controlada y señales claras cuando algo falla.

  1. Elige una ventana de scrub usando datos de latencia, no por hábito.
  2. Añade un script guardián que se niegue a iniciar scrubs bajo carga, durante resilvers o en pools degradados.
  3. Instrumenta el comportamiento por vdev para que “un disco malo” se detecte temprano.
  4. Define criterios de cancelación ligados al impacto al cliente y ensaya la decisión.
  5. Revisa tendencias de scrub mensualmente; si la duración crece, arregla arquitectura o hardware antes de que el pool decida por ti.

Haz esas cosas y los scrubs serán lo que deben ser: comprobaciones rutinarias de integridad, no experimentos sorpresa de rendimiento sobre tus usuarios.

← Anterior
Docker «No Space Left on Device»: Los lugares ocultos donde Docker consume tu disco
Siguiente →
La interfaz web de Proxmox no se abre en el puerto 8006: reinicia pveproxy y recupera el acceso

Deja un comentario