Optimización de TCP para el rendimiento de la red

La red global de Cloud de Confiance ofrece alta confiabilidad y baja latencia, ya que conecta aplicaciones en todas las regiones y zonas sin salir de la red de Google. Sin embargo, la configuración predeterminada de TCP/IP de Linux suele estar ajustada para entornos locales y puede causar cuellos de botella en el rendimiento en la nube.

Para obtener el mejor rendimiento en Cloud de Confiance by S3NS, usa la configuración de TCP/IP de este documento, que está optimizada específicamente para el entorno de la nube.

La configuración mencionada en este documento se probó para su uso en el Cloud de Confiance entorno de. La configuración es principalmente para las comunicaciones internas de la instancia y no se aplica necesariamente a la comunicación entre las instancias de Compute Engine y las direcciones externas.

Información sobre el límite de capacidad de procesamiento

TCP usa un mecanismo de "sistema de ventanas" para administrar el flujo de datos entre un remitente y un receptor. La capacidad de procesamiento máxima alcanzable se rige por la siguiente relación:

Throughput <= window size / round-trip time (RTT) latency

En el diseño original de TCP, el tamaño máximo de la ventana se limita a 65,535 bytes (64 KiB - 1), lo que a menudo hace que las redes modernas de alta velocidad no se utilicen por completo, ya que el remitente espera actualizaciones de la ventana.

Secuencia de comandos de shell para optimizar el rendimiento de TCP

Se recomienda la siguiente configuración de TCP para mejorar el rendimiento:

Puedes habilitar esta configuración sugerida con la siguiente secuencia de comandos de shell. Antes de ejecutar la secuencia de comandos, asegúrate de reemplazar eth0 por la interfaz de red principal de tu instancia de procesamiento.

# Set DEV to your primary network interface
DEV=eth0

# 1. Reduce MinRTO
sysctl -w net.ipv4.tcp_rto_min_us=5000

# 2. Enable Fair Queueing
tc qdisc replace dev $DEV root fq

# 3. Disable slow start after idle
sysctl -w net.ipv4.tcp_slow_start_after_idle=0

# 4. Disable TCP Cubic HyStart ACK train
echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect

# 5. Increase socket memory budgets
echo 4194304 > /proc/sys/net/core/rmem_max
echo 4194304 > /proc/sys/net/core/wmem_max
echo 4194304 > /proc/sys/net/ipv4/tcp_notsent_lowat
echo "4096 262144 16777216" > /proc/sys/net/ipv4/tcp_rmem
echo "4096 262144 33554432" > /proc/sys/net/ipv4/tcp_wmem

# 6. Enable hardware GRO
ethtool -K $DEV rx-gro-hw on

En las siguientes secciones, se describe cada uno de los parámetros de configuración sugeridos con más detalle.

Reduce MinRTO

Recupera más rápido la pérdida de paquetes reduciendo los retrasos de retransmisión.

El tiempo de espera de retransmisión de TCP (RTO) controla cuánto tiempo espera un remitente de TCP una señal ACK antes de retransmitir. Linux inicializa el RTO en un segundo (según la sección 2.1 de RFC 6298) y, luego, lo ajusta con el tiempo al tiempo de ida y vuelta (RTT) más un margen de seguridad.

El RTO mínimo (MinRTO) implementa un límite inferior en este ajuste. Si la estimación de RTO es demasiado baja, puede introducir retransmisiones falsas, en las que el remitente retransmite paquetes mientras la respuesta aún está en proceso.

El MinRTO predeterminado es de 200 ms, que es innecesariamente conservador para las redes modernas de la nube. Se probó exhaustivamente un valor de 5 ms en Cloud de Confiance y es un valor predeterminado seguro para las conexiones entre servidores Linux modernos dentro de Cloud de Confiance.

Otro factor son los ACK retrasados, en los que el par retrasa las respuestas ACK. Este retraso permite agrupar los ACK en varios paquetes de datos o combinar un ACK con un paquete de datos en la dirección inversa. Los temporizadores de ACK retrasados se basan en el RTT, de manera similar al temporizador de RTO.

Reducir el MinRTO acelera la reparación de pérdidas en conexiones de RTT bajo, como las conexiones dentro de una zona o región. Este ajuste no afecta el comportamiento de las conexiones que experimentan un RTT alto, ya que no cambia su RTT estimado.

Configura MinRTO

Puedes configurar MinRTO con uno de estos dos métodos:

  1. Usa sysctl (Linux 6.11 y versiones posteriores):

    Puedes configurar el MinRTO predeterminado con un comando sysctl:

    sysctl -w net.ipv4.tcp_rto_min_us=5000
    
  2. Usa ip route (control por ruta o versiones de Linux anteriores a 6.11):

    Como alternativa, para versiones anteriores de Linux o control por ruta, MinRTO se puede configurar por ruta:

    ip route change default rto_min 5ms
    

El enfoque por ruta es preferible en entornos en los que las conexiones pueden salir de Cloud de Confiance o comunicarse con pilas de TCP/IP que no son de Linux. Es posible que estos sistemas tengan diferentes temporizadores de ACK retrasados. Para una implementación amplia de Internet pública, se recomienda mantener la configuración conservadora predeterminada de minRTO y aplicar la configuración de 5 ms solo para las rutas dentro de la Cloud de Confiance nube privada virtual (VPC).

Habilita el sistema de colas justo

Minimiza la congestión y las pérdidas de paquetes causadas por las ráfagas de aplicaciones.

A diferencia de las colas FIFO (First-In, First-Out) estándar, el sistema de colas justo (FQ) distribuye el ancho de banda de manera equitativa entre los diferentes flujos. También acelera el tráfico, ya que permite que cada conexión TCP calcule su tasa y tiempo de entrega óptimos para cada paquete. Si FQ está presente, la pila de TCP se basa en FQ para retener los paquetes hasta su tiempo de entrega óptimo. Esta aceleración reduce las ráfagas dentro de un flujo, lo que, a su vez, minimiza las pérdidas de paquetes y las retransmisiones.

Para establecer el modelador de tráfico FQ como el modelador de tráfico del dispositivo de red, usa el siguiente comando tc (control de tráfico):

tc qdisc replace dev $DEV root fq

En instancias grandes con un ancho de banda de salida alto, el dispositivo de red puede tener varias colas de transmisión. Para estas instancias, puede ser preferible fragmentar la determinación por tráfico en las colas de transmisión mediante la instalación del modelador de tráfico de varias colas (MQ). Este es un multiplexor que conecta un modelador de tráfico independiente a cada cola de transmisión. Los modeladores de tráfico por cola reducen la contención en los bloqueos y las líneas de caché en las CPUs.

Inhabilita el inicio lento después de la inactividad

Mantén tasas de transferencia altas después de que una conexión haya estado inactiva.

Para evitar la congestión, las conexiones TCP comienzan enviando datos a una tasa baja y, luego, aumentan la tasa de forma exponencial hasta que se detecta la pérdida de paquetes. Esta fase inicial se conoce como inicio lento.

De forma predeterminada, TCP vuelve a la configuración conservadora de "inicio lento" después de un período de inactividad. Un período de inactividad puede ser tan corto como un tiempo de espera de retransmisión (RTO), como se define en RFC 2581. Inhabilitar esta función permite que la conexión se reanude de inmediato con la última tasa correcta conocida.

Cuando sea posible, las aplicaciones deben usar conexiones de larga duración en lugar de establecer conexiones repetidas con el mismo par. Esto evita el costo del establecimiento de la conexión y mantiene la información de congestión. Sin embargo, incluso con conexiones de larga duración, después de un período de inactividad, TCP olvida la información de congestión de forma predeterminada y vuelve a la configuración conservadora inicial y a la fase de "inicio lento".

Para inhabilitar la función de inicio lento después de la inactividad, usa el siguiente comando:

sysctl -w net.ipv4.tcp_slow_start_after_idle=0

Inhabilita el tren de ACK de HyStart cúbico de TCP

Aumenta rápidamente a tasas de transferencia altas ignorando los indicadores de congestión de falsos positivos.

La tasa de crecimiento exponencial de la fase de "inicio lento" puede ser agresiva y, potencialmente, superar la tasa objetivo óptima. El inicio híbrido (HyStart) es un mecanismo adicional diseñado para salir de la fase de "inicio lento" antes mediante el uso de dos indicadores de congestión clave:

  • Retraso del tiempo de ida y vuelta (RTT): Mide el retraso de propagación de los paquetes a través de la red. Durante los períodos de congestión de la red, las colas de paquetes se acumulan en los vínculos de cuello de botella. Esto hace que aumente el RTT, lo que puede indicar la presencia de congestión.
  • Espacio ACK: Se basa en indicadores de que los paquetes se retrasan en un cuello de botella, pero se enfoca en los ACK de respuesta. Este mecanismo supone que, sin congestión, los ACK llegarán con el mismo espacio que los paquetes de datos originales. Este patrón suele denominarse tren de ACK. Si los ACK se retrasan más allá de este patrón esperado, indica que podría haber congestión.

Para optimizar el rendimiento, inhabilita la detección de trenes de paquetes ACK mientras mantienes habilitado el mecanismo de retraso de RTT.

echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect

Aumenta los presupuestos de memoria de sockets

Aumenta la capacidad de procesamiento máxima en vínculos de RTT alto permitiendo más datos en tránsito.

La cantidad de datos en tránsito es una función del ancho de banda y el retraso de propagación, que se conoce como el producto de retraso del ancho de banda (BDP). El BDP se calcula como el ancho de banda multiplicado por el tiempo de ida y vuelta (RTT), lo que da como resultado un valor que especifica la cantidad óptima de bits para enviar y llenar la canalización:

BDP (bits) = bandwidth (bits/second) * RTT (seconds)

Todos los datos en tránsito deben permanecer almacenados en búfer en el remitente en caso de que deban retransmitirse. Los límites de memoria de sockets TCP pueden limitar directamente la capacidad de procesamiento alcanzable, ya que determinan la cantidad de datos en tránsito que se pueden almacenar en búfer.

En Linux, los límites de memoria de sockets TCP se configuran con los siguientes parámetros de configuración de sysctl(8):

  • net.core.rmem_max
  • net.core.wmem_max
  • net.ipv4.tcp_rmem
  • net.ipv4.tcp_wmem

Estos límites de memoria de sockets TCP existen para evitar usar toda la memoria del sistema y provocar condiciones de memoria insuficiente (OOM), en especial en cargas de trabajo con muchas conexiones. Aumentar estos límites puede aumentar la capacidad de procesamiento, en especial en rutas de RTT alto. A menos que el recuento de conexiones sea de millones, hay poco riesgo de aumentar estos límites.

Estas variables establecen límites superiores en el tamaño del búfer de sockets, no en la asignación directa de memoria. Aumentar estos valores no afecta la asignación de memoria real para las conexiones con RTT bajo, como las que se encuentran dentro de una Cloud de Confiance zona.

Los primeros dos parámetros ajustables afectan el tamaño máximo de ventana TCP para las aplicaciones que establecen el tamaño de ventana TCP directamente, lo que hacen relativamente pocas aplicaciones. Estos límites delimitan lo que una aplicación puede solicitar de forma explícita con las opciones de sockets SO_RCVBUF y SO_SNDBUF.

Para las versiones de kernel de Linux 6.18 y posteriores, net.core.rmem_max y net.core.wmem_max tienen un valor predeterminado de 4 MB. Según años de experiencia, estos parámetros de configuración se consideran seguros. Para versiones anteriores de Linux, te recomendamos que aumentes estos límites a 4 MB en plataformas modernas:

echo 4194304 > /proc/sys/net/core/rmem_max
echo 4194304 > /proc/sys/net/core/wmem_max

El segundo conjunto de límites, net.ipv4.tcp_rmem y net.ipv4.tcp_wmem, administra los límites de ajuste automático de los búferes de envío y recepción de TCP.

Cada uno de estos parámetros de configuración toma tres valores: mínimo, predeterminado inicial y tamaño máximo de memoria de sockets. Los valores máximos predeterminados suelen ser más conservadores de lo necesario en las plataformas modernas, por ejemplo:

  • tcp_rmem: 4096, 131072, 6291456
  • tcp_wmem: 4096, 16384, 4194304

La pila de TCP ajusta automáticamente el tamaño de los búferes de envío y recepción de TCP según las estimaciones de RTT y la ventana de congestión. Un tamaño máximo de búfer de escritura de 4, 194,304 o 4 MB es pequeño para las conexiones de RTT alto; con un RTT de 100 ms, este parámetro de configuración limita la capacidad de procesamiento a 40 MB/s en el mejor de los casos. En lugar de intentar calcular qué valores usar, un enfoque más simple es usar valores predeterminados seguros para los servidores modernos que tienen muchos GB de RAM.

Como precaución adicional, te recomendamos que limites la cantidad de datos que se pueden poner en cola en el socket, pero que aún no se enviaron. Si bien el objetivo de aumentar el wmem máximo es permitir más datos en tránsito, un proceso puede escribir en el socket más rápido de lo que TCP puede enviarlo, lo que hace que se acumule una cola de datos no enviados en el host y se desperdicie memoria. Para evitar esto, limita la cantidad de datos que aún no se enviaron estableciendo tcp_notsent_lowat y, luego, aumenta el límite general de wmem para permitir búferes en tránsito más grandes.

A menos que un servidor tenga millones de conexiones, la siguiente configuración debería ser segura. Sin embargo, si experimentas condiciones de memoria insuficiente con muchas conexiones, usa un tamaño máximo de búfer más bajo.

echo 4194304 > /proc/sys/net/ipv4/tcp_notsent_lowat

echo "4096 262144 16777216" > /proc/sys/net/ipv4/tcp_rmem
echo "4096 262144 33554432" > /proc/sys/net/ipv4/tcp_wmem

Habilita GRO de hardware

Aumenta la eficiencia del procesamiento de recepción de TCP/IP para flujos grandes. Reduce la sobrecarga de CPU agrupando los paquetes recibidos.

Para la mayoría de las operaciones de TCP/IP, el costo del ciclo de CPU se ajusta con la tasa de paquetes, no con la tasa de bytes. Para mitigar esta sobrecarga en la transmisión, los sistemas operativos modernos envían datos TCP a través de la ruta de transmisión en paquetes grandes que contienen varios segmentos TCP. Estos pueden variar hasta 64 KB o incluso cientos de kilobytes con BIG-TCP de Linux.

Estos paquetes grandes superan el tamaño máximo de paquete en la red, la MTU. Esta optimización del sistema operativo se basa en la compatibilidad del dispositivo de red para dividir estos paquetes y enviarlos como un tren de paquetes más pequeños, cada uno con un solo segmento TCP. Esta compatibilidad, la descarga de segmentación de TCP (TSO), está disponible de forma generalizada y habilitada de forma predeterminada.

En la recepción, los dispositivos GVNIC en plataformas de tercera generación y posteriores pueden realizar la operación inversa: almacenar en búfer brevemente los segmentos en el dispositivo para ver si llegan segmentos consecutivos y, si es así, combinarlos y reenviarlos como paquetes de varios segmentos al host. Esta función se conoce de varias maneras, como Receive Segment Coalescing (RSC) en Windows, descarga de recepción grande (LRO) o descarga de recepción genérica de hardware (HW-GRO) en Linux. HW-GRO es un refinamiento más estricto de LRO.

HW-GRO es seguro para habilitarse de forma predeterminada. Esto sucede cuando está disponible.

Mientras tanto, en plataformas con dispositivos GVNIC que admiten la función, habilita HW-GRO con ethtool. Según la versión del kernel y del controlador, la función se anuncia como LRO o HW-GRO. La implementación es la misma, independientemente del nombre.

    ethtool -K $DEV large-receive-offload on
    ethtool -K $DEV rx-gro-hw on

Aumenta el tamaño de la MTU a 4,082 bytes

Aumenta la eficiencia de la transferencia de datos para flujos de gran capacidad de procesamiento.

Aumentar el tamaño del paquete, de manera similar a las optimizaciones de TSO y HW-GRO, aumenta la eficiencia de la transferencia porque la mayor parte del trabajo de procesamiento se realiza por paquete, no por byte.

Cloud de Confiance Las redes de VPC pueden admitir paquetes de hasta 8,898 bytes, que es significativamente más grande que la MTU predeterminada de 1,460 bytes. Usar un tamaño de paquete de red más grande reduce los ciclos de CPU que se usan por byte de capacidad de procesamiento (goodput).

Consideraciones sobre el tamaño óptimo de los paquetes

Si bien los paquetes más grandes suelen ser mejores, la MTU máxima posible no siempre es la opción óptima. La eficiencia obtenida del envío de menos paquetes debe equilibrarse con los siguientes costos:

  • Uso de memoria: Los búferes más grandes requieren más memoria asignada al dispositivo de red para la recepción de paquetes. Si tienes muchas colas, se puede dejar sin usar una cantidad considerable de memoria (varada).
  • Manejo de paquetes pequeños: Los búferes más grandes manejan paquetes pequeños, como los acuses de recibo puros (ACKs), de manera menos eficiente.
  • Costos de asignación de CPU: Los costos de la ruta de datos se ven afectados de manera significativa por la asignación y la liberación de memoria. Alinear el tamaño del paquete a un múltiplo de las páginas de memoria ayuda a optimizar este costo de CPU.
  • Interacción de TSO: El tamaño del paquete afecta de manera sutil la descarga de segmentación de TCP (TSO). Para compilar el paquete TSO más grande posible, es posible que debas elegir un tamaño máximo de segmento (MSS) más pequeño. Por ejemplo, dado el paquete IP más grande posible de 64 KB, incluidos los encabezados, un MSS de 4 KB da como resultado una carga útil más grande (60 KB) que un MSS de 8 KB (56 KB).

Para la mayoría de las cargas de trabajo, la diferencia en la eficiencia entre las MTU de 4 KB, 8 KB o 9 KB es pequeña. Sin embargo, cualquiera de estas es una mejora significativa con respecto a los paquetes predeterminados de 1,460 bytes.

Recomendación para paquetes de tamaño de página

Como opción sólida y, por lo general, eficiente, te recomendamos que establezcas el tamaño de la MTU para tu red de VPC en 4,082 bytes. Se recomienda este tamaño porque permite que todo el paquete de Ethernet quepa dentro de una página de memoria de 4,096 bytes, lo que optimiza la asignación de páginas de memoria. Esta recomendación es para la MTU de capa 3, que incluye el encabezado IP, pero excluye la capa de vínculo de Ethernet de 14 bytes.

Configuración de MTU de IP

Puedes configurar la MTU para cada red de VPC directamente a través de la Cloud de Confiance console.

Para la mayoría de las distribuciones de Linux en Cloud de Confiance, no se necesita configuración manual en la instancia de procesamiento. La instancia aprende automáticamente la MTU de la red con DHCP durante el inicio (con la opción 26). Luego, la instancia establece la MTU del dispositivo de red para que coincida. Te recomendamos que uses esta configuración automática.

Si es necesaria la configuración manual, la MTU del dispositivo de red se puede establecer en un valor inferior a la MTU de la red de VPC con el siguiente comando:

ip link set dev $DEV mtu 4082

Establece diferentes tamaños de MTU para rutas específicas

Si tu instancia de procesamiento se comunica de forma externa fuera de la VPC, donde la MTU de la ruta puede ser más baja, entonces establecer la MTU en rutas específicas es la opción preferida. En este caso, establece la MTU predeterminada en los 1,460 bytes conservadores y aplica la MTU más alta, por ejemplo, 4,082 bytes, solo para las rutas dentro de la VPC:

#Set intra-VPC route MTU:
ip -4 route change $SUBNET/$MASK dev $DEV mtu 4082
#Set default route MTU:
ip -4 route change default dev $DEV mtu 1460

Configura el tamaño máximo de segmento (MSS) de TCP

El tamaño máximo de segmento (MSS) de TCP determina el tamaño de la carga útil del paquete para una conexión TCP. Dado que los paquetes TCP/IP no deben exceder el tamaño de la MTU para evitar la fragmentación o la pérdida de paquetes, el MSS debe ajustarse en consecuencia.

En general, no es necesario configurar el MSS de TCP de forma manual, ya que el sistema operativo lo deriva automáticamente de la MTU de la ruta.

El MSS cubre la carga útil y las opciones de TCP, pero excluye el encabezado IPv4 de 20 bytes y el encabezado TCP de 20 bytes. Por lo tanto, en una red IPv4, el MSS suele ser 40 bytes más pequeño que la MTU.

Si prefieres un MSS más pequeño para el tráfico específico, puedes configurarlo por ruta. Por ejemplo, si tu red de VPC y la MTU del dispositivo usan el máximo (8,896 bytes) para el tráfico general, pero quieres usar una MTU de 4 KB para el tráfico TCP, puedes usar el siguiente comando:

ip -4 route change default dev $DEV advmss 4042

Modo de división de encabezado

El controlador gve de gVNIC en las versiones de kernel de Linux 6.9 y posteriores admite la división de datos de encabezado de TCP (tcp-data-split), que está inhabilitada de forma predeterminada.

La división de encabezado separa los encabezados y los datos de los paquetes en búferes distintos. Esto permite llenar una página de memoria completa con 4,096 bytes de datos. Esta separación permite optimizaciones cruciales, como reemplazar las costosas operaciones de copia de kernel a espacio de usuario con operaciones de asignación de páginas de memoria más económicas (por ejemplo, con TCP_ZEROCOPY_RECEIVE de Linux).

Cuando se habilita la función de división de encabezado de recepción, cambia el cálculo de la MTU. La MTU óptima es aquella en la que todos los encabezados se asignan al búfer de encabezado y el búfer de carga útil llena una página completa de datos. Con la división de encabezado habilitada:

  • Un búfer de encabezado contiene lo siguiente:
    • Ethernet (14 bytes)
    • IPv4 (20 bytes)
    • Encabezados TCP (20 bytes)
    • Opciones comunes de TCP (12 bytes para la configuración predeterminada con marcas de tiempo de TCP)
  • El búfer de datos contiene 4,096 bytes de datos de carga útil.

Esto da como resultado un tamaño total de trama de 4,162 bytes y, por lo tanto, una MTU de 4,148 bytes.

¿Qué sigue?