Directrices para probar la carga de servicios de backend con balanceadores de carga de aplicaciones

Al integrar un servicio de backend con un balanceador de carga de aplicaciones, es importante medir el rendimiento del servicio de backend por sí solo, sin un balanceador de carga. Las pruebas de carga en condiciones controladas te ayudan a evaluar las compensaciones de la planificación de la capacidad entre las diferentes dimensiones del rendimiento, como el rendimiento y la latencia. Como una planificación de la capacidad cuidadosa podría seguir subestimando la demanda real, te recomendamos que uses pruebas de carga para determinar de forma proactiva cómo se ve afectada la disponibilidad de un servicio cuando el sistema está sobrecargado.

Objetivos de las pruebas de carga

Una prueba de carga típica mide el comportamiento visible externamente del servicio backend en diferentes dimensiones de carga. Estas son algunas de las dimensiones más relevantes de esta prueba:

  • Rendimiento de las solicitudes: el número de solicitudes atendidas por segundo.
  • Simultaneidad de las solicitudes: número de solicitudes procesadas simultáneamente.
  • Rendimiento de conexión: número de conexiones iniciadas por los clientes por segundo. La mayoría de los servicios que usan Seguridad en la capa de transporte (TLS) tienen una sobrecarga de transporte de red y negociación de TLS asociada a cada conexión, que es independiente del procesamiento de solicitudes.
  • Concurrencia de conexiones: número de conexiones de cliente procesadas simultáneamente.

  • Latencia de la solicitud: tiempo total transcurrido entre el inicio de la solicitud y el final de la respuesta.

  • Tasa de errores: frecuencia con la que las solicitudes provocan errores, como errores HTTP 5xx y conexiones cerradas prematuramente.

Para evaluar el estado del servidor bajo carga, un procedimiento de prueba de carga también puede recoger las siguientes métricas de servicio internas:

  • Uso de recursos del sistema: los recursos del sistema, como la CPU, la RAM y los controladores de archivos (sockets), suelen expresarse en porcentaje.

    La importancia de estas métricas varía en función de cómo se implemente el servicio. Las aplicaciones experimentan un rendimiento reducido, pierden carga o fallan cuando agotan sus recursos. Por lo tanto, es fundamental determinar la disponibilidad de los recursos cuando un host está sometido a una carga elevada.

  • Uso de otros recursos limitados: recursos que no son del sistema y que podrían agotarse bajo carga, como los de la capa de aplicación.

    Estos son algunos ejemplos de recursos:

    • Un grupo limitado de hilos o procesos de trabajadores.
    • En el caso de un servidor de aplicaciones que usa subprocesos, es habitual limitar el número de subprocesos de trabajo que operan simultáneamente. Los límites de tamaño del grupo de subprocesos son útiles para evitar que se agote la memoria y la CPU, pero los ajustes predeterminados suelen ser muy conservadores. Si los límites son demasiado bajos, es posible que no se puedan usar los recursos del sistema de forma adecuada.
    • Algunos servidores usan grupos de procesos en lugar de grupos de subprocesos. Por ejemplo, un servidor Apache, cuando se configura con el modelo de multiprocesamiento Prefork, asigna un proceso a cada conexión de cliente. Por lo tanto, el límite de tamaño del grupo determina el límite superior de la simultaneidad de las conexiones.
    • Servicio desplegado como frontend de otro servicio que tiene un grupo de conexiones de backend de tamaño limitado.

Planificación de la capacidad frente a pruebas de sobrecarga

Las herramientas de pruebas de carga te ayudan a medir diferentes dimensiones de escalado de forma individual. Para la planificación de la capacidad, determina el umbral de carga para el rendimiento aceptable en varias dimensiones. Por ejemplo, en lugar de medir el límite absoluto de una solicitud de servicio, puedes medir lo siguiente:

  • La tasa de solicitudes a la que el servicio puede responder con una latencia del percentil 99 inferior a un número de milisegundos especificado. El número se especifica en el objetivo de nivel de servicio del servicio.
  • La frecuencia de solicitudes máxima que no provoca que la utilización de los recursos del sistema supere los niveles óptimos. Ten en cuenta que la utilización óptima varía en función de la aplicación y puede ser significativamente inferior al 100%. Por ejemplo, con un uso máximo de memoria del 80 %, la aplicación podría gestionar picos de carga menores mejor que si el uso máximo fuera del 99%.

Aunque es importante usar los resultados de las pruebas de carga para tomar decisiones sobre la planificación de la capacidad, también es fundamental saber cómo se comporta un servicio cuando la carga supera la capacidad. A continuación se indican algunos comportamientos del servidor que suelen evaluarse mediante pruebas de sobrecarga:

  • Reducción de carga: cuando un servicio recibe demasiadas solicitudes o conexiones entrantes, puede responder ralentizando todas las solicitudes o rechazando algunas para mantener un rendimiento aceptable en las demás. Te recomendamos que uses este último método para evitar que se agote el tiempo de espera del cliente antes de recibir una respuesta y para reducir el riesgo de que se agote la memoria disminuyendo la simultaneidad de las solicitudes en el servidor.

  • Resistencia frente al agotamiento de recursos: un servicio suele evitar que se produzcan fallos por agotamiento de recursos, ya que es difícil que las solicitudes pendientes avancen si el servicio ha fallado. Si un servicio de backend tiene muchas instancias, la solidez de las instancias individuales es vital para la disponibilidad general del servicio. Mientras una instancia se reinicia tras un fallo, otras instancias pueden experimentar más carga, lo que podría provocar un fallo en cascada.

Directrices generales para las pruebas

Al definir los casos de prueba, ten en cuenta las siguientes directrices.

Crear pruebas a pequeña escala

Crea pruebas a pequeña escala para medir los límites de rendimiento del servidor. Si la capacidad del servidor es excesiva, es posible que una prueba no revele los límites de rendimiento del servicio en sí, pero sí que descubra cuellos de botella en otros sistemas, como los hosts de los clientes o la capa de red.

Para obtener los mejores resultados, considera un caso de prueba que utilice una sola instancia de máquina virtual (VM) o un pod de Google Kubernetes Engine (GKE) para probar el servicio de forma independiente. Para conseguir una carga completa en el servidor, puedes usar varias máquinas virtuales si es necesario, pero ten en cuenta que pueden complicar la recogida de datos de rendimiento.

Elegir patrones de carga de bucle abierto

La mayoría de los generadores de carga usan el patrón de bucle cerrado para limitar el número de solicitudes simultáneas y retrasar las nuevas solicitudes hasta que se completen las anteriores. No recomendamos este enfoque porque es posible que los clientes de producción del servicio no muestren este comportamiento de limitación.

Por el contrario, el patrón de bucle abierto permite que los generadores de carga simulen la carga de producción enviando solicitudes a un ritmo constante, independientemente del ritmo al que lleguen las respuestas del servidor.

Recomendamos los siguientes generadores de carga para probar la carga del servicio backend:

Nighthawk

Nighthawk es una herramienta de código abierto desarrollada en coordinación con el proyecto Envoy. Puedes usarla para generar carga de cliente, visualizar comparativas y medir el rendimiento del servidor en la mayoría de los casos de prueba de carga de servicios HTTPS.

Prueba HTTP/1

Para probar HTTP/1, usa el siguiente comando:

nighthawk_client URI \
    --duration DURATION \
    --open-loop \
    --no-default-failure-predicates \
    --protocol http1 \
    --request-body-size REQ_BODY_SIZE \
    --concurrency CONCURRENCY \
    --rps RPS \
    --connections CONNECTIONS

Haz los cambios siguientes:

  • URI: el URI de referencia
  • DURATION: tiempo total de ejecución de la prueba en segundos
  • REQ_BODY_SIZE: tamaño de la carga útil POST en cada solicitud
  • CONCURRENCY: número total de bucles de eventos simultáneos.

    Este número debe coincidir con el número de núcleos de la VM cliente.

  • RPS: la tasa objetivo de solicitudes por segundo por bucle de eventos.

  • CONNECTIONS: número de conexiones simultáneas por bucle de eventos.

Consulta el siguiente ejemplo:

nighthawk_client http://10.20.30.40:80 \
    --duration 600 --open-loop --no-default-failure-predicates \
    --protocol http1 --request-body-size 5000 \
    --concurrency 16 --rps 500 --connections 200

El resultado de cada ejecución de prueba proporciona un histograma de las latencias de respuesta. En el ejemplo de la documentación de Nighthawk , observa que la latencia del percentil 99 es de aproximadamente 135 microsegundos.

Initiation to completion
    samples: 9992
    mean:    0s 000ms 113us
    pstdev:  0s 000ms 061us

    Percentile  Count       Latency
    0           1           0s 000ms 077us
    0.5         4996        0s 000ms 115us
    0.75        7495        0s 000ms 118us
    0.8         7998        0s 000ms 118us
    0.9         8993        0s 000ms 121us
    0.95        9493        0s 000ms 124us
    0.990625    9899        0s 000ms 135us
    0.999023    9983        0s 000ms 588us
    1           9992        0s 004ms 090us

Probar HTTP/2

Para probar HTTP/2, usa el siguiente comando:

nighthawk_client URI \
    --duration DURATION \
    --open-loop \
    --no-default-failure-predicates \
    --protocol http2 \
    --request-body-size REQ_BODY_SIZE \
    --concurrency CONCURRENCY \
    --rps RPS \
    --max-active-requests MAX_ACTIVE_REQUESTS \
    --max-concurrent-streams MAX_CONCURRENT_STREAMS

Haz los cambios siguientes:

  • URI: el URI de referencia
  • DURATION: tiempo total de ejecución de la prueba en segundos
  • REQ_BODY_SIZE: tamaño de la carga útil POST en cada solicitud
  • CONCURRENCY: número total de bucles de eventos simultáneos.

    Este número debe coincidir con el número de núcleos de la VM cliente.

  • RPS: la tasa objetivo de solicitudes por segundo de cada bucle de eventos

  • MAX_ACTIVE_REQUESTS: número máximo de solicitudes activas simultáneas para cada bucle de eventos.

  • MAX_CONCURRENT_STREAMS: número máximo de flujos simultáneos permitidos en cada conexión HTTP/2

Consulta el siguiente ejemplo:

nighthawk_client http://10.20.30.40:80 \
    --duration 600 --open-loop --no-default-failure-predicates \
    --protocol http2 --request-body-size 5000 \
    --concurrency 16 --rps 500 \
    --max-active-requests 200 --max-concurrent-streams 1

ab (herramienta de comparativas de Apache)

ab es una alternativa menos flexible a Nighthawk, pero está disponible como paquete en casi todas las distribuciones de Linux. ab solo se recomienda para pruebas rápidas y sencillas.

Para instalar ab, usa el siguiente comando:

  • En Debian y Ubuntu, ejecuta sudo apt-get install apache2-utils.
  • En las distribuciones basadas en Red Hat, ejecuta sudo yum install httpd-utils.

Una vez que hayas instalado ab, usa el siguiente comando para ejecutarlo:

ab -c CONCURRENCY \
    -n NUM_REQUESTS \
    -t TIMELIMIT \
    -p POST_FILE URI

Haz los cambios siguientes:

  • CONCURRENCY: número de solicitudes simultáneas que se van a realizar
  • NUM_REQUESTS: número de solicitudes que se van a realizar
  • TIMELIMIT: número máximo de segundos que se pueden dedicar a las solicitudes
  • POST_FILE: archivo local que contiene la carga útil de HTTP POST
  • URI: el URI de referencia

Consulta el siguiente ejemplo:

ab -c 200 -n 1000000 -t 600 -p body http://10.20.30.40:80

El comando del ejemplo anterior envía solicitudes con una simultaneidad de 200 (patrón de bucle cerrado) y se detiene después de 1.000.000 (un millón) de solicitudes o de 600 segundos de tiempo transcurrido. El comando también incluye el contenido del archivo body como carga útil POST de HTTP.

El comando ab genera histogramas de latencia de respuesta similares a los de Nighthawk, pero su resolución se limita a milisegundos en lugar de microsegundos:

Percentage of the requests served within a certain time (ms)
    50%     7
    66%     7
    75%     7
    80%     7
    90%    92
    95%   121
    98%   123
    99%   127
    100%  156 (longest request)

Siguientes pasos