Cómo superar el agotamiento de puertos efímeros en NGINX y NGINX Plus

NGINX | 19 de abril de 2016

NGINX y NGINX Plus son balanceadores de carga de HTTP, TCP y UDP extremadamente potentes. Son muy eficientes a la hora de actuar como proxy para grandes ráfagas de solicitudes y mantener un gran número de conexiones simultáneas. Sin embargo, estas características hacen que NGINX y NGINX Plus sean especialmente susceptibles al agotamiento de puertos efímeros; una situación en la que no se pueden crear nuevas conexiones porque el sistema operativo ha agotado los números de puerto asignados para establecer nuevos sockets locales. (El agotamiento de puertos efímeros afecta a ambos productos, pero, en aras de la brevedad, durante el resto de este artículo nos referiremos únicamente a NGINX Plus).

En este blog, repasamos los componentes de una conexión TCP y cómo se determina su contenido antes de establecerla. A continuación, mostramos cómo detectar cuándo NGINX Plus se ve afectado por el agotamiento de puertos efímeros. Por último, analizamos estrategias para combatir estas limitaciones mediante ajustes del kernel de Linux y directivas de NGINX Plus.

Breve descripción general de los sockets de red

Cuando se establece una conexión mediante TCP, se crea un socket tanto en el host local como en el remoto. A continuación, estos sockets se conectan para crear un par de sockets, que se describe mediante una 4-tupla única formada por la dirección IP y el puerto locales, junto con la dirección IP y el puerto remotos.

La dirección IP y el puerto remotos pertenecen al lado del servidor de la conexión, y el cliente debe determinarlos antes de poder siquiera iniciar la conexión. En la mayoría de los casos, el cliente elige automáticamente qué dirección IP local utilizar para la conexión, aunque a veces la selecciona el software que establece la conexión. Por último, el puerto local se selecciona aleatoriamente de un intervalo definido que proporciona el sistema operativo. El puerto solo está asociado al cliente mientras dura la conexión, por lo que se denomina efímero. Cuando finaliza la conexión, el puerto efímero queda disponible para volver a utilizarse.

Cómo reconocer el agotamiento de puertos efímeros

Como se mencionó en la introducción, NGINX Plus está sujeto por naturaleza al agotamiento de puertos efímeros y a los problemas que este provoca. Cuando NGINX Plus actúa como proxy de una solicitud a un servidor ascendente, es el cliente en el proceso de creación de sockets descrito anteriormente y, de forma predeterminada, vincula automáticamente el socket de la solicitud reenviada a una dirección IP local y a un puerto efímero disponible en el host donde se ejecuta. Si la tasa de conexiones es elevada y los sockets que se establecen pasan a un estado de espera más rápido de lo que se cierran los sockets abiertos existentes, los puertos disponibles terminan por agotarse y no se pueden crear nuevos sockets. Esto provoca errores tanto en el sistema operativo como en NGINX Plus. A continuación se muestra un ejemplo del error resultante en el registro de errores de NGINX Plus.

2016/03/18 09:08:37 [crit] 1888#1888: *13 connect() to 10.2.2.77:8081 failed (99: Cannot assign requested address) while connecting to upstream, client: 10.2.2.42, server: , request: "GET / HTTP/1.1", upstream: "http://10.2.2.77:8081/", host: "10.2.2.77"

El agotamiento de puertos también provoca un aumento de los errores 500 generados por NGINX Plus en lugar de por el servidor ascendente. A continuación se muestra una entrada de ejemplo del registro de acceso de NGINX Plus.

10.2.2.42 - - [18/Mar/2016:09:14:20 -0700] "GET / HTTP/1.1" 500 192 "-" "curl/7.35.0"

Para comprobar el número de sockets en el estado TIME-WAIT en su servidor NGINX Plus, ejecute el siguiente comando ss en el shell de Linux. El ejemplo muestra que hay 143 sockets abiertos con el estado TIME-WAIT. En el ejemplo, obtenemos el recuento mediante el comando wc para contar el número de líneas de la salida del comando.

# ss -a | grep TIME-WAIT | wc -l143

Ajustar el kernel para aumentar el número de puertos efímeros disponibles

Una forma de reducir el agotamiento de puertos efímeros es utilizar el parámetro del kernel de Linux net.ipv4.ip_local_port_range. El intervalo predeterminado suele ser de 32768 a 61000.

Si observa que se está quedando sin puertos efímeros, cambiar el intervalo predeterminado por el comprendido entre 1024 y 65000 es una forma práctica de duplicar el número de puertos efímeros disponibles. Para obtener más información sobre cómo cambiar la configuración del kernel, consulte nuestra entrada del blog «Optimización de NGINX para mejorar el rendimiento».

Habilitación de conexiones keepalive en NGINX Plus

Otra forma de reducir el agotamiento de los puertos efímeros es habilitar las conexiones persistentes entre NGINX Plus y los servidores ascendentes. En la implementación más sencilla de HTTP, un cliente abre una nueva conexión, envía la solicitud, lee la respuesta y, a continuación, cierra la conexión para liberar los recursos asociados.

Una conexión persistente se mantiene abierta después de que el cliente lea la respuesta, de modo que pueda reutilizarse para solicitudes posteriores.

Utilice la directiva keepalive para habilitar conexiones persistentes de NGINX Plus a los servidores ascendentes y definir el número máximo de conexiones persistentes inactivas con dichos servidores que se conservan en la caché de cada proceso de trabajo. Cuando se supera este número, se cierran las conexiones utilizadas menos recientemente. Sin conexiones persistentes, se genera una mayor sobrecarga y se hace un uso ineficiente tanto de las conexiones como de los puertos efímeros.

La siguiente configuración de ejemplo indica a NGINX Plus que mantenga al menos 128 conexiones persistentes con los servidores definidos en el bloque upstream denominado backend.

upstream backend {    server 10.0.0.100:1234;
    server 10.0.0.101:1234;
    keepalive 128;
}

Al habilitar conexiones persistentes con los servidores ascendentes, también debe utilizar la directiva proxy_http_version para indicar a NGINX Plus que utilice la versión 1.1 de HTTP y la directiva proxy_set_header para eliminar cualquier encabezado denominado Connection. Ambas directivas pueden incluirse en los bloques de configuración http, server o location.

proxy_http_version 1.1; proxy_set_header Connection "";

Vinculación dinámica de conexiones a una lista definida de direcciones IP locales

Optimizar el kernel y habilitar conexiones persistentes permite controlar mucho mejor la disponibilidad y el uso de los puertos efímeros, pero hay determinadas situaciones en las que estos cambios no bastan para combatir su uso excesivo. En estos casos, podemos emplear algunas directivas y parámetros de NGINX Plus que nos permiten vincular un porcentaje de nuestras conexiones a direcciones IP locales estáticas. De este modo, el número de puertos efímeros disponibles se multiplica por el número de direcciones IP definidas en la configuración. Para ello, utilizamos las directivas proxy_bind y split_clients.

En la configuración de ejemplo que aparece a continuación, la directiva proxy_bind del bloque location establece la dirección IP local durante cada solicitud según el valor de la variable $split_ip. Esta variable se establece dinámicamente en el valor generado por el bloque split_clients, que utiliza una función hash para determinar dicho valor.

El primer parámetro de la directiva split_clients es una cadena ("$remote_addr$remote_port") a la que se aplica una función hash MurmurHash2 durante cada solicitud. El segundo parámetro ($split_ip) es la variable que establecemos dinámicamente en función del hash del primer parámetro.

Las instrucciones entre llaves dividen la tabla hash en «cubos», cada uno de los cuales contiene un porcentaje de los hashes. En este caso, dividimos la tabla en 10 cubos del mismo tamaño, pero podemos crear tantos como queramos y no es necesario que todos tengan el mismo tamaño. (El porcentaje del último cubo siempre se representa mediante un asterisco [*] en lugar de un número concreto, ya que es posible que el número de hashes no se pueda dividir de manera uniforme según los porcentajes especificados).

El intervalo de valores hash posibles va de 0 a 4294967295, por lo que, en nuestro caso, cada segmento contiene unos 429496700 valores (el 10 % del total): el primer segmento contiene los valores de 0 a 429496700, el segundo, de 429496701 a 858993400, y así sucesivamente. La variable $split_ip se establece en la dirección IP asociada al segmento que contiene el hash de la cadena $remote_addr$remote_port. Como ejemplo concreto, el valor hash 150000000 se encuentra en el cuarto segmento, por lo que, en ese caso, la variable $split_ip se establece dinámicamente en 10.0.0.213.

http {    upstream backend {
        server 10.0.0.100:1234;
        server 10.0.0.101:1234;
 }

    server {
        # ...
        location / {
            # ...
            proxy_pass http://backend;
            proxy_bind $split_ip;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }

    split_clients "$remote_addr$remote_port" $split_ip { 10% 10.0.0.210; 10% 10.0.0.211; 10% 10.0.0.212; 10% 10.0.0.213; 10% 10.0.0.214; 10% 10.0.0.215; 10% 10.0.0.216; 10% 10.0.0.217; 10% 10.0.0.218; * 10.0.0.219; }
}

Conclusión

Ajustar la configuración del kernel de Linux relativa al número de puertos efímeros permite mejorar su disponibilidad y uso. Habilitar conexiones persistentes entre NGINX Plus y los servidores ascendentes permite utilizar los puertos efímeros de forma más eficiente. Por último, el uso de las directivas split_clients y proxy_bind en la configuración de NGINX Plus permite vincular dinámicamente las conexiones salientes a una lista definida de direcciones IP locales, lo que aumenta considerablemente el número de puertos efímeros disponibles para NGINX Plus.

Para probar NGINX Plus, empiece hoy mismo su prueba gratuita de 30 días o póngase en contacto con nosotros para hablar sobre sus casos de uso.


Compartir

Sobre el autor

Kevin Jones
Kevin JonesResponsable sénior de producto | F5 NGINX

Más artículos de Kevin Jones

Artículos relacionados del blog

Únete a F5 NGINX en AppWorld 2024
NGINX | 04/01/2024

Únete a F5 NGINX en AppWorld 2024

Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
NGINX | 26/09/2023

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source

La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.

Exposición de APIs en Kubernetes
NGINX | 10/03/2022

Exposición de APIs en Kubernetes

La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
NGINX | 23/08/2021

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto

Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.

Presentamos NGINX Plus R22
NGINX | 09/06/2020

Presentamos NGINX Plus R22

NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.

Presentamos NGINX Plus R17
NGINX | 11/12/2018

Presentamos NGINX Plus R17

NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.