Cuando se integra un servicio de backend en el balanceador de cargas de aplicaciones, es importante medir el rendimiento del servicio de backend por sí solo si no hay un balanceador de cargas. Las pruebas de carga en condiciones controladas te ayudan a evaluar las compensaciones de la planificación de la capacidad entre diferentes dimensiones de rendimiento, como la capacidad de procesamiento y la latencia. Debido a que una planificación cuidadosa de la capacidad aún podría subestimar la demanda real, te recomendamos que uses pruebas de carga para determinar de forma proactiva cómo la disponibilidad de un servicio se ve afectada cuando el sistema se sobrecarga.
Objetivos de las pruebas de carga
Una prueba de carga típica mide el comportamiento visible de forma externa del servicio de backend en diferentes dimensiones de carga. Estas son algunas de las dimensiones más relevantes de estas pruebas:
- Capacidad de procesamiento solicitudes: Es la cantidad de solicitudes entregadas por segundo.
- Simultaneidad de solicitudes: La cantidad de solicitudes procesadas de forma simultánea.
- Capacidad de procesamiento de la conexión: La cantidad de conexiones que inician los clientes por segundo. La mayoría de los servicios que usan la seguridad de la capa de transporte (TLS) tienen cierta sobrecarga de transporte de red y de negociación de TLS asociada con cada conexión que es independiente del procesamiento de solicitudes.
Simultaneidad de conexiones: la cantidad de conexiones de clientes que se procesan de forma simultánea.
Latencia de la solicitud: Es el tiempo transcurrido total entre el inicio de la solicitud y el final de la respuesta.
Tasa de errores: Indica con qué frecuencia las solicitudes causan errores, como errores HTTP 5xx y conexiones cerradas de forma prematura.
Para evaluar el estado del servidor bajo carga, un procedimiento de prueba de carga también podría recoger las siguientes métricas de servicio internas:
Uso de recursos del sistema: Los recursos del sistema, como la CPU, la RAM y los identificadores de archivos (sockets), suelen expresarse en porcentaje.
La importancia de estas métricas difiere según cómo se implementa el servicio. Las aplicaciones experimentan un rendimiento menor, suprimen la carga o fallan cuando agotan sus recursos. Por lo tanto, es fundamental determinar la disponibilidad de los recursos cuando un host tiene una carga pesada.
Uso de otros recursos delimitados: Recursos que no son del sistema y que podrían agotarse con la carga, como en la capa de la aplicación.
Estos son algunos ejemplos de esos recursos:
- Un grupo delimitado de subprocesos o procesos de trabajador.
- En el caso de un servidor de aplicaciones que usa subprocesos, es común limitar la cantidad de subprocesos de trabajo que operan de forma simultánea. Los límites de tamaño del grupo de subprocesos son útiles para evitar el agotamiento de la memoria y la CPU, pero la configuración predeterminada suele ser muy conservadora. Los límites demasiado bajos pueden impedir el uso adecuado de los recursos del sistema.
- Algunos servidores usan grupos de procesos en lugar de grupos de subprocesos. Por ejemplo, un servidor Apache, cuando se configura con el modelo de procesamiento múltiple 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 máximo de la simultaneidad de la conexión.
- Un servicio implementado como frontend para otro servicio que tiene un grupo de conexiones de backend de tamaño limitado.
La planificación de la capacidad frente a las pruebas de sobrecarga
Las herramientas de pruebas de carga te ayudan a medir diferentes dimensiones de escalamiento de forma individual. Para la planificación de capacidad, determina el umbral de carga del rendimiento aceptable en varias dimensiones. Por ejemplo, en lugar de medir el límite absoluto de una solicitud de servicio en todo momento, considera medir lo siguiente:
- El porcentaje de solicitudes en el que el servicio puede entregar con una latencia del percentil 99, que es inferior a una cantidad especificada de milisegundos. El SLO del servicio especifica el número.
- El porcentaje de solicitudes máximo que no hace que el uso de recursos del sistema supere los niveles óptimos. Ten en cuenta que la utilización óptima varía según la aplicación y podría ser significativamente inferior al 100%. Por ejemplo, con un uso máximo de memoria del 80%, la aplicación podría manejar los aumentos de carga menores mejor que si el uso máximo fuera del 99%.
Si bien es importante usar los resultados de la prueba de carga para definir decisiones de planificación de capacidad, es igual de importante comprender cómo se comporta un servicio cuando la carga excede la capacidad. Algunos comportamientos del servidor que a menudo se evalúan mediante pruebas de sobrecarga son los siguientes:
Distribución de cargas: Cuando un servicio recibe solicitudes o conexiones entrantes excesivas, podría responder si ralentiza todas las solicitudes o si rechaza algunas solicitudes a fin de mantener un rendimiento aceptable para las restantes. Te recomendamos el último enfoque para evitar que se agote el tiempo de espera del cliente antes de recibir una respuesta y reducir el riesgo de agotamiento de la memoria disminuyendo la simultaneidad de solicitudes en el servidor.
Resiliencia contra el agotamiento de recursos: Por lo general, los servicios evitan las por agotamiento de recursos, ya que es difícil que las solicitudes pendientes progresen si el servicio falló. Si un servicio de backend tiene muchas instancias, la solidez de las instancias individuales es vital para la disponibilidad general del servicio. Si bien una instancia se reinicia luego de una falla, otras instancias pueden experimentar más carga, lo que podría causar una falla en cascada.
Lineamientos generales de pruebas
Cuando definas tus casos de prueba, ten en cuenta los siguientes lineamientos.
Crea pruebas a pequeña escala
Crea pruebas a pequeña escala para medir los límites de rendimiento del servidor. Con una capacidad excesiva del servidor, existe el riesgo de que una prueba no revele los límites de rendimiento del servicio, pero puede descubrir cuellos de botella en otros sistemas, como los hosts del cliente o la capa de red.
Para obtener mejores resultados, considera un caso de prueba que usa una sola instancia de máquina virtual (VM) o un pod de Google Kubernetes Engine (GKE) a fin de probar el servicio de forma independiente. Para lograr una carga completa en el servidor, si es necesario, puedes usar varias VMs, pero recuerda que pueden complicar la recopilación de datos de rendimiento.
Elige patrones de carga de bucle abierto
La mayoría de los generadores de cargas usan el patrón de bucle cerrado para limitar la cantidad de solicitudes simultáneas y retrasar las solicitudes nuevas hasta que se completen las anteriores. No recomendamos este enfoque porque es posible que los clientes de producción del servicio no muestren ese comportamiento de limitación.
El patrón de bucle abierto, en cambio, permite que los generadores de carga simulen la carga de producción enviando solicitudes a una velocidad constante, independientemente de la velocidad a la que llegan las respuestas del servidor.
Ejecuta pruebas con generadores de cargas recomendados
Recomendamos los siguientes generadores de cargas para las pruebas de carga del servicio de backend:
Nighthawk
Nighthawk es una herramienta de código abierto desarrollada en coordinación con el proyecto Envoy. Puedes usarlo a fin de generar una carga de cliente, visualizar comparativas y medir el rendimiento del servidor en la mayoría de las situaciones de pruebas de carga de los 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
Reemplaza lo siguiente:
URI
: Es el URI para realizar comparativas.DURATION
: Es el tiempo total de ejecución de prueba en segundos.REQ_BODY_SIZE
: Es el tamaño de la carga útil de POST en cada solicitud.CONCURRENCY
: Es la cantidad total de bucles de eventos simultáneos.Este número debe coincidir con el recuento de núcleos de la VM de cliente.
RPS
: Es la tasa objetivo de solicitudes por segundo por bucle de eventos.CONNECTIONS
: Es la cantidad 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, ten en cuenta que la latencia del percentil 99 es de alrededor de 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
Prueba 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
Reemplaza lo siguiente:
URI
: Es el URI para realizar comparativas.DURATION
: Es el tiempo total de ejecución de prueba en segundos.REQ_BODY_SIZE
: Es el tamaño de la carga útil de POST en cada solicitud.CONCURRENCY
: Es la cantidad total de bucles de eventos simultáneos.Este número debe coincidir con el recuento de núcleos de la VM de cliente.
RPS
: Es la tasa objetivo de solicitudes por segundo para cada bucle de eventos.MAX_ACTIVE_REQUESTS
: Es la cantidad máxima de solicitudes activas simultáneas para cada bucle de eventos.MAX_CONCURRENT_STREAMS
: Es la cantidad máxima de transmisiones simultáneas permitidas 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 un paquete en casi todas las distribuciones de Linux. ab
solo se recomienda para pruebas rápidas y simples.
Para instalar ab
, usa el siguiente comando:
- Ejecuta
sudo apt-get install apache2-utils
en Debian y Ubuntu. - En distribuciones basadas en Red Hat, ejecuta
sudo yum install httpd-utils
.
Después de instalar ab
, usa el siguiente comando para ejecutarlo:
ab -c CONCURRENCY \ -n NUM_REQUESTS \ -t TIMELIMIT \ -p POST_FILE URI
Reemplaza lo siguiente:
CONCURRENCY
: Es la cantidad de solicitudes simultáneas que se deben realizar.NUM_REQUESTS
: Cantidad de solicitudes que se deben realizarTIMELIMIT
: Es la cantidad máxima de segundos que se dedicarán a las solicitudes.POST_FILE
: Es el archivo local que contiene la carga útil HTTP POST.URI
: Es el URI para realizar comparativas.
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 600 segundos de tiempo transcurrido. El comando también incluye el contenido del archivo body
como una carga útil HTTP POST.
El comando ab
produce 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)