↓ Ir al contenido

Recuperación automática de almacenamiento NFS en Docker Swarm con healthchecks

Rogelio Guerra Riverón
Autor
Rogelio Guerra Riverón
Construyendo mi propia infraestructura web desde cero. Aquí documento cada paso: servidores, redes, contenedores y lo que vaya surgiendo.

El problema
#

Llevo meses corriendo Docker Swarm con almacenamiento NFS compartido entre nodos. Todo funciona bien hasta que algo falla. Un reinicio de red, un problema temporal con el servidor NFS, o un cable suelto. De repente tienes contenedores corriendo con puntos de montaje fantasma. Leen datos obsoletos. Escriben en el vacío. Es un desastre silencioso.

Necesitaba detección automática y recuperación sin intervención manual.

Solución: healthchecks + script de validación
#

La idea es simple. Un healthcheck valida constantemente que el punto de montaje NFS está realmente conectado. Si falla, Docker reinicia el contenedor. Al reiniciarse, el sistema intenta remontarlo. Si sigue caído, el contenedor no llega a ejecutarse.

1. Script de validación
#

Creé un script que verifica que el NFS no solo existe, sino que responde:

#!/bin/bash
# /usr/local/bin/check-nfs-mount.sh

MOUNT_POINT="${1:-/mnt/nfs}"
TIMEOUT=5

# Verificar que el punto existe
if [ ! -d "$MOUNT_POINT" ]; then
    echo "CRITICAL: Mount point $MOUNT_POINT does not exist"
    exit 2
fi

# Intentar crear y eliminar un archivo de prueba
TEST_FILE="$MOUNT_POINT/.nfs-healthcheck-$$"

# Timeout para detectar NFS colgado
timeout $TIMEOUT touch "$TEST_FILE" 2>/dev/null
if [ $? -ne 0 ]; then
    echo "CRITICAL: Cannot write to $MOUNT_POINT (timeout or permission denied)"
    exit 2
fi

rm -f "$TEST_FILE"
echo "OK: NFS mount $MOUNT_POINT is responsive"
exit 0

Esto es importante: no solo verifico que el directorio existe. Intento escribir con timeout. Si el NFS se cuelga sin desconectarse, touch se queda esperando. El timeout lo mata y detectamos el problema.

2. Dockerfile con healthcheck
#

FROM ubuntu:24.04

RUN apt-get update && apt-get install -y \
    nfs-common \
    curl \
    && rm -rf /var/lib/apt/lists/*

COPY check-nfs-mount.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/check-nfs-mount.sh

# El healthcheck
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=10s \
    CMD /usr/local/bin/check-nfs-mount.sh /data

VOLUME /data
CMD ["your-app-command"]

Los parámetros importan:

  • --interval=30s: comprueba cada 30 segundos
  • --timeout=10s: mata el check si tarda más de 10
  • --retries=3: reinicia tras 3 fallos consecutivos
  • --start-period=10s: espera 10s antes de activar (permite montaje inicial)

3. Docker Compose para Swarm
#

version: '3.9'

services:
  mi-app:
    image: mi-imagen:latest
    deploy:
      mode: replicated
      replicas: 2
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    volumes:
      - type: volume
        source: nfs-data
        target: /data
        volume:
          nocopy: true

volumes:
  nfs-data:
    driver: local
    driver_opts:
      type: nfs
      o: addr=servidor-nfs,vers=4,soft,timeo=10,retrans=3
      device: ":/exports/compartida"

Nota el soft,timeo=10,retrans=3. Así el NFS no se queda esperando indefinidamente. Después de timeouts reintentos, falla rápido. El healthcheck lo detecta.

4. Monitoreo
#

Verifico el estado en tiempo real:

docker service ps mi-app --no-trunc
docker inspect $(docker ps -q -f label=com.docker.swarm.service.name=mi-app) | grep -A 5 Health

Resultados
#

En producción, esto detecta desconexiones en segundos. El servicio se reinicia automáticamente. La mayoría de veces el NFS vuelve a estar disponible y no hay pérdida de datos.

Para problemas persistentes, Swarm reintenta según max_attempts. Si sigue fallando, tienes alertas en los logs.

No es perfecto, pero es mucho mejor que descubrir la desconexión porque tu aplicación crasheó de forma silenciosa.