...

Receive Side Scaling a 10 y 25 Gbit/s: optimización del rendimiento para redes de servidores Linux modernas

Escalado del lado receptor distribuye el tráfico de red de forma selectiva a través de varios núcleos en enlaces de 10 y 25 Gbit/s, para que los servidores Linux puedan gestionar altos caudales con baja latencia. Mostraré de forma práctica cómo activo el RSS, el Cues en el mapa de núcleos y así evitar cuellos de botella en las interrupciones y las aciertos de caché.

Puntos centrales

Voy a resumir brevemente los aspectos más importantes para que puedas planificar rápidamente los próximos pasos.

  • Distribución de la carga: Los paquetes se distribuyen entre varios núcleos a través de varias colas.
  • Localidad de la caché: Un flujo permanece siempre en la misma cola.
  • Función hash: El hash de cuádruples distribuye los flujos de manera uniforme entre las colas.
  • afinidad: Una asignación específica de IRQ reduce las latencias.
  • Escala: A partir de 10/25 Gbit/s, RSS garantiza un alto rendimiento.

Estos puntos están interrelacionados y sustentan la Actuación sobre cargas de trabajo reales. Mi prioridad es, en primer lugar, el número correcto de colas y, a continuación, la CPU-Afinidad. A continuación, compruebo los parámetros de hash y los ajustes de precisión.

Qué ofrece el «Receive Side Scaling»

RSS divide la recepción de paquetes en varios Colas de recepción, que asigno a núcleos específicos de la CPU para que ningún núcleo concreto se convierta en un cuello de botella. Esto reduce los picos de interrupciones duras y suaviza el procesamiento mediante SoftIRQs, lo que reduce los picos de latencia y aumenta el rendimiento. Cada cola activa sus propias interrupciones, que asigno de forma fija a determinados núcleos para mantener la coherencia de las rutas de datos. Esta coherencia favorece la Cache-Localidad, porque un flujo siempre incide en el mismo núcleo. Es precisamente esta interacción la que, con altas tasas de PPS, se traduce directamente en una eficiencia cuantificable.

Así funciona el RSS desde el punto de vista técnico

La NIC crea, a partir de la IP de origen/destino y el puerto de origen/destino, un Hash y lo utiliza como índice para la tabla de indirección, que apunta a las colas. De este modo, los paquetes de un flujo siempre terminan en la misma cola y, por lo tanto, permanecen vinculados al mismo núcleo. Los distintos flujos se distribuyen de manera uniforme, siempre que las claves de hash y los campos de protocolo estén configurados adecuadamente. De este modo, la carga de trabajo se distribuye cerca de la Hardware, lo que hace que el núcleo tenga que realizar menos operaciones de equilibrio y se reduzca la sobrecarga. Eso es precisamente lo que quiero para mantener bajo el procesamiento de paquetes por núcleo a 10G/25G.

Por qué el RSS es importante a partir de 10 y 25 Gbit/s

A 1 Gbit/s, a menudo basta con un solo Núcleo la carga de paquetes, pero a partir de 10 Gbit/s el equilibrio se rompe rápidamente. Los paquetes pequeños hacen que aumente la cifra de PPS, lo que provoca que un núcleo alcance rápidamente el 100 % de carga y se produzcan caídas. Es precisamente entonces cuando el RSS actúa como un multiplicador del ancho de banda útil. Distribuyo la carga entre varios Núcleos, reduce los cambios de contexto y mantiene las curvas de latencia más estables. El resultado: el rendimiento real solo se aproxima a la velocidad de enlace cuando el RSS funciona correctamente.

Configurar RSS en el servidor Linux

En Linux, gestiono el RSS principalmente a través de ethtool, opciones del controlador y sysfs, para que las capacidades de la tarjeta de red se apliquen realmente. Primero leo el número máximo de canales de recepción (RX) y, a continuación, establezco un número de colas adecuado para la CPU. A continuación, compruebo el hash RSS para TCP/UDP y, opcionalmente, para VLAN o túneles, con el fin de que los perfiles de carga se distribuyan correctamente. Para el ruido de fondo de la distribución de interrupciones, me ayuda Equilibrio de IRQ, aunque prefiero marcar manualmente las colas críticas. Así es como las vinculo Cues se adapte perfectamente a la topología del host y evite desplazamientos indeseados.

Tabla de indirección, clave RSS y ajuste fino del hash: comandos concretos

En primer lugar, compruebo la distribución actual y la clave de la tarjeta de red:

ethtool -x eth0 #: mostrar la tabla de indirección (colas de recepción) y la clave RSS
ethtool -n eth0 rx-flow-hash tcp4
ethtool -n eth0 rx-flow-hash udp4

Para conseguir una distribución limpia y uniforme, configuro la tabla de indirección con el número de colas deseado. Con 16 colas, elijo una asignación uniforme:

ethtool -X eth0 equal 16  Distribución # de forma uniforme en 16 colas

Si es necesario, adapto los campos de hash. Para TCP4 con cuádruples (s = IP de origen, d = IP de destino, f = puerto de origen, n = puerto de destino):

ethtool -N eth0 rx-flow-hash tcp4 sdfn
ethtool -N eth0 rx-flow-hash udp4 sdfn
ethtool -N eth0 rx-flow-hash tcp6 sdfn
ethtool -N eth0 rx-flow-hash udp6 sdfn

Algunos controladores también permiten establecer una clave RSS propia (por ejemplo, para una mejor distribución en casos especiales):

ethtool -X eth0 hkey   # solo si el controlador o la tarjeta de red lo admiten

Configurar correctamente la afinidad de la CPU y NUMA

Asigno cada cola de recepción mediante Afinidad IRQ asigna las colas a núcleos específicos y ten en cuenta la arquitectura NUMA, para que los datos recorran una distancia mínima a través del controlador de memoria. Si la tarjeta de red (NIC) se ejecuta en el nodo 0, también asigno las colas principales a núcleos del nodo 0 y coloco las cargas de trabajo cerca de ellos. Esta proximidad reduce los accesos remotos y disminuye considerablemente las latencias de memoria. Para ello, resulta útil disponer de un perfil para las colas productivas, así como de núcleos separados para las tareas de gestión y descarga. Quien desee profundizar más, encontrará indicaciones para el ajuste fino en Afinidad IRQ, en lo que respecta a la planificación por Núcleo simplificado.

Guía práctica sobre la afinidad de IRQ: de las IRQ a una asignación estable de núcleos

Primero averiguo qué IRQ corresponden a las colas de recepción y, a continuación, las asigno de forma fija:

grep -E "eth0.*Rx" /proc/interrupts
cat /sys/class/net/eth0/device/numa_node

La asignación la realizo mediante smp_affinity_list, para no tener que hacer cálculos complicados. Ejemplo: colas RX 0–7 en los núcleos 2–9:

Ejemplo #: asignar IRQ a los núcleos 2-9 (una línea por IRQ)
echo 2  > /proc/irq//smp_affinity_list
echo 3  > /proc/irq//smp_affinity_list
echo 4  > /proc/irq//smp_affinity_list
...
echo 9  > /proc/irq//smp_affinity_list

Importante: MSI-X debe estar activado para que cada cola tenga sus propias interrupciones. Si utilizo la asignación manual de pines, bloqueo irqbalance para estas IRQ (por ejemplo, mediante una lista negra) o desactivar el servicio de forma selectiva en los hosts con una estructura estática. Además, compruebo NUMA con lscpu y la asignación de PCIe, para no generar rutas entre nodos.

Configuración de hash y protocolos

Defino los campos hash de tal manera que los auténticos Tráfico-Distribuir bien los patrones, en lugar de que se concentren en unas pocas colas. Para TCP/UDP utilizo el cuádruplet; para IPv6, algo similar, mientras que en VXLAN o GRE tengo en cuenta campos adicionales de la encapsulación. Algunas tarjetas de red ofrecen claves de hash configurables, que adapto a la carga de trabajo dominante. En cuanto observo una concentración de carga en colas concretas, reajusto la selección de hash. Este paso requiere poco tiempo, pero evita una desequilibrio con un elevado número de conexiones.

Coalescencia de interrupciones y PPS

Combino RSS con un uso moderado de Interrupción coalescente, para agrupar el tráfico con gran volumen de PPS en lotes manejables. Esto reduce la sobrecarga de interrupciones, pero no debe aumentar la latencia de los servicios más sensibles. Por eso mido los tiempos de ida y vuelta y modifico los valores de coalescencia de forma gradual. Quien gestione cargas de almacenamiento o copias de seguridad puede agruparlas de forma más clara que en el caso de las API de capa 7 o el VoIP. En resumen, equilibro Latencia frente al rendimiento, hasta que ambos coincidan.

La coalescencia en la práctica: perfiles y puntos de medición

Empiezo con unos valores predeterminados moderados y voy ajustándolos poco a poco hasta alcanzar el nivel óptimo para cada carga de trabajo. Tres perfiles iniciales que han dado buenos resultados:

  • API/Baja latencia: rx-usecs 2–6, rx-frames 16–32, adaptativo desactivado
  • Versátil: rx-usecs 8–16, rx-frames 32–64, adaptativa a
  • A granel/Almacenamiento: 24-48 rx-usecs, rx-frames 128–256, adaptativa a
ethtool -c eth0
ethtool -C eth0 rx-usecs 12 rx-frames 64 adaptive-rx on

Para ello, mido las latencias p95/p99, el PPS, la carga de la CPU por núcleo y las retransmisiones. En cuanto observo un aumento de la varianza en API/VoIP, procedo a rx-usecs de nuevo hacia abajo. En el almacenamiento, prefiero escalar hacia arriba mediante fotogramas para ahorrar interrupciones.

RSS en entornos de 10 Gbit

En las tarjetas de red de 10G, suelo trabajar con valores de entre 8 y 16 Cues por puerto, siempre que la CPU disponga de suficientes núcleos. De este modo, los servidores web, las pasarelas de almacenamiento y los hosts de virtualización se escalan correctamente a través de numerosas conexiones paralelas. Asigno las colas principales a los núcleos libres y, a continuación, mido el PPS, la latencia y las retransmisiones. Si se producen pérdidas de paquetes, compruebo la coalescencia, el hash y la utilización de cada cola. A continuación, ajusto la afinidad, hasta que la carga de trabajo parezca uniforme.

RSS en configuraciones de 25 Gbit y Multi-25G

A 25 Gbit/s, el PPS y la carga del bus aumentan, por lo que yo NUMA-Presto más atención a la gestión de colas y a las descargas. La descarga de recepción de grandes paquetes (LRO) o RSC pueden reducir la carga de paquetes en la pila, siempre que las aplicaciones lo toleren. Además, compruebo los carriles PCIe para descartar cuellos de botella ajenos a la red. En hosts con varios enlaces de 25G, separo las colas y la afinidad de forma estricta según las tareas y los nodos. Así es como utilizo Ancho de banda y núcleos de forma eficiente, sin caer en el tráfico entre nodos.

Detalles sobre el hardware y los controladores: lo que tengo en cuenta

No todas las tarjetas de red se comportan igual. Las generaciones de Intel (por ejemplo, ixgbe, i40e, ice) ofrecen funciones como Flow Director/ATR, que vinculan los flujos de forma específica a colas, lo cual resulta útil si quiero suavizar los picos de tráfico. Mellanox mlx5 puede admitir aRFS por hardware, lo que reduce la carga de la CPU cuando la pila atiende a muchos sockets. Decido caso por caso si activo estas funciones y mido si mejoran la distribución. En los sistemas de enrutamiento/NAT, suelo desactivar LRO y utilizo GRO para mantener la coherencia de las cabeceras; en cargas de trabajo exclusivamente de servidor, LRO/GRO pueden ayudar a mitigar la presión de PPS. También es importante disponer de suficientes vectores MSI-X por cola y de versiones de firmware correctas.

RSS en virtualización y contenedores

En el hipervisor combino los recursos físicos RSS-Colas con vNIC compatibles con múltiples colas, como virtio-net, para que los sistemas invitados no sufran cuellos de botella artificiales. Presto atención a la asignación de CPU de las máquinas virtuales y configuro la proximidad NUMA de sus vCPU a la NIC física. De este modo, los datos permanecen en el entorno local y el host paga menos por los accesos a la memoria. En el caso de los contenedores, asigno los pods críticos a los núcleos adecuados y mantengo las colas del host libres de carga molesta. Este orden aumenta el Eficacia en el caso de los microservicios, donde se generan muchos flujos pequeños.

Cómo utilizar correctamente SR-IOV y VF-RSS

Con SR-IOV asigno a las máquinas virtuales (VM) sus propias funciones virtuales (VF), que a su vez pueden proporcionar varias colas y RSS. Preveo un número suficiente de VF por puerto, tengo en cuenta la capacidad de MSI-X y asigno las IRQ de las VF en la máquina virtual de acuerdo con sus vCPU. En los sistemas invitados de Linux, activo explícitamente la función Multi-Queue; de lo contrario, la vNIC suele permanecer en un solo nivel:

# en el sistema invitado (ejemplo de virtio-net)
ethtool -l eth0
ethtool -L eth0 combined 4

Distribuyo los hosts con varias máquinas virtuales (VM) estrictamente por nodo NUMA y por carga de trabajo, para que las máquinas virtuales no se interfieran entre sí en las mismas rutas físicas de recepción (RX).

Supervisión y resolución de problemas

Superviso la ocupación por Cola, núcleos individuales, pérdidas de paquetes y retransmisiones, para detectar desequilibrios en una fase temprana. Si un núcleo se destaca y los demás permanecen inactivos, a menudo es porque la afinidad o el número de colas no son los adecuados. En esos casos, compruebo uno tras otro los campos hash, las máscaras de IRQ y los valores de coalescencia. Además, compruebo el Carga de SoftIRQ, porque ofrece indicios de efectos de desplazamiento. Solo cuando estas señales parecen estables, aumente el tráfico o amplíe Cues continuar.

RPS, RFS y XPS: complementos de software para RSS

Si una tarjeta de red tiene pocas colas o si utilizo bonding o tunneling, complemento RSS con RPS (Receive Packet Steering) y RFS (Receive Flow Steering). RPS distribuye las SoftIRQ entre los núcleos, mientras que RFS vincula los flujos al núcleo en el que está activo el socket correspondiente. Yo activo ambos de forma selectiva:

# Aumentar el número global de entradas de flujo (RFS)
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# Configurar el número de CPU por cola de recepción para RPS (máscara de ejemplo, ¡adaptarla!)
for f in /sys/class/net/eth0/queues/rx-*/rps_cpus; do echo ffff > "$f"; done

Configurar la tabla de flujos por cola de recepción # para RFS
for f in /sys/class/net/eth0/queues/rx-*/rps_flow_cnt; do echo 4096 > "$f"; done

En el lado de TX utilizo XPS (Transmit Packet Steering), para que los paquetes salientes sean enviados desde el núcleo que los ha generado:

for f in /sys/class/net/eth0/queues/tx-*/xps_cpus; do echo ffff > "$f"; done

RPS/RFS/XPS consumen algo de CPU, pero resultan útiles cuando me faltan colas a nivel de hardware o cuando quiero mantener estrictamente la localidad de los sockets.

Rendimiento de flujo único, GRO/TSO y sondeo de actividad

Un flujo individual permanece vinculado a un núcleo por una buena razón. Si quiero aumentar el ancho de banda de un flujo individual, apuesto por las descargas (GRO/TSO), una frecuencia de núcleo elevada y una coalescencia adecuada. Para las rutas en las que la latencia es crítica, se puede Sondeo ocupado ayudar:

Establecer un valor bajo para # y realizar la medición
sysctl -w net.core.busy_read=25
sysctl -w net.core.busy_poll=25

El «busy polling» reduce los cambios de contexto, pero consume tiempo de CPU. Solo lo activo cuando las latencias p99 son importantes, y siempre compruebo los efectos sobre la carga total y la latencia de cola. Por lo general, mantengo GRO en los servidores y utilizo LRO en función de su función; en los middleboxes, opto por un enfoque conservador para no interferir en el procesamiento de encabezados ni en la consistencia de los hash.

Recomendaciones y ejemplos: colas, afinidad, comandos

Como punto de partida, elijo un número de colas que se adapte a la CPU Ajusta los valores, observa la carga por cola y ve ajustándolos poco a poco. Con 10G suelen bastar entre 8 y 16 colas; con 25G suelo establecer un número mayor, siempre que haya núcleos disponibles. Para la afinidad, utilizo máscaras claras por cada IRQ, para poder analizar más fácilmente las rutas posteriormente. La siguiente tabla ofrece valores orientativos resumidos, que luego verifico mediante mediciones. Solo los resultados de las mediciones determinan si más aumenta o reduce.

Velocidad de conexión Colas RX típicas Ejemplos de comandos Notas
10 Gbit/s 8-16 ethtool -l eth0 | ethtool -L eth0 rx 16 Coalescente mantener un nivel moderado, comprobar la latencia L7
25 Gbit/s 16–32+ grep . /proc/interrupts | Máscaras de IRQ mediante echo NUMA Tener en cuenta: comprobar los carriles PCIe
Multi-25G Por puerto, por separado Activar vNIC con colas múltiples (por ejemplo, virtio) Colas por núcleos y Cargas de trabajo dividir

Estos valores orientativos son solo un punto de partida, no el objetivo, ya que las cargas de trabajo varían mucho. Registro los cambios, realizo mediciones antes y después del ajuste y, por lo demás, mantengo el entorno sin modificaciones. En cuanto el sistema se mantiene estable bajo carga de producción, congelo la configuración. Más adelante, vuelvo a realizar mediciones tras las actualizaciones del núcleo o de los controladores. De este modo, me mantengo en RSS Un proceso bien encaminado y resultados seguros y reproducibles.

Problemas habituales y soluciones

Demasiado pocos Cues Sobrecargar núcleos concretos; un número excesivo aumenta la carga administrativa y perjudica la tasa de aciertos de la caché. Una afinidad inadecuada desvía las interrupciones hacia núcleos ya sobrecargados o hacia nodos NUMA incorrectos. Asimismo, un hash inadecuado provoca que los flujos dominantes obstruyan las colas. Lo resuelvo paso a paso: ajustar el número de colas, corregir la afinidad, ampliar los campos de hash y ajustar con precisión la coalescencia. Justifico cada cambio con Métricas, antes de pasar a la siguiente palanca.

Escenarios prácticos

Un servidor de almacenamiento con 10G se beneficia rápidamente de 8-12 Cues además de una coalescencia moderada, para que las transferencias masivas se realicen sin problemas. Un servidor de API con un elevado número de conexiones suele necesitar campos hash más precisos y una menor latencia en las interrupciones. Los hosts de virtualización se benefician notablemente cuando la función «vNIC-Multi-Queue» está activa en el lado del invitado y se adapta a la configuración del host. Las cargas de trabajo de los contenedores funcionan mejor cuando los pods críticos se ejecutan cerca de la NIC y de la memoria NUMA. Amplío estos patrones según la situación, mediante PPS, compara las retransmisiones y la distribución de colas.

Las plataformas de alto rendimiento como ventaja

Configuraciones de alojamiento con RSS, las tarjetas de red con múltiples colas y una afinidad bien definida aportan reservas apreciables en momentos de máxima carga. Quien evalúe ofertas de servidores debería preguntar específicamente por la capacidad de múltiples colas, el «NUMA-Pinning» y la supervisión. Un proveedor que aplique estos aspectos de forma visible suele obtener curvas de rendimiento notablemente mejores. Para soluciones de servidores y alojamiento de alto rendimiento, recomiendo sin duda alguna webhoster.de. Esta orientación da sus frutos en Actuación y estabilidad, sobre todo cuando hay muchos flujos paralelos.

Resumen para la práctica

Activo Reciba Escalado lateral: establezco un número razonable de colas, asigno las IRQ a los núcleos adecuados y compruebo la configuración del hash. A continuación, optimizo la coalescencia para reducir la latencia, presto atención a la proximidad NUMA y distribuyo las cargas de trabajo de forma coherente. En virtualización, utilizo colas múltiples hasta en los sistemas invitados y mantengo sincronizados el pinning y la afinidad. Las mediciones de PPS, carga de colas, retransmisiones y latencia determinan el siguiente paso. Quien proceda así, sacará el máximo partido a 10G y 25G, y mantendrá Latencia en este contexto y obtiene un rendimiento de red fiable.

Artículos de actualidad