Volúmenes NFS de Docker con tiempo de espera: Opciones de montaje que realmente mejoran la estabilidad

¿Te fue útil?

Despliegas un contenedor perfectamente aburrido. Escribe unos archivos. Luego, a las 02:17, todo se congela como si esperara una autorización.
df se queda colgado. Los hilos de tu aplicación se acumulan. Los logs de Docker no dicen nada útil. La única pista es un rastro de “nfs: server not responding”.

NFS en sistemas containerizados falla de formas que parecen errores de la aplicación, fallos del kernel o “vibras de red”.
Normalmente no es ninguno de esos. Son las semánticas de montaje chocando con fallos de red transitorios, sorpresas de DNS y un driver de volúmenes que no te dice lo que hizo.

Por qué los volúmenes NFS de Docker agotan el tiempo (y por qué parece aleatorio)

“Volúmenes NFS” en Docker son simplemente montajes NFS de Linux creados por el host y luego bind-mountados dentro de contenedores.
Suena simple, y lo es—hasta que recuerdas que NFS es un sistema de archivos de red con semánticas de reintento,
timeouts de RPC, locking y estado.

La mayoría de los “tiempos de espera” no son un único timeout

Cuando alguien informa “NFS timed out”, normalmente quiere decir una de estas cosas:

  • El cliente está reintentando para siempre (hard mount) y el hilo de la aplicación queda bloqueado en sueño no interrumpible (D state). Esto parece un cuelgue.
  • El cliente se rindió (soft mount) y devolvió un error de E/S. Esto parece corrupción, escrituras fallidas o “mi app falla al azar.”
  • El servidor se fue y volvió, pero la vista del cliente sobre el export ya no coincide (stale file handles). Esto parece “funcionaba ayer”.
  • Problemas de plomería RPC (especialmente NFSv3: portmapper/rpcbind, mountd, lockd) causan fallos parciales donde algunas operaciones funcionan y otras agotan el tiempo.
  • Flaps en resolución de nombres o enrutamiento causan parones intermitentes que se autorrecuperan. Estos son los peores porque alimentan superstición.

Docker amplifica los modos de fallo

NFS es sensible al tiempo de montaje. Docker es muy bueno iniciando contenedores rápidamente, de forma concurrente y a veces antes de que la red esté lista.
Si el montaje NFS se desencadena bajo demanda, tu “inicio de contenedor” se convierte en “inicio de contenedor más red más DNS más disponibilidad del servidor”.
Eso está bien en un laboratorio. En producción, es una manera elaborada de convertir pequeña fluctuación de red en una caída completa del servicio.

Hard vs soft no es un control de rendimiento; es una decisión de riesgo

Para la mayoría de cargas con estado, el valor seguro por defecto es hard: seguir reintentando, no fingir que las escrituras tuvieron éxito.
Pero los montajes hard pueden colgar procesos cuando el servidor no está alcanzable. Tu trabajo es hacer que “servidor no alcanzable” sea raro y breve,
y hacer el montaje resiliente ante el caos normal de las redes.

Hay una idea parafraseada de Werner Vogels (CTO de Amazon) que vale la pena mantener en la cabeza: “Todo falla, así que diseña para el fallo.”
Los montajes NFS son exactamente donde esa filosofía deja de ser inspiradora y pasa a ser una lista de comprobación.

Hechos interesantes y contexto (breve, concreto, útil)

  • NFS existe desde antes de los contenedores. Se originó en los años 80 como forma de compartir archivos en red sin complejidad de estado en el cliente.
  • NFSv3 es mayormente stateless. Eso hizo que el failover fuera más simple en algunos aspectos, pero desplazó la complejidad a daemons auxiliares (rpcbind, mountd, lockd).
  • NFSv4 consolidó los canales laterales. v4 suele usar un solo puerto conocido (2049) e integra locking y estado, lo que a menudo mejora la compatibilidad con firewalls y NAT.
  • “Hard mount” es el predeterminado histórico por una razón. Perder datos silenciosamente es peor que esperar; los hard mounts favorecen corrección sobre disponibilidad.
  • El cliente NFS de Linux tiene múltiples capas de timeouts. Existe el timeout por RPC (timeo), el número de reintentos (retrans) y luego comportamientos de recuperación a mayor nivel.
  • Stale file handles son un impuesto clásico de NFS. Ocurren cuando el mapeo inode/file handle del servidor cambia bajo el cliente—común después de failover o cambios en el export.
  • NFS sobre TCP no siempre fue el predeterminado. UDP fue popular al principio; TCP es ahora la opción sensata por fiabilidad y control de congestión.
  • DNS importa más de lo que crees. Los clientes NFS pueden cachear el nombre a IP de forma distinta a tu aplicación; un cambio de DNS a mitad puede producir síntomas de “la mitad del mundo funciona”.

Broma #1: NFS es como una impresora compartida en la oficina—cuando funciona, nadie se da cuenta; cuando falla, de repente todos tienen documentos “críticos para el negocio”.

Guía de diagnóstico rápido (encuentra el cuello de botella deprisa)

El objetivo no es “recopilar cada métrica”. El objetivo es decidir, rápido, si tienes un problema de ruta de red,
un problema del servidor, un problema de semánticas de montaje del cliente o un problema de orquestación de Docker.
Aquí está el orden que ahorra tiempo.

Primero: confirma que es NFS y no la app

  1. En el host Docker, intenta un simple stat o ls en la ruta montada. Si cuelga, no es tu app. Es el montaje.
  2. Revisa dmesg por server not responding / timed out / stale file handle. Los mensajes del kernel son contundentes y generalmente correctos.

Segundo: decide “red vs servidor” con una prueba

  1. Desde el cliente, verifica conectividad al puerto 2049 y (si usas NFSv3) rpcbind/portmapper. Si no puedes conectar, deja de culpar las opciones de montaje.
  2. Desde otro host en el mismo segmento de red, prueba lo mismo. Si el problema está aislado a un cliente, sospecha firewall local, agotamiento de conntrack, MTU o una ruta mala.

Tercero: verifica versión de protocolo y opciones de montaje

  1. Comprueba si estás en NFSv3 o NFSv4. Muchos “tiempos de espera aleatorios” son en realidad problemas de rpcbind/mountd de NFSv3 en redes modernas.
  2. Confirma hard, timeo, retrans, tcp, y si usaste intr (comportamiento obsoleto) u otras flags legacy.

Cuarto: inspecciona logs y saturación en el servidor

  1. El load average del servidor no basta. Mira hilos NFS, latencia de disco y caídas de red.
  2. Si el servidor es un appliance NAS, identifica si está limitado por CPU (cifrado, checksumming) o por I/O (discos, rebuild, borrado de snapshots).

Si haces esas cuatro fases, normalmente puedes nombrar la clase de fallo en menos de diez minutos. La parte larga es la política.

Patrones de configuración de Docker que no te sabotean

Patrón 1: driver local de Docker con opciones NFS (bien, pero verifica qué montó)

El driver local de Docker puede montar NFS usando type=nfs y o=....
Esto es común en Compose y Swarm.
La trampa: la gente asume que Docker “hace algo inteligente”. No lo hace. Pasa opciones al helper de montaje.
Si el helper de montaje cae a otra versión o ignora una opción, puede que no lo notes.

Patrón 2: pre-montar en el host y bind-mountar en contenedores (a menudo más predecible)

Si pre-montas vía /etc/fstab o unidades systemd, puedes controlar ordering, reintentos y observar el montaje directamente.
Docker entonces solo bind-mounta una ruta local. Esto reduce la “magia de Docker”, lo cual suele ser bueno para dormir tranquilo.

Patrón 3: separar montajes por clase de carga

No uses un solo export NFS y un solo conjunto de opciones para todo.
Trata NFS como un servicio con SLOs: metadata baja latencia (caches de CI), throughput masivo (media), corrección primero (datos de aplicaciones stateful).
Distintos montajes, distintas opciones, distintas expectativas.

Tareas prácticas: comandos, salida y la decisión que tomas

Estas son las acciones de on-call que convierten “NFS es inestable” en un siguiente paso claro. Ejecútalas en el host Docker salvo indicación.
Cada tarea incluye (1) comando, (2) qué significa la salida, (3) qué decisión tomar.

Tarea 1: Identificar qué montajes son NFS y cómo están configurados

cr0x@server:~$ findmnt -t nfs,nfs4 -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET                SOURCE                     FSTYPE OPTIONS
/var/lib/docker-nfs    nas01:/exports/appdata     nfs4   rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.8.21

Significado: Confirma versión NFS, proto y si estás en hard o soft. También muestra si rsize/wsize son enormes y potencialmente desajustados.

Decisión: Si ves vers=3 inesperadamente, planifica migrar a v4 o auditar puertos rpcbind/mountd. Si ves soft en cargas con muchas escrituras, cámbialo.

Tarea 2: Confirmar configuración del volumen Docker (qué cree Docker que pidió)

cr0x@server:~$ docker volume inspect appdata
[
  {
    "CreatedAt": "2026-01-01T10:12:44Z",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/appdata/_data",
    "Name": "appdata",
    "Options": {
      "device": ":/exports/appdata",
      "o": "addr=10.10.8.10,vers=4.1,proto=tcp,hard,timeo=600,retrans=2,noatime",
      "type": "nfs"
    },
    "Scope": "local"
  }
]

Significado: Esto es configuración, no la verdad. Las opciones de Docker pueden ser correctas mientras el montaje real difiere.

Decisión: Compárala con findmnt. Si difieren, investiga comportamiento del helper de montaje, defaults y soporte del kernel.

Tarea 3: Buscar errores del cliente NFS en el kernel ahora mismo

cr0x@server:~$ dmesg -T | egrep -i 'nfs:|rpc:|stale|not responding|timed out' | tail -n 20
[Fri Jan  3 01:58:41 2026] nfs: server nas01 not responding, still trying
[Fri Jan  3 01:59:12 2026] nfs: server nas01 OK

Significado: “Not responding, still trying” indica un hard mount reintentando tras una interrupción.

Decisión: Si estos eventos coinciden con bloqueos de la app, investiga caídas de red o stalls del servidor; no “arregles” la app.

Tarea 4: Confirmar el estado de procesos durante un hang (¿está en D-state?)

cr0x@server:~$ ps -eo pid,stat,comm,wchan:40 | egrep 'D|nfs' | head
 8421 D    php-fpm          nfs_wait_on_request
 9133 D    rsync            nfs_wait_on_request

Significado: Estado D con nfs_wait_on_request apunta a I/O del kernel bloqueada esperando NFS.

Decisión: Trata como incidente de infraestructura. Reiniciar contenedores no ayudará si el montaje está hard-stuck.

Tarea 5: Comprobar conectividad TCP básica al servidor NFS

cr0x@server:~$ nc -vz -w 2 10.10.8.10 2049
Connection to 10.10.8.10 2049 port [tcp/nfs] succeeded!

Significado: Puerto 2049 accesible ahora.

Decisión: Si esto falla durante el incidente, tus opciones de montaje no son el problema principal; arregla enrutamiento, ACLs, firewall o disponibilidad del servidor.

Tarea 6: Si usas NFSv3, confirma que rpcbind es alcanzable (dependencia oculta común)

cr0x@server:~$ nc -vz -w 2 10.10.8.10 111
Connection to 10.10.8.10 111 port [tcp/sunrpc] succeeded!

Significado: rpcbind/portmapper alcanzable. Sin ello, los montajes NFSv3 pueden fallar o colgar durante la negociación.

Decisión: Si 111 está bloqueado y estás en v3, migra a v4 o abre los puertos requeridos correctamente (y documéntalo).

Tarea 7: Identificar versión NFS negociada y dirección del servidor usada (atrapa sorpresas de DNS)

cr0x@server:~$ nfsstat -m
/var/lib/docker-nfs from nas01:/exports/appdata
 Flags: rw,hard,noatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.8.21,local_lock=none

Significado: Confirma ajustes negociados. Fíjate en el nombre del servidor vs IP, y en cualquier comportamiento de local_lock.

Decisión: Si el montaje usa un hostname y tu DNS es inestable, cambia a IP o fija entradas en hosts—luego planifica una mejor estrategia de DNS.

Tarea 8: Medir retransmisiones y dolor a nivel RPC (¿es pérdida de paquetes?)

cr0x@server:~$ nfsstat -rc
Client rpc stats:
calls      retrans    authrefrsh
148233     912        148245

Significado: Retransmits indican RPCs que tuvieron que ser reenviadas. Un conteo creciente de retrans se correlaciona con pérdida, congestión o stalls del servidor.

Decisión: Si retrans salta durante incidentes, inspecciona caídas de red y carga del servidor; considera aumentar timeo modestamente, no disminuirlo.

Tarea 9: Comprobar errores y drops en la interfaz (no adivines)

cr0x@server:~$ ip -s link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    RX:  bytes packets errors dropped  missed   mcast
    128G  98M     0     127      0      1234
    TX:  bytes packets errors dropped carrier collsns
    141G  92M     0      84      0      0

Significado: Drops en RX/TX pueden ser suficientes para disparar “not responding” de NFS bajo carga.

Decisión: Si los drops crecen, investiga NIC rings, mismatch de MTU, congestión del switch o saturación de CPU del host.

Tarea 10: Detectar mismatch de MTU rápidamente (las jumbo frames son inocentes hasta probadas culpables)

cr0x@server:~$ ping -c 3 -M do -s 8972 10.10.8.10
PING 10.10.8.10 (10.10.8.10) 8972(9000) bytes of data.
From 10.10.8.21 icmp_seq=1 Frag needed and DF set (mtu = 1500)
From 10.10.8.21 icmp_seq=2 Frag needed and DF set (mtu = 1500)
From 10.10.8.21 icmp_seq=3 Frag needed and DF set (mtu = 1500)

--- 10.10.8.10 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2043ms

Significado: La MTU de la ruta es 1500, pero tu host cree que es 9000. Eso provoca blackholing y stalls “aleatorios”.

Decisión: Arregla MTU de extremo a extremo o reduce a 1500. Luego reevalúa la estabilidad de NFS antes de tocar opciones de montaje.

Tarea 11: Confirmar que el export del servidor existe y los permisos son sensatos (vista servidor)

cr0x@server:~$ showmount -e 10.10.8.10
Export list for 10.10.8.10:
/exports/appdata 10.10.8.0/24
/exports/shared  10.10.0.0/16

Significado: Muestra exports (útil sobretodo en entornos NFSv3; para v4 sigue siendo una pista útil).

Decisión: Si el export no aparece o la subred del cliente no está permitida, deja de tunear el cliente y arregla la política de export.

Tarea 12: Capturar una traza corta de paquetes NFS durante el evento (prueba pérdida vs silencio del servidor)

cr0x@server:~$ sudo tcpdump -i eth0 -nn host 10.10.8.10 and port 2049 -c 30
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:02:11.101223 IP 10.10.8.21.51344 > 10.10.8.10.2049: Flags [P.], seq 219:451, ack 1001, win 501, length 232
10:02:12.102988 IP 10.10.8.21.51344 > 10.10.8.10.2049: Flags [P.], seq 219:451, ack 1001, win 501, length 232
10:02:13.105441 IP 10.10.8.21.51344 > 10.10.8.10.2049: Flags [P.], seq 219:451, ack 1001, win 501, length 232

Significado: Repetidos retransmits sin respuestas del servidor indican que el servidor no responde o que las respuestas no regresan.

Decisión: Si ves retransmits del cliente y ningún reply del servidor, ve a salud del servidor / ruta de retorno de red. Si ves replies del servidor pero el cliente sigue retransmitiendo, sospecha enrutamiento asimétrico o estado del firewall.

Tarea 13: Revisar logs del daemon Docker por intentos y fallos de montaje

cr0x@server:~$ journalctl -u docker --since "30 min ago" | egrep -i 'mount|nfs|volume|rpc' | tail -n 30
Jan 03 09:32:14 server dockerd[1321]: time="2026-01-03T09:32:14.112345678Z" level=error msg="error while mounting volume 'appdata': failed to mount local volume: mount :/exports/appdata:/var/lib/docker/volumes/appdata/_data, data: addr=10.10.8.10,vers=4.1,proto=tcp,hard,timeo=600,retrans=2,noatime, type: nfs: connection timed out"

Significado: Confirma que Docker no pudo montar, frente a la app fallando más tarde.

Decisión: Si los montajes fallan al inicio del contenedor, prioriza la disponibilidad de la red y el servidor; no persigas afinados de runtime todavía.

Tarea 14: Inspeccionar ordering de systemd (network-online no es lo mismo que network)

cr0x@server:~$ systemctl status network-online.target
● network-online.target - Network is Online
     Loaded: loaded (/lib/systemd/system/network-online.target; static)
     Active: active since Fri 2026-01-03 09:10:03 UTC; 1h 2min ago

Significado: Si este target no está activo cuando ocurren los montajes, tu NFS puede competir con la preparación de la red.

Decisión: Si ves problemas de ordering, mueve montajes a unidades systemd con After=network-online.target y Wants=network-online.target, o usa automount.

Tarea 15: Validar que el montaje responde (chequeo rápido)

cr0x@server:~$ time bash -c 'stat /var/lib/docker-nfs/. && ls -l /var/lib/docker-nfs >/dev/null'
real    0m0.082s
user    0m0.004s
sys     0m0.012s

Significado: Operaciones básicas de metadata son rápidas. Si esto a veces tarda segundos o cuelga, tienes latencia intermitente o stalls.

Decisión: Si la metadata está lenta, investiga latencia de disco del servidor y saturación de hilos NFS; las opciones de montaje no salvarán un array ahogándose.

Tres micro-historias corporativas (cómo falla esto en la práctica)

1) Incidente causado por una suposición errónea: “El volumen es local, porque Docker dijo ‘local’”

Una empresa mediana ejecutaba un clúster Swarm para servicios internos. Un equipo creó un volumen Docker con el driver local y opciones NFS.
Todos leyeron “local” y asumieron que los datos vivían en cada nodo. Esa suposición moldeó todo: drills de fallo, ventanas de mantenimiento, incluso propiedad del incidente.

Durante un mantenimiento de red, un switch top-of-rack hizo flapping. Solo algunos nodos perdieron conectividad al NAS por unos segundos.
Los nodos afectados tenían volúmenes montados en hard. Sus contenedores no se reiniciaron; simplemente dejaron de avanzar. Los health checks caducaron.
El orquestador empezó a reprogramar, pero las nuevas tareas recalcaron en los mismos nodos dañados porque el scheduler no sabía que NFS era el cuello de botella.

La respuesta de on-call fue clásica: reiniciar el servicio. Eso solo creó más procesos bloqueados. Alguien intentó borrar y recrear el volumen.
Docker cumplió, pero el montaje del kernel todavía estaba trabado. El host se convirtió en un museo de tareas atascadas.

La solución no fue heroica. Documentaron que “driver local” aún puede ser almacenamiento remoto, añadieron una comprobación preflight en pipelines de despliegue
para verificar el tipo de montaje con findmnt, y separaron servicios críticos de NFS de nodos que no podían alcanzar la VLAN de almacenamiento.
El mayor cambio fue cultural: almacenamiento dejó de ser “problema de otro” en el momento en que los contenedores entraron en escena.

2) Optimización que salió mal: “Bajamos timeouts para que los fallos fallen rápido”

Otra organización tenía un problema intermitente: las apps se quedaban colgadas cuando NFS fallaba momentáneamente. Alguien propuso un cambio “simple”:
pasar a soft, bajar timeo y subir retrans para que el cliente se rindiera rápido y la app lo manejara.
En un ticket parecía razonable, porque todo parece razonable en un ticket.

En la práctica, las aplicaciones no estaban diseñadas para manejar EIO a mitad de flujo.
Un worker en background escribía a un archivo temporal y luego lo renombraba en su lugar. Con montajes soft y timeouts bajos,
la escritura a veces fallaba pero el flujo no siempre propagaba el error. El rename ocurrió con contenido parcial.
Tareas posteriores procesaron basura.

El incidente no fue una caída limpia; fue peor. El sistema permaneció “arriba” mientras producía resultados incorrectos.
Eso desencadenó una respuesta en cámara lenta: rollback, reprocesar, auditar salidas. Eventualmente se revirtieron las opciones de montaje.
Luego arreglaron el problema real: pérdida de paquetes intermitente por un bond LACP mal configurado y un mismatch de MTU que solo aparecía bajo carga.

El takeaway que escribieron en su runbook fue dolorosamente exacto: “Fail fast” es genial cuando el fallo se expone de forma fiable.
Los montajes soft hicieron el fallo más fácil de ignorar, no más fácil de gestionar.

3) Práctica aburrida pero correcta que salvó el día: pre-mount + automount + dependencias explícitas

Una firma financiera ejecutaba trabajos batch stateful en contenedores, escribiendo artefactos en NFS.
Tenían una regla sobria: montajes NFS los gestiona systemd, no la creación de volúmenes de Docker en runtime.
Cada montaje tenía una unidad automount, un timeout definido y dependencia de network-online.target.

Una mañana, un ciclo rutinario de reboot afectó un nodo mientras parchaban el NAS. El NAS era alcanzable pero lento por unos minutos.
Los contenedores arrancaron, pero sus rutas respaldadas por NFS fueron automontadas solo cuando se necesitaron. El intento de automount esperó y luego tuvo éxito cuando el NAS se recuperó.
Los jobs empezaron un poco tarde, y nadie se despertó.

La diferencia no fue mejor hardware. Fue que el ciclo de vida del montaje no estaba acoplado al ciclo de vida del contenedor.
Docker no decidió cuándo ocurrió el montaje, y los fallos eran visibles a nivel sistema con logs claros.

Ese es el tipo de práctica que los ejecutivos rara vez elogian porque no pasó nada. También es la práctica que te mantiene empleado.

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

1) Síntoma: los contenedores “se congelan” y no se detienen; docker stop cuelga

Causa raíz: montaje NFS en hard atascado; procesos en estado D esperando I/O del kernel.

Corrección: restaurar conectividad/salud del servidor; no esperes que las señales funcionen. Si debes recuperar un nodo, desmonta tras la recuperación del servidor, o reinicia el host como último recurso. Prevén recurrencias con red estable y timeo/retrans sensatos.

2) Síntoma: “funciona en un nodo, falla en otro”

Causa raíz: diferencias por nodo en routing/firewall/MTU, o agotamiento de conntrack en un subconjunto de nodos.

Corrección: compara ip route, ip -s link y reglas de firewall. Valida MTU con pings DF. Asegura configuración de red idéntica en la flota.

3) Síntoma: montajes fallan en boot o justo después de reinicio del host

Causa raíz: intentos de montaje compiten con la preparación de la red; Docker inicia contenedores antes de que network-online sea verdadero.

Corrección: gestiona montajes vía unidades systemd con ordering explícito, o usa automount. Evita montajes bajo demanda iniciados por el arranque de contenedores.

4) Síntoma: “permission denied” intermitente o problemas de identidad extraños

Causa raíz: mismatch de UID/GID, root-squash, o problemas de idmapping de NFSv4. Los contenedores empeoran esto porque los espacios de usuario y usuarios de imagen varían.

Corrección: estandariza UID/GID para quienes escriben, valida opciones de export en el servidor, y para NFSv4 confirma la configuración de idmapping. No lo tapes con 0777; eso no es estabilidad, es rendición.

5) Síntoma: frecuentes “stale file handle” tras failover del NAS o mantenimiento del export

Causa raíz: el mapeo de file handles en el servidor cambió; los clientes mantienen referencias que ya no resuelven.

Corrección: evita mover/re-escribir exports bajo los clientes; usa rutas estables. Para recuperación, remonta y reinicia las cargas afectadas. Arquitectónicamente, prefiere métodos HA estables soportados por tu NAS y versión NFS, y prueba failover con clientes reales.

6) Síntoma: fallos “aleatorios” de montaje solo en redes aseguradas

Causa raíz: puertos dinámicos de NFSv3 bloqueados; rpcbind/mountd/lockd no permitidos por firewall/security groups.

Corrección: migra a NFSv4 cuando sea posible. Si estás atado a v3, fija puertos de los daemons en el servidor y ábrelos intencionalmente—luego documéntalo para que la próxima persona no “optimice” tu firewall.

7) Síntoma: picos de latencia altos, luego recuperación, repitiéndose bajo carga

Causa raíz: latencia de disco del servidor (rebuild/snapshot), saturación de hilos NFS o colas de red congestionadas.

Corrección: mide latencia I/O del servidor e hilos de servicio NFS; arregla el cuello de botella. Opciones cliente como rsize/wsize no salvarán un array saturado.

8) Síntoma: cambiar a soft “arregla” los cuelgues pero introduce problemas de datos misteriosos

Causa raíz: montajes soft convierten outages en errores de I/O; las aplicaciones manejan mal fallos parciales.

Corrección: revierte a hard para escrituras stateful, arregla la conectividad subyacente y actualiza aplicaciones para manejar errores cuando corresponda.

Listas de comprobación / plan paso a paso

Paso a paso: estabilizar un despliegue NFS Docker existente

  1. Inventaría montajes con findmnt -t nfs,nfs4. Anota vers, proto, hard/soft, timeo, retrans, y si usaste hostnames.
  2. Confirma la realidad con nfsstat -m. Si Docker dice una cosa y el kernel otra, confía en el kernel.
  3. Decide protocolo: preferir NFSv4.1+. Si estás en v3, lista dependencias de firewall y casos de fallo que no toleras.
  4. Arregla la red antes de ajustar: valida MTU extremo a extremo; elimina drops en interfaces; verifica simetría de enrutamiento; asegura estabilidad del puerto 2049.
  5. Elige semánticas de montaje:
    • Escrituras con estado: hard, timeo moderado, retrans relativamente bajo, TCP.
    • Solo lectura/cache: considera soft solo si tu app maneja EIO y aceptas “error en vez de colgar”.
  6. Haz montajes predecibles: pre-monta vía systemd o usa automount. Evita montajes en runtime iniciados por el arranque de contenedores cuando sea posible.
  7. Prueba fallos: desconecta la red del servidor (en laboratorio), reinicia un cliente, hace flapping a una ruta y observa. Si tu prueba es “esperar y confiar”, no estás probando.
  8. Operacionaliza: añade dashboards para retransmits, drops de interfaz, saturación de hilos NFS del servidor y latencia de disco. Añade un runbook de on-call que empiece con la Guía de diagnóstico rápido arriba.

Checklist corta para opciones de montaje (estabilidad primero)

  • Usa TCP: proto=tcp
  • Prefiere NFSv4.1+: vers=4.1 (o 4.2 si está soportado)
  • Corrección primero: hard
  • No sobre-afines: comienza con timeo=600, retrans=2 y ajusta solo con evidencia
  • Reduce churn de metadata: noatime para cargas típicas
  • Sé cauteloso con actimeo y similares; caché no es “rendimiento gratis”
  • Considera nconnect solo después de medir capacidad del servidor y del firewall

Preguntas frecuentes

1) ¿Debo usar NFSv3 o NFSv4 para volúmenes Docker?

Usa NFSv4.1+ salvo que tengas una razón de compatibilidad específica. En redes con muchos contenedores, menos daemons auxiliares y puertos suelen significar menos fallos “aleatorios” de montaje.

2) ¿Es soft aceptable alguna vez?

Sí—para datos de solo lectura o tipo cache donde un error de I/O es preferible a un colgado, y donde tu aplicación está diseñada para tratar EIO como normal. Para escrituras stateful es una mina con bandera roja.

3) ¿Por qué docker stop cuelga cuando NFS está caído?

Porque los procesos están bloqueados en I/O del kernel sobre un filesystem montado en hard. Las señales no pueden interrumpir un hilo en sueño no interrumpible. Arregla la alcanzabilidad del montaje.

4) ¿Qué hacen realmente timeo y retrans?

Gobernan el comportamiento de reintento RPC. timeo es el timeout base para un RPC; retrans es cuántos reintentos ocurren antes de que el cliente informe eventos de “not responding” (y para montajes soft, antes de fallar la I/O).

5) ¿Debo tunear rsize y wsize a valores enormes?

No por superstición. Los defaults modernos suelen ser buenos. Valores sobredimensionados pueden interactuar mal con MTU, límites del servidor o drops de red. Afina solo tras medir throughput y retransmits.

6) ¿Usar una IP en vez de un hostname ayuda?

Puede. Si DNS es inestable, lento o cambia inesperadamente, usar una IP evita la resolución de nombres como dependencia de fallo. La compensación es perder migración de servidor fácil a menos que gestiones la IP como endpoint estable.

7) ¿Qué causa “stale file handle” y cómo lo prevengo?

Normalmente lo causa un cambio en el servidor que invalida file handles: mover la ruta del export, comportamiento de failover o cambios en el filesystem bajo el export. Prevénlo manteniendo exports estables y usando métodos HA soportados por tu NAS, probándolos con clientes reales.

8) ¿Debo montar vía volúmenes Docker o pre-montar en el host?

Pre-montar (mounts systemd/automount) suele ser más predecible y más fácil de depurar. Montar desde Docker funciona, pero acopla el ciclo de vida del montaje al del contenedor, que no es donde quieres centrar tu historia de fiabilidad.

9) ¿Qué hay de nolock para arreglar cuelgues?

Evítalo a menos que estés absolutamente seguro de que tu carga no depende de locks. Puede “arreglar” problemas relacionados con lockd en NFSv3 deshabilitando locking, pero eso intercambia outages por bugs de corrección.

10) Si mi servidor NFS está bien, ¿por qué solo algunos clientes ven timeouts?

Porque “el servidor está bien” suele ser atajo para “respondió a un ping”. Problemas locales del cliente como mismatch de MTU, enrutamiento asimétrico, límites de conntrack y drops de NIC pueden romper selectivamente NFS mientras otro tráfico parece mayormente OK.

Conclusión: siguientes pasos que reducen alertas

Si luchas con tiempos de espera de volúmenes NFS en Docker, no empieces a tornear timeo como si fuera una radio.
Empieza por nombrar el fallo: ruta de red, saturación del servidor, fricción por versión de protocolo u ordering de orquestación.
Luego toma una decisión deliberada sobre semánticas: montajes correctness-first hard para escrituras stateful, y solo soft scoped y cuidadosamente para datos desechables.

Pasos prácticos que puedes hacer esta semana:

  1. Audita cada host Docker con findmnt y nfsstat -m; registra opciones reales y versión NFS.
  2. Estandariza en NFSv4.1+ sobre TCP salvo que tengas una razón en contrario.
  3. Arregla MTU y contadores de drops antes de cambiar el tuning de montajes.
  4. Mueve montajes críticos a montajes gestionados por systemd (idealmente automount) con ordering explícito network-online.
  5. Escribe un runbook basado en la Guía de diagnóstico rápido, y practícalo una vez mientras está tranquilo.

El objetivo final no es “NFS nunca falle”. El objetivo es: cuando falle, que se comporte de manera predecible, se recupere limpiamente y no convierta tus contenedores en arte moderno.

← Anterior
ZFS ZVOL vs Dataset: La decisión que cambia tu dolor futuro
Siguiente →
VoIP sobre VPN: Evita audio robótico con MTU, jitter y fundamentos de QoS

Deja un comentario