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 (
Dstate). 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
- En el host Docker, intenta un simple
statolsen la ruta montada. Si cuelga, no es tu app. Es el montaje. - Revisa
dmesgpor 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
- 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.
- 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
- 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.
- Confirma
hard,timeo,retrans,tcp, y si usasteintr(comportamiento obsoleto) u otras flags legacy.
Cuarto: inspecciona logs y saturación en el servidor
- El load average del servidor no basta. Mira hilos NFS, latencia de disco y caídas de red.
- 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.
Opciones de montaje que mejoran la estabilidad (haz esto, no adivinanzas)
“Mejores” opciones de montaje dependen de si prefieres corrección o tiempo de actividad cuando la red se porta mal.
Para la mayoría de sistemas de producción con escrituras stateful, tiendo hacia la corrección: montajes hard,
timeouts conservadores y elecciones de protocolo que reduzcan piezas móviles.
Base: lo que desplegaría para volúmenes NFS de uso general en Docker
Usa NFSv4.1+ cuando puedas. Usa TCP. Evita opciones que “hagan desaparecer errores” devolviendo éxito prematuramente.
La estabilidad tiene más que ver con comportamiento de fallo predecible.
- Preferir:
vers=4.1(o 4.2 si es compatible),proto=tcp,hard,timeo=600,retrans=2,noatime - Considerar:
nconnect=4(cliente Linux) para throughput y algo de resiliencia,rsize/wsizesolo si tienes evidencia - Evitar por defecto:
soft,nolock(a menos que entiendas realmente los requisitos de locking), ajustes agresivos detimeo, y UDP
Hard vs soft: haz explícita la compensación
hard significa que las llamadas al sistema pueden bloquearse hasta que el servidor responda, potencialmente para siempre. Esto te protege de pérdida silenciosa de datos.
También significa que tu proceso puede colgar cuando el servidor es inalcanzable. Es una ventaja y una responsabilidad.
soft significa que el kernel devuelve un error después de reintentos. Esto es tentador porque “desatasca” tus contenedores.
También fomenta escrituras parciales, salidas corruptas y aplicaciones que no manejan EIO correctamente (la mayoría no lo hace).
Si eliges soft, úsalo para cargas de solo lectura o tipo cache, y trata los errores como esperados.
Para bases de datos, colas y cualquier cosa con garantías de durabilidad: no lo uses.
Elige NFSv4 cuando puedas, especialmente en plataformas con contenedores
NFSv3 requiere rpcbind y mountd para la negociación inicial del montaje, además de lockd/statd para locking. Son dependencias y puertos extra.
En redes complejas—firewalls, redes overlay, NAT, security groups—eso son más maneras de fallar.
NFSv4 consolida gran parte de eso en el puerto 2049 y un protocolo stateful. Esa statefulness puede introducir sus propios problemas,
pero en flotas reales de contenedores suele reducir los fallos “aleatorios de montaje”.
timeo y retrans: deja de tratarlos como números mágicos
timeo es el timeout base de RPC (en décimas de segundo para muchos montajes). El cliente realiza backoff.
retrans es el número de veces que reintentar un RPC antes de informar un evento “not responding” (para soft) o continuar reintentando (para hard).
Una postura razonable de estabilidad:
- No los pongas demasiado bajos. Timeouts mínimos amplifican jitter transitorio en outages.
- No los pongas demasiado altos sin pensar. Timeouts enormes pueden ocultar fallos reales por demasiado tiempo y retrasar el failover a niveles superiores.
- Retrans más bajo, timeo moderado suele funcionar: fallar “lo suficientemente rápido” a nivel RPC, pero seguir reintentando de forma predecible a nivel de montaje.
nconnect: una herramienta moderna con aristas
nconnect crea múltiples conexiones TCP al servidor NFS para un solo montaje. Esto puede mejorar throughput y reducir head-of-line blocking.
También puede aumentar la carga en el servidor, exponer límites de firewall/conntrack y hacer la depuración más “divertida” porque hay más de un flujo.
Úsalo cuando tengas evidencia de saturación por conexión única, y después de validar la capacidad del servidor y las tablas de estado de la red.
Si tu problema son timeouts por pérdida de paquetes o sobrecarga del servidor, nconnect puede empeorarlo.
Opciones de locking: no desactives locks para detener timeouts
nolock es una “solución” común que intercambia timeouts por bugs de corrección. Puede ayudar con algunos problemas de lockd en NFSv3,
pero también rompe aplicaciones que dependen de locks POSIX o patrones cooperativos de bloqueo.
En flotas de contenedores rara vez existe memoria institucional para saber quién depende de locking. Así que no lo uses a la ligera.
Caché de atributos y consistencia: elige tu veneno conscientemente
Opciones como actimeo, acregmin, acregmax, acdirmin, acdirmax y nocto afectan la rapidez con que los clientes notan cambios.
Caché agresiva puede reducir tráfico de metadata y mejorar rendimiento.
También puede hacer que tu app piense que un archivo no existe (todavía) o que sigue siendo la versión antigua.
Para cargas con escrituras compartidas, mantén la caché conservadora.
Para activos mayormente de lectura, puedes aumentar la caché, pero confirma que tu patrón de despliegue no tropezará con “visibilidad retrasada”.
Cuándo considerar bg, automount y ordering con systemd
Muchos “tiempos de espera” de NFS en Docker no ocurren en runtime; son problemas de ordering en el arranque/inicio.
Tu host arranca, Docker se inicia, los contenedores se inician y luego la pila de red termina de negociar rutas.
Los montajes NFS que ocurren en esta ventana se comportan mal.
Una solución práctica es usar unidades automount de systemd o al menos asegurar que los montajes requieran network-online.
Docker iniciará contenedores y los bloqueará en I/O; quieres que el montaje esté listo antes de que las cargas dependan de él.
Broma #2: La manera más fácil de reducir los timeouts de NFS es dejar de llamarlos “timeouts” y empezar a llamarlos “oportunidades para avanzar en tu carrera”.
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
- Inventaría montajes con
findmnt -t nfs,nfs4. Anotavers,proto,hard/soft,timeo,retrans, y si usaste hostnames. - Confirma la realidad con
nfsstat -m. Si Docker dice una cosa y el kernel otra, confía en el kernel. - Decide protocolo: preferir NFSv4.1+. Si estás en v3, lista dependencias de firewall y casos de fallo que no toleras.
- 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.
- Elige semánticas de montaje:
- Escrituras con estado:
hard,timeomoderado,retransrelativamente bajo, TCP. - Solo lectura/cache: considera
softsolo si tu app manejaEIOy aceptas “error en vez de colgar”.
- Escrituras con estado:
- Haz montajes predecibles: pre-monta vía systemd o usa automount. Evita montajes en runtime iniciados por el arranque de contenedores cuando sea posible.
- 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.
- 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=2y ajusta solo con evidencia - Reduce churn de metadata:
noatimepara cargas típicas - Sé cauteloso con
actimeoy similares; caché no es “rendimiento gratis” - Considera
nconnectsolo 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:
- Audita cada host Docker con
findmntynfsstat -m; registra opciones reales y versión NFS. - Estandariza en NFSv4.1+ sobre TCP salvo que tengas una razón en contrario.
- Arregla MTU y contadores de drops antes de cambiar el tuning de montajes.
- Mueve montajes críticos a montajes gestionados por systemd (idealmente automount) con ordering explícito network-online.
- 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.