Al trabajar con Kubernetes en Azure podemos encontrarnos con el error de SNAT Port Exhaustion, que básicamente consiste en que las aplicaciones que tenemos ejecutándose en Kubernetes, abren una gran cantidad de conexiones a Internet para acceder a otros recursos (ej: estáticos, un APIM, un Front Door, etc.), alcanzando el número máximo de conexiones salientes, quedando pendientes o incluso acabando en fallo, impactando en el rendimiento de nuestras aplicaciones, con una sensación que puede variar desde degradación a indisponibilidad, en función de la magnitud del problema, y de forma aleatoria o intermitente.
SNAT Port Exhaustion en Kubernetes (AKS) – El Diagnóstico
Al crear un Cluster de Kubernetes en Azure (AKS), se nos crea un Grupo de Recursos donde Azure crea los recursos que necesita para ofrecernos este servicios, desde las máquinas o Scale Sets hasta otros recursos como los Load Balancers o Public IPs (PIP).
El caso, es que por defecto, las conexiones salientes (a Internet) de nuestros contenedores saldrán a Internet a través del Load Balancer. Esto es algo que podemos comprobar fácilmente, arrancando un contenedor como el network-multitool (que viene con un montón de cositas ya instaladas, listo para trastear) y hacer un curl a una web cualquiera que nos devuelva la IP Pública por la que salimos a Internet. Esa IP Pública, será la que utiliza el Load Balancer de Kubernetes. A continuación se muestra un ejemplo.
kubectl run -it --rm aks-ip --image=praqma/network-multitool -n wamb -- /bin/bash
curl icanhazip.com
Este sería el resultado de ejecución, en un Cluster de Kubernetes de Azure (AKS). Super fácil 🙂
Si tenemos un problema de SNAT Port Exhaustion, empezaremos a experimentar lentitud en procesos que necesiten establecer conexiones salientes con ciertos recursos. Imagina que tienes un proceso que hace multitud de llamadas a una API, siempre la misma, y que no está gestionando bien las conexiones, produciendo este problema. Si nos conectamos a uno de sus contenedores y hacemos un curl a dicha API, veremos que tarda en conectar de forma aleatoria, a veces super rápido, otras veces decenas o incluso cientos de segundos. Un ejemplo de curl para depurar este problema, sería el siguiente:
curl -o /dev/null -s -w 'Total: %{time_total}s\n' "https://super-api.domain.com"
Pero la prueba del algodón, será acceder a las métricas del Load Balancer de Kubernetes, mostrar la métrica de SNAT Connection Count, filtrar por Status=failed, y agrupar por Backend IP (estas serán las IPs de los Nodos de nuestro Cluster de Kubernetes, ojo, que podemos sufrir SNAT Port Exhaustion en unos nodos, pero en otros no, y probablemente, un drain del Node sea pan para hoy y hambre para mañana, vamos que no nos sirva de mucho, incluso podríamos empeorar la situación). El caso es que el resultado tiene que ser cero patatero. Si no es así, si tienes una gráfica del estilo a la siguiente, tienes un problema de SNAT. Pero no pasa nada, se puede arreglar 🙂
SNAT Port Exhaustion en Kubernetes (AKS) – La Solución
Hay varias soluciones posibles, sin tocar el código de nuestras aplicaciones. En principio, la mejor solución es configurar un NAT Gateway en el Segmento de Red utilizado por Kubernetes, de tal modo, que a partir de entonces, todo el tráfico saliente a Internet de nuestros contenedores, dejará de salir a través del Load Balancer, y lo hará a través del NAT Gateway.
Además, por cada dirección IP Pública (PIP) del NAT Gateway podremos soportar hasta 65K conexiones salientes, pudiendo configurar hasta 16 PIPs, para poder soportar más y más conexiones simultáneas. De hecho, si ponemos varias IPs y ejecutamos el anterior curl sucesivas veces, veremos que cada vez sale por una PIP diferente, como si hiciera una especie de Round Robin.
La configuración del NAT Gateway implica, que si volvemos a revisar la gráfica anterior del Load Balancer, pues ya no veremos conexiones salientes ahí. En su lugar, tendremos que sacar esa gráfica, pero en el NAT Gateway (que es más eficiente), y de nuevo, comprobar las conexiones en estado failed. A continuación se muestra un ejemplo, y como aumentan las conexiones en el NAT Gateway a partir del momento en que lo configuramos en el Segmento de Red de Kubernetes, pero esta vez, con zero-failed (sin SNAT Port Exhaustion, como Dios manda). Fácil y sencillo.
Despedida y Cierre
Poco más que contar. Los problemas de SNAT en Azure son relativamente habituales, depende de cada caso, y no sólo aplica a Kubernetes, también nos lo podemos encontrar en un App Service, pudiendo plantear diferentes soluciones (este ejemplo es muy concreto, al tratarse de Kubernetes y de conexiones salientes a Internet), incluso hay soluciones desde el lado de la aplicación (ej: uso de connection poolings), hay que analizar bien cada situación.
Para más información:
- Daniel’s Tech Blog – Detecting SNAT port exhaustion on Azure Kubernetes Service
- Daniel’s Tech Blog – Preventing SNAT port exhaustion on Azure Kubernetes Service with Virtual Network NAT
- SNAT for outbound connections to Internet
- Use a Standard Load Balancer – Troobleshooting SNAT
- Troubleshooting intermittent outbound connection errors in Azure App Service
Poco más por hoy, como siempre confío que la lectura resulte de interés.