Investigando filesystems NFS que no se montan al arranque

Hoy me he pasado buena parte del día intentando averiguar por qué un filesystem NFS, aparentemente bien configurado, no era montado cuando se iniciaba el servidor. Los mensajes de arranque decían :

Bringing up loopback interface: [ OK ]  
Bringing up interface eth0: [ OK ]  
Bringing up interface eth1: [ OK ]  
Starting auditd: [ OK ]  
Starting system logger: [ OK ]  
Starting kernel logger: [ OK ]  
Starting irqbalance: [ OK ]  
Starting portmap: [ OK ]  
Starting NFS statd: [ OK ]  
Starting system message bus: [ OK ]  
Mounting NFS filesystems: mount: mount to NFS server 'ServidorNFS'
failed: System Error: No route to host.  
[FAILED]  
Mounting other filesystems: [ OK ]  
Starting snmpd: [ OK ]

La parte del 'no route to host' es particularmente divertida porque ambos hosts (servidor y cliente) están en el mismo segmento de red; obviamente no hay ningún firewall que filtre tráfico, la configuración de red del cliente es correcta (se levantan los interfaces con las IPs, máscaras y rutas correctas) y la configuración de /etc/fstab también estaba OK (una vez arrancado el servidor, un simple mount -a montaba todos los filesystems NFS sin problemas ni warnings.

Todo esto parecía llevar una conclusión del tipo "parece que se intenta montar el NFS antes de que la red esté inicializada (aunque la secuencia de arranque diga que lo está)". Investigando un poco por la KB de Red Hat encontré esta nota indicando que los drivers de Broadcom (bnx2) a veces presentaban este problema de declararse inicializados pero no procesar tráfico.

Visto esto la solución más simple era intentar poner algún tipo de retardo en la inicialización de la red. Leyendo el script de inicialización de red /sbin/ifup vi que llamaba a /etc/sysconfig/network-scripts/ifup-eth y finalmente a /etc/sysconfig/network-scripts/ifup-post , con una llamada del tipo :

if [ -x /sbin/ifup-local ]; then  
/sbin/ifup-local ${DEVICE}

¡Eureka! Bastaría con crear un fichero /sbin/ifup-local con el código de retardo deseado para evitar el problema. Dicho y hecho:

#! /bin/bash
iface="eth1"  
host="ServidorNFS"  
siesta=5
# Do not edit beyond this line.
timer=0
if [ $1 == "$iface" ]; then  
    while [ `ping -s 1 -c 1 -q $host > /dev/null ; echo $?` != 0 ]; do  
        msg="=== This is /sbin/ifup-local . Network interface "$iface" is not up yet (or cannot ping $host). Sleeping siesta for $siesta secs ($timer)"  
        /bin/echo $msg  
        /bin/logger $msg  
        timer=`/usr/bin/expr $timer + $siesta`  
        if [ $timer -gt 120 ]; then  
            msg="=== This is $0 . Network interface "$iface" is not up. Giving up with network initialization :·( "  
            /bin/echo $msg  
            /bin/logger $msg  
            exit 1  
        fi  
    sleep $siesta  
    done  
fi

Por supuesto, lo mejor de ese código es la parte del sleep $siesta ;-) . Probado esto y reiniciado el servidor varias veces, en todas ellas se montaba correctamente el FS NFS al arranque y, sin embargo, había algo que no me acababa de cuadrar... ¿Por qué pasaba esto aquí y con otros servidores del mismo fabricante/modelo/OS no pasaba?

Dado que la parte del servidor es básicamente la misma, lo mejor es seguir el cable Ethernet a ver qué hay al otro lado :-) . En mi caso un Catalyst 3560 Gigabit, así que me puse a mirar sus logs cada vez que se (re)inicializaba el interfaz afectado.

Del lado del servidor :

root@clienteNFS ~ # ifdown eth1 ; ifup eth1  
=== This is /sbin/ifup-local . Network interface "eth1" is not up yet (or cannot ping ServidorNFS). Sleeping siesta for 5 secs (0)  
=== This is /sbin/ifup-local . Network interface "eth1" is not up yet (or cannot ping ServidorNFS). Sleeping siesta for 5 secs (5)  
=== This is /sbin/ifup-local . Network interface "eth1" is not up yet (or cannot ping ServidorNFS). Sleeping siesta for 5 secs (10)  
=== This is /sbin/ifup-local . Network interface "eth1" is not up yet (or cannot ping ServidorNFS). Sleeping siesta for 5 secs (15)  
root@clienteNFS ~ #

Es decir, el lado del servidor tardaba 25 segundos mínimo en volver a procesar tráfico. Mirando el lado del switch :

001458: Nov 30 16:49:15.144 CET: %SPANTREE-6-PORT_STATE: Port Gi0/7 instance 98 moving from forwarding to disabled  
001459: Nov 30 16:49:15.144 CET: %SPANTREE-7-PORTDEL_SUCCESS: GigabitEthernet0/7 deleted from Vlan 98  
001460: Nov 30 16:49:16.151 CET: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/7, changed state to down  
001461: Nov 30 16:49:17.149 CET: %LINK-3-UPDOWN: Interface GigabitEthernet0/7, changed state to down  
001462: Nov 30 16:49:17.703 CET: %SPANTREE-6-PORT_STATE: Port Gi0/7 instance 98 moving from disabled to blocking  
001463: Nov 30 16:49:17.703 CET: %SPANTREE-6-PORT_STATE: Port Gi0/7 instance 98 moving from blocking to listening  
001464: Nov 30 16:49:19.699 CET: %LINK-3-UPDOWN: Interface GigabitEthernet0/7, changed state to up  
001465: Nov 30 16:49:20.706 CET: %LINEPROTO-5-UPDOWN: Line protocol on Interface GigabitEthernet0/7, changed state to up  
001466: Nov 30 16:49:32.710 CET: %SPANTREE-6-PORT_STATE: Port Gi0/7 instance 98 moving from listening to learning  
001467: Nov 30 16:49:47.717 CET: %SPANTREE-6-PORT_STATE: Port Gi0/7 instance 98 moving from learning to forwarding

Casualmente hay unos 20+ segundos entre el paso del puerto del estado Listening a Forwarding. ¿A qué me suena esto? ¡Pues claro! A Spanning Tree. Es decir, el switch está configurado para, en cuanto se gana enlace en uno de sus puertos, ponerse a la escucha por si hubiera un switch al otro lado y evitar crear bucles en la comunicación entre switches.

Dado que en el puerto elegido siempre va a haber un servidor conectado, no tiene sentido que el switch se ponga a la escucha de posibles notificaciones BPDU. Una vez configurado el puerto del switch en modo portfast, la inicialización de la red es inmediata del lado del servidor y el switch procesa paquetes inmediatamente, por lo que se soluciona el problema.

¿Por qué esto pasaba con algunos servidores sí y otros no? Pues porque la persona que instaló los switches en ambos casos era distinta. Corolario: La culpa siempre es del departamento de Networking ;-)