A día de hoy el sistema de alta disponibilidad más utilizado para SQL Server sigue siendo aún Failover Clustering. Uno de los componentes críticos para el buen funcionamiento del clúster es el mantener unas comunicaciones entre los nodos de calidad. Concretamente con calidad nos referimos a que no tengamos cortes, descartes de paquetes o latencias excesivas para el tráfico de heartbeat.
Por esta razón las buenas prácticas “obsoletas” (https://support.microsoft.com/en-us/help/258750/recommended-private-heartbeat-configuration-on-a-cluster-server) son las de dedicar un interfaz de red específico y dedicado para el heartbeat de forma que no tengamos interferencia alguna. A día de hoy (Windows Server 2008+) esta recomendación ya no es tal, siendo la recomendación oficial de Microsoft que habilitemos el tráfico de heartbeat en todos los interfaces por los que los nodos se puedan comunicar.
Sin embargo, esto no quiere decir que debamos descuidar la configuración, ya que con el uso de virtualización, de Live Migration de VMs, de almacenamiento CSV (cluster shared volumes), del uso de iSCSI, de los teamings de red, de chasis en formato blade, los entornos hyperconvergentes, etc. las exigencias de la red aumentan y se complica evitar problemas de “abusos”. Os recomiendo este video para quien quiera tener una mejor visión global sobre todos estos temas: https://channel9.msdn.com/Events/TechEd/NorthAmerica/2013/MDC-B337#fbid=ZpvM0cLRvyX
Por centrarnos un poco, en este post vamos a jugar con un Cluster sobre Windows Server 2008 R2 con 6 nodos. El comportamiento es bastante similar en nuevas versiones así que la problemática que vamos a mostrar es aplicable también a versiones más modernas:
Como podemos ver tenemos 3 interfaces de red, una red interna, otra dedicada a iSCSI y otra para el acceso externo. Por defecto tenemos permitida la comunicación del cluster por cada uno de los interfaces:
Esto haría que si fallara cualquiera de ellas la comunicación del cluster pudiera fluir a través de cualquiera de los otros interfaces. Vamos a configurar las tarjetas de forma que únicamente podamos utilizar el interfaz “Interna” para la comunicación del cluster y el resto para acceso de clientes únicamente. De esta forma si algo le ocurre a este interfaz, la comunicación del cluster podría verse comprometida:
Debido a que estamos hablando de máquinas virtuales donde el interfaz de red virtual es de 10 Gbps es muy complicado crear un entorno de saturación con pérdida de paquetes UDP por congestión. Por tanto, vamos a habilitar una funcionalidad incluida en Hyper-V que nos permite controlar el ancho de banda de una máquina virtual. Concretamente vamos a intentar limitar el ancho de banda del nodo1 a 1 Mbps:
Obtendremos un error poco clarificador pero que realmente lo que significa es que no podemos limitar el ancho de banda a menos de 10 Mbps:
Cambiaremos el valor mínimo y máximo a 10 Mbps por tanto:
Comprobaremos que efectivamente el limitador está funcionando correctamente realizando una copia de ficheros por red del nodo1 al nodo2 a través de la red interna. La velocidad alcanzada de 1 MB/s corresponde aproximadamente con el rendimiento que podemos esperar al copiar ficheros cuando tenemos un canal de 10 Mbps bruto a nivel de interfaz de red:
A continuación vamos a intentar saturar el canal de comunicaciones hasta un límite suficiente tras el cual algunos paquetes UDP se pierdan. Para ello instalaremos las herramientas Nmap y Nping:
Si no la teniamos instalada previamente deberemos instalar también la librería WinPcap ya que es una dependencia necesaria para el funcionamiento de Nmap y Nping:
Una vez tengamos instalado este software en el nodo1 y el nodo2 vamos a probar de arrancar el Nping en modo servidor en el nodo1 y en modo cliente en el nodo2. Utilizaremos un puerto UDP arbitrario que esté abierto a nivel de firewall (o lo abriremos si no lo está):
A continuación, lo que haremos es cambiar el cliente para que trabaje en modo stress. Para ello lo que haremos es fijar un tiempo de espera mínimo (1ms), un alto objetivo de paquetes por segundo , un tamaño de paquete elevado y un número elevado de iteraciones:
Una vez lanzada la carga encontramos que el Nping en modo servidor nos ejercía de cuello de botella dando las respuestas de las peticiones. Así que lo que hicimos fue añadir más cores a la máquina virtual y crear 10 servidores Nping en distintos puertos y atacarlos con varios clientes cada uno contra los distintos puertos:
Finalmente insistiendo con en el “ataque” desde múltiples sesiones y manteniendo la limitación de ancho de banda que configuramos activa conseguimos que el nodo1 sea excluido del cluster al sufrir pérdidas de heartbeats:
En cuanto se detiene el “ataque” el servicio de cluster consigue arrancar y volverse a comunicar con el resto de nodos volviendo a unir al cluster. Por tanto, aunque no se necesario dedicar un único interfaz de red dedicado para el heartbeat sí sigue siendo una buena práctica el poner todos los mecanismos posibles para conseguir que el tráfico de heartbeat sea tratado con más mimo si cabe, con redundancia asegurada (teams, múltiples nics), priorizado (QoS) y aislado del resto de tráfico (VLANs). Os dejo este otro enlace con más detalles al respecto: https://technet.microsoft.com/en-us/library/dn550728(v=ws.11).aspx
En aquellos casos donde por razones extremas como por ejemplo la mala calidad de un radio enlace o bugs/microcortes causados por firewalls/VPNs y que escapan a nuestro control podemos considerar reducir la “sensibilidad” a esta pérdida de paquetes. Para ello la recomendación es aumentar el número de heartbeats (no aumentar la frecuencia de testeo) que tienen que perderse antes de considerarse la conectividad perdida.
El valor por defecto más habitual que tendremos configurado en nuestro cluster es de 5 heartbeats y una frecuencia de testeo de 1000ms. En versiones de operativo más modernas (Windows Server 2016) este valor ya está aumentado “de serie” a 10 dentro de la misma subnet y a 20 cuando cruzamos distintas subnets. También a partir de Windows Server 2012 R2 si se instaló el rol de Hyper-V en el cluster automáticamente tendremos los umbrales incrementados a 10 y 20. Los comandos para realizar estos cambios los podéis consultar con detalle aquí: https://blogs.msdn.microsoft.com/clustering/2012/11/21/tuning-failover-cluster-network-thresholds
Debemos ser conscientes es que si aumentamos mucho este valor tendremos dos problemas, el primero que podemos estar tapando un problema de comunicaciones y que se estén degradando más y más con el tiempo. El segundo que cuando realmente no sea un “falso positivo” el tiempo necesario para que el mecanismo de alta disponibilidad del cluster actúe se verá incrementado con lo que los tiempos de recovery serán mayores. También hay veces que los problemas pueden venir por un driver buggy o algún parámetro que necesitemos ajustar a nivel de virtualización/driver. Por ejemplo puede ser necesario incrementar en algunos casos los buffers de recepción, a nivel de la tarjeta de red:
O bien tener que configurar un parámetro relacionado de la tarjeta virtual a nivel del hypervisor (https://blogs.technet.microsoft.com/askcore/2013/06/03/nodes-being-removed-from-failover-cluster-membership-on-vmware-esx/ y https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2039495)
La conclusión final de este post es que aunque un cluster de Windows actualmente se pueda configurar y pueda funcionar con un único interfaz de red, sin redundancia alguna, etc. no es para nada una práctica recomendable. También vemos una tendencia a confiar excesivamente en mecanismos de virtualización, de teaming, QoS “automáticos/mágicos”, etc. para evitar realizar una configuración de red más elaborada donde todo el tráfico no comparta el mismo interfaz (lógico y físico). El no hacer esto es fácil que genere problemas cuando se realizan operaciones sobre la red que requieren throughput masivo (discos iSCSI, snapshots de VM, Live Migrations, backups, operaciones Bulk en SQL/ SSIS, etc.) a la vez que operaciones sensibles a la latencia (heartbeat, escrituras en logs, grupos de disponibilidad síncronos, etc.) siendo normalmente estas últimas las más perjudicadas proporcionalmente y las que acaban sufriendo más las consecuencias.