Optimización de TCP para el rendimiento de la red en Google Cloud y situaciones híbridas

En este artículo, se explica cómo mejorar la latencia de conexión entre los procesos de Google Cloud. En el artículo, se describen los métodos para calcular la configuración correcta a fin de disminuir la latencia de la conexión TCP.

La arquitectura moderna de microservicios recomienda que los desarrolladores compilen servicios pequeños con una sola responsabilidad. Los servicios deben comunicarse mediante TCP o UDP, según las expectativas de confiabilidad del sistema. Por lo tanto, es fundamental que los sistemas basados en microservicios se comuniquen con baja latencia y confiabilidad.

Google Cloud proporciona confiabilidad y baja latencia, ya que proporciona una red global, lo que significa que nuestros usuarios también pueden globalizarse. Tener una red global significa que un usuario simplemente puede crear una VPC que puede abarcar regiones y zonas. Las aplicaciones se pueden conectar entre sí en regiones y zonas sin salir de la red de Google Cloud.

Las aplicaciones que se escribieron para un entorno de centro de datos tradicional pueden exhibir un rendimiento lento cuando se mueven a un entorno de nube híbrida, es decir, cuando algunos de los componentes de la aplicación se ejecutan en un centro de datos corporativo y otros se ejecutan en la nube. El rendimiento lento puede ser el resultado de una serie de factores. En este artículo, nos centramos en las latencias de ida y vuelta, y en la forma en que la latencia afecta al rendimiento de TCP en aplicaciones que mueven una cantidad considerable de datos en cualquier parte de la red.

El problema: latencia y comportamiento de TCP

TCP usa un mecanismo de sistema de ventanas para evitar que un remitente rápido desborde a un receptor lento. El receptor anuncia cuántos datos debe enviar el remitente antes de que deba esperar una actualización de la ventana del receptor. Como resultado, si una aplicación receptora no puede recibir datos de la conexión, existe un límite de la cantidad de datos que pueden estar en cola para esperar a la aplicación.

La ventana de TCP permite un uso eficiente de la memoria en los sistemas de envío y recepción. Como la aplicación receptora consume datos, las actualizaciones de ventana se envían al remitente. Lo más rápido que puede ocurrir la actualización de ventana es en un proceso de ida y vuelta, lo que lleva a la fórmula siguiente para uno de los límites del rendimiento de transferencia masiva de una conexión TCP:

Capacidad de procesamiento <= tamaño de ventana / latencia de tiempo de ida y vuelta (RTT)

En el diseño original para TCP, esta ventana tiene un tamaño máximo de 65,535 bytes (64 KiB - 1). Esta era la cantidad máxima de datos que el remitente podía enviar antes de recibir una actualización de ventana para permitir que se enviaran más datos.

Cambios en TCP desde su introducción

Desde que se introdujo TCP, cambiaron algunas funciones clave:

  • Las velocidades de red típicas aumentaron en cuatro órdenes de magnitud.
  • La memoria típica de un sistema aumentó en cuatro órdenes de magnitud.

El resultado del primer cambio es que los tamaños de ventana TCP originales condujeron a un uso ineficiente de los recursos de red. Un remitente enviaba los datos de una ventana a la mejor velocidad posible en condiciones de red y, luego, permanecía inactivo durante un tiempo considerable mientras esperaba la actualización de la ventana de TCP. El resultado del segundo cambio es que los remitentes y receptores pueden usar más memoria para las redes a fin de abordar la limitación expuesta por el primer cambio.

En el siguiente diagrama se ilustra este intercambio.

El remitente envía solo 64,000 datos y pasa mucho tiempo esperando antes de recibir una actualización de ventana.

El remitente no puede usar la red por completo, ya que está esperando la actualización de ventana de TCP antes de enviar datos adicionales.

Envía más datos a la vez

La solución es enviar más datos a la vez. A medida que aumenta el ancho de banda de la red, pueden caber más datos en la canalización (red) y, a medida que la canalización se hace más larga, tarda más en confirmar la recepción de los datos. Esta relación se conoce como el producto de retraso de ancho de banda (BDP). Esto se calcula como el ancho de banda multiplicado por el tiempo de ida y vuelta (RTT), lo que da como resultado un valor óptimo de bits para enviar y llenar la canalización. La fórmula es la siguiente:

BDP (bits) = ancho de banda (bits/segundo) * RTT (segundos)

El BDP calculado se usa como tamaño de ventana de TCP para la optimización.

Por ejemplo, imagina que tienes una red de 10 Gbps con un RTT de 30 milisegundos. Para el tamaño de la ventana, usa el valor del tamaño original de la ventana TCP (65,535 bytes). Este valor no se acerca a aprovechar la capacidad de ancho de banda. El rendimiento de TCP máximo posible en este vínculo es el siguiente:

(65,535 bytes * 8 bits / byte) = ancho de banda * 0.030 segundos
ancho de banda = (65,535 bytes * 8 bits / byte) / 0.030 segundos
ancho de banda = 524,280 bits / 0.030 segundos
ancho de banda = 17,476,000 bits / segundo

Para decirlo de otra manera, estos valores generan una capacidad de procesamiento de algo más de 17 Mbits por segundo, que es una pequeña fracción de la capacidad de 10 Gbps de la red.

La solución: escalamiento del tamaño de la ventana de TCP

Para resolver las limitaciones de rendimiento que impone el diseño original del tamaño de ventana TCP, se introdujeron extensiones al protocolo TCP que permiten escalar el tamaño de la ventana a valores mucho más grandes. El escalamiento de ventana admite ventanas de hasta 1,073,725,440 bytes, o casi 1 GiB. Esta función se describe en RFC 1,323 como opción de escalamiento de ventana de TCP.

Las extensiones de escalamiento de ventana expanden la definición de la ventana de TCP para usar 32 bits y, luego, usan un factor de escala a fin de llevar este valor de 32 bits en el campo de ventana de 16 bits del encabezado de TCP. Para ver si la función está habilitada en sistemas basados en Linux, usa el comando siguiente:

sudo sysctl net.ipv4.tcp_window_scaling
    

(Todas las máquinas virtuales de Google Cloud Linux tienen esta función habilitada de forma predeterminada). Un valor de retorno de 1 indica que la opción está habilitada. Si la función está inhabilitada, puedes habilitarla mediante el siguiente comando:

sudo sysctl -w net.ipv4.tcp_window_scaling=1
    

Capacidad de procesamiento con un tamaño de ventana más grande

Puedes usar el ejemplo anterior para mostrar el beneficio de tener escalamiento de ventana. Como antes, supongamos una red de 10 Gbps con latencia de 30 milisegundos y, luego, calcula un nuevo tamaño de ventana con esta fórmula:

(Velocidad del vínculo * latencia) / 8 bits = tamaño de ventana

Si agregas los números de ejemplo, obtendrás el resultado siguiente:

(10 Gbps * 30 ms/1,000 s) / 8 bits/byte = tamaño de ventana
(10,000 Mbps * 0.030 segundos) / 8 bits/byte = 37.5 MB

Aumentar el tamaño de ventana TCP a 37 MB puede aumentar el límite teórico del rendimiento de la transferencia masiva de TCP a un valor que se aproxime a la capacidad de la red. Por supuesto, muchos otros factores pueden limitar el rendimiento, incluida la sobrecarga del sistema, el tamaño promedio del paquete y la cantidad de otros flujos que comparten el vínculo, pero, como puedes ver, el tamaño de la ventana mitiga los límites impuestos por el tamaño limitado de la ventana anterior de manera sustancial.

Configura los ajustes de Linux para cambiar el tamaño de la ventana de TCP

En Linux, el tamaño de la ventana TCP se ve afectado por los siguientes parámetros sysctl(8) ajustables:

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

Los primeros dos ajustes afectan el tamaño máximo de ventana TCP para las aplicaciones que intentan controlar el tamaño de ventana TCP directamente, ya que limitan la solicitud de las aplicaciones a no más de esos valores. Los segundos dos ajustes afectan el tamaño de ventana de TCP para las aplicaciones que permiten que el ajuste automático de Linux haga el trabajo.

El valor de tamaño de ventana óptimo depende de las circunstancias específicas, pero un punto de partida es el mayor BDP (producto de retraso de ancho de banda) para la ruta o rutas sobre las que esperas que el sistema envíe datos. En ese caso, deseas configurar los optimizables mediante los pasos siguientes:

  1. Asegúrate de tener privilegios de administrador.
  2. Obtén la configuración actual del búfer. Guarda esta configuración en caso de que quieras revertir estos cambios.

    sudo sysctl -a | grep mem
        
  3. Configura una variable de entorno con el tamaño de ventana TCP nuevo que deseas usar:

    MaxExpectedPathBDP=8388608
        
  4. Establece el tamaño máximo de búfer de recepción del SO para todos los tipos de conexiones:

    sudo sysctl -w net.core.rmem_max=$MaxExpectedPathBDP
        
  5. Establece el tamaño máximo de búfer de envío del SO para todos los tipos de conexiones:

    sudo sysctl -w net.core.wmem_max=$MaxExpectedPathBDP
        
  6. Establece la configuración del búfer de memoria de recepción de TCP (tcp_rmem):

    sudo sysctl -w net.ipv4.tcp_rmem="4096 87380 $MaxExpectedPathBDP"
        

    La configuración tcp_rmem toma tres valores:

    • El tamaño mínimo del búfer de recepción que se puede asignar para un socket TCP. En este ejemplo, el valor es 4096 bytes.
    • El tamaño predeterminado de búfer de recepción, que también anula el valor /proc/sys/net/core/rmem_default que usan otros protocolos. En el ejemplo, el valor es 87380 bytes.
    • El tamaño máximo del búfer de recepción que se puede asignar para un socket TCP. En el ejemplo, se establece según el valor que estableciste antes (8388608 bytes).
  7. Establece la configuración del búfer de la memoria de envío TCP (tcp_wmem):

    sudo sysctl -w net.ipv4.tcp_wmem="4096 16384 $MaxExpectedPathBDP"
        

    La configuración tcp_wmem toma tres valores:

    • El espacio mínimo de búfer de envío de TCP disponible para un solo socket TCP.
    • El espacio de búfer predeterminado permitido para un solo socket TCP.
    • El espacio máximo de búfer de envío TCP.
  8. Configura los parámetros de ajuste para que las conexiones posteriores usen los valores que especificaste:

    sudo sysctl -w net.ipv4.route.flush=1
        

Para conservar esta configuración en los reinicios, agrega los comandos que configuraste anteriormente en el archivo /etc/sysctl.conf:

sudo bash -c 'cat << EOF >> /etc/sysctl.conf
    net.core.rmem_max=8388608
    net.core.wmem_max=8388608
    net.ipv4.tcp_rmem=4096 87380 8388608
    net.ipv4.tcp_wmem=4096 16384 8388608
    net.ipv4.route.flush=1
    EOF'
    

Prueba de RTT con un tamaño de ventana actualizado

Cuando TCP tiene un tamaño de ventana lo suficientemente grande como para usar el BDP, la imagen cambia, como se muestra en el diagrama siguiente:

El remitente envía una gran cantidad de datos a la vez y pasa muy poco tiempo esperando una actualización de la ventana

El tamaño de ventana TCP siempre se puede adaptar en función de los recursos disponibles para el proceso involucrado y el algoritmo TCP en uso. Como se muestra en el diagrama, el escalamiento de ventana permite que una conexión supere el tamaño de ventana de 65 KiB definido en la especificación TCP original.

Puedes probarlo tú mismo. Primero, asegúrate de haber realizado cambios en el tamaño de la ventana TCP en tu computadora local y en una computadora remota. Para ello, configura los parámetros en ambas máquinas. Luego, ejecuta los comandos siguientes:

dd if=/dev/urandom of=sample.txt bs=1M count=1024 iflag=fullblock
    scp sample.txt your_username@remotehost.com:/some/remote/directory
    

El primer comando crea un archivo sample.txt de 1 GB que tiene datos aleatorios. El segundo comando copia ese archivo de tu máquina local a una máquina remota.

Ten en cuenta el resultado del comando scp en la consola, que muestra el ancho de banda en Kbps. Deberías ver una diferencia considerable en los resultados antes y después de que cambie el tamaño de ventana de TCP.

Próximos pasos