NGINX y NGINX Plus son balanceadores de carga HTTP , TCP y UDP extremadamente potentes. Son muy eficientes a la hora de procesar grandes ráfagas de solicitudes y mantener una gran cantidad de conexiones simultáneas. Pero estas características hacen que NGINX y NGINX Plus estén particularmente sujetos al agotamiento efímero de puertos , una condición en la que no se pueden crear nuevas conexiones porque el sistema operativo se ha quedado sin los números de puerto asignados para establecer nuevos sockets locales. (El agotamiento del puerto efímero se aplica a ambos productos, pero por el bien de la brevedad nos referiremos solo a NGINX Plus en el resto de este blog).
En este blog, repasamos los componentes de una conexión TCP y cómo se decide su contenido antes de que se establezca una conexión. A continuación mostramos cómo determinar cuándo NGINX Plus se ve afectado por el agotamiento del puerto efímero. Por último, analizamos estrategias para combatir esas limitaciones utilizando ajustes del kernel de Linux y directivas NGINX Plus.
Cuando se establece una conexión a través de TCP, se crea un socket tanto en el host local como en el remoto. Estos sockets se conectan luego para crear un par de sockets, que se describe mediante una tupla única de 4 que consta de la dirección IP local y el puerto junto con la dirección IP remota y el puerto.
La dirección IP y el puerto remotos pertenecen al lado del servidor de la conexión y deben ser determinados por el cliente antes de poder 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, pero a veces la elige el software que establece la conexión. Finalmente, el puerto local se selecciona aleatoriamente de un rango definido que el sistema operativo pone a disposición. El puerto está asociado con el cliente sólo mientras dura la conexión, por eso se denomina efímero . Cuando se finaliza la conexión, el puerto efímero queda disponible para ser reutilizado.
Como se mencionó en la introducción, NGINX Plus por naturaleza está sujeto al agotamiento efímero del puerto y los problemas que esto causa. Cuando NGINX Plus envía una solicitud a un servidor ascendente, es el cliente en el proceso de creación de socket descrito anteriormente, y su comportamiento predeterminado es vincular el socket para la solicitud enviada automáticamente a una dirección IP local y un puerto efímero disponible en el host donde se está ejecutando. Si la tasa de conexión es alta, de modo que los sockets que se están estableciendo pasan a un estado de espera más rápido que el tiempo en que se cierran los sockets abiertos existentes, eventualmente los puertos disponibles se agotan y no se pueden crear nuevos sockets. Esto genera 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.
18/03/2016 09:08:37 [crit] 1888#1888: *13 la conexión() a 10.2.2.77:8081 falló (99: No se puede asignar la dirección solicitada ) mientras se conecta al upstream, cliente: 10.2.2.42, servidor: , solicitud: "GET / HTTP/1.1", origen: "http://10.2.2.77:8081/", host: "10.2.2.77"
El agotamiento del puerto también provoca un aumento en500
errores originados por NGINX Plus en lugar del servidor ascendente. A continuación se muestra una entrada de muestra en el registro de acceso de NGINX Plus.
10.2.2.42 - - [18/Mar/2016:09:14:20 -0700] "GET / HTTP/1.1"500 192 "-" "rizo/7.35.0"
Para verificar la cantidad 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 utilizando el comando wc
para enumerar la cantidad de líneas en la salida del comando.
# ss -a | grep TIEMPO-ESPERA | wc -l143
Una forma de reducir el agotamiento del puerto efímero es con la configuración net.ipv4.ip_local_port_range
del kernel de Linux. El rango predeterminado más comúnmente es 32768 a 61000.
Si nota que se está quedando sin puertos efímeros, cambiar el rango predeterminado de 1024 a 65000 es una forma práctica de duplicar la cantidad de puertos efímeros disponibles para usar. Para obtener más información sobre cómo cambiar la configuración del kernel, consulte nuestra publicación de blog Cómo optimizar NGINX para mejorar el rendimiento .
Otra forma de reducir el agotamiento del puerto efímero es habilitar conexiones keepalive entre NGINX Plus y los servidores ascendentes. En la implementación más simple de HTTP, un cliente abre una nueva conexión, escribe la solicitud, lee la respuesta y luego cierra la conexión para liberar los recursos asociados.
Una conexión keepalive se mantiene abierta después de que el cliente lee la respuesta, de modo que puede reutilizarse para solicitudes posteriores.
Utilice la directiva keepalive
para habilitar conexiones keepalive desde NGINX Plus a servidores ascendentes, definiendo la cantidad máxima de conexiones keepalive inactivas a servidores ascendentes que se conservan en la memoria caché de cada proceso de trabajo. Cuando se supera este número, se cierran las conexiones utilizadas menos recientemente. Sin keepalives, agrega más sobrecarga y resulta ineficiente tanto con las conexiones como con los puertos efímeros.
La siguiente configuración de ejemplo le indica a NGINX Plus que mantenga al menos 128 conexiones keepalive a los servidores definidos en el bloque ascendente
llamado backend .
backend ascendente { servidor 10.0.0.100:1234;
servidor 10.0.0.101:1234;
keepalive 128;
}
Al habilitar conexiones keepalive a sus servidores ascendentes, también debe usar la directiva proxy_http_version
para indicarle a NGINX Plus que use la versión HTTP 1.1, y la directiva proxy_set_header
para eliminar cualquier encabezado llamado Connection
. Ambas directivas se pueden colocar en los bloques de configuración http
, servidor
o ubicación
.
proxy_http_version 1.1; proxy_set_header Conexión "";
Optimizar el kernel y habilitar conexiones keepalive proporciona mucho más control sobre la disponibilidad y el uso de puertos efímeros, pero hay ciertas situaciones en las que esos cambios no son suficientes para combatir el uso excesivo de puertos efímeros. En este caso podemos emplear algunas directivas y parámetros de NGINX Plus que nos permitan vincular un porcentaje de nuestras conexiones a direcciones IP locales estáticas. Esto multiplica efectivamente la cantidad de puertos efímeros disponibles por la cantidad de direcciones IP definidas en la configuración. Para lograr esto utilizamos las directivas proxy_bind
y split_clients
.
En la siguiente configuración de ejemplo, la directiva proxy_bind
en el bloque de ubicación
establece la dirección IP local durante cada solicitud de acuerdo con el valor de la variable $split_ip
. Establecemos esta variable dinámicamente en el valor generado por el bloque split_clients
, que utiliza una función hash para determinar ese valor.
El primer parámetro de la directiva split_clients
es una cadena ( "$remote_addr$remote_port"
) que se codifica mediante una función 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 declaraciones dentro de las llaves dividen la tabla hash en “grupos”, cada uno de los cuales contiene un porcentaje de los hashes. Aquí dividimos la tabla en 10 cubos del mismo tamaño, pero podemos crear cualquier cantidad de cubos y no es necesario que todos sean del mismo tamaño. (El porcentaje del último segmento siempre se representa con un asterisco [ * ] en lugar de un número específico, porque la cantidad de hashes podría no ser divisible de manera uniforme en los porcentajes especificados).
El rango de valores hash posibles es de 0 a 4294967295, por lo que en nuestro caso cada depósito contiene aproximadamente 429496700 valores (10% del total): el primer depósito los valores de 0 a 429496700, el segundo depósito de 429496701 a 858993400, y así sucesivamente. La variable $split_ip
se establece en la dirección IP asociada con el depósito que contiene el hash de la cadena $remote_addr$remote_port
. Como ejemplo específico, el valor hash 150000000 cae en el cuarto grupo, por lo que la variable $split_ip
se establece dinámicamente en 10.0.0.213 en ese caso.
http { servidor backend ascendente { servidor 10.0.0.100:1234; servidor 10.0.0.101:1234; } servidor { # ... ubicación / { # ... contraseña_de_proxy http://backend; enlace_de_proxy $ip_dividida; encabezado_de_conjunto_de_proxy X-Reenviado-Para $dirección_remota; } } clientes_divididos "$dirección_remota$puerto_remoto" $ip_dividida { 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; } }
Ajustar la configuración del kernel de Linux para la cantidad de puertos efímeros proporciona una disponibilidad y un uso más eficientes. Habilitar conexiones keepalive desde NGINX Plus a sus servidores ascendentes hace que su consumo de puertos efímeros sea más eficiente. Por último, el uso de las directivas split_clients
y proxy_bind
en su configuración de NGINX Plus le permite vincular dinámicamente conexiones salientes a una lista definida de direcciones IP locales, lo que aumenta en gran medida la cantidad de puertos efímeros disponibles para NGINX Plus.
Para probar NGINX Plus, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso.
"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.