SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

El análisis y ajuste del rendimiento es una herramienta poderosa para verificar el cumplimiento del rendimiento para los clientes.

El análisis de rendimiento se puede utilizar para comprobar si hay cuellos de botella en un programa aplicando un enfoque científico para probar experimentos de ajuste. Este artículo define un enfoque general para el análisis y ajuste del rendimiento, utilizando un servidor web Go como ejemplo.

Go es especialmente bueno aquí porque tiene herramientas de creación de perfiles. pprof en la biblioteca estándar.

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

estrategia

Creemos una lista resumida para nuestro análisis estructural. Intentaremos utilizar algunos datos para tomar decisiones en lugar de realizar cambios basados ​​en la intuición o conjeturas. Para ello haremos esto:

  • Determinamos los límites de optimización (requisitos);
  • Calculamos la carga de transacciones para el sistema;
  • Realizamos la prueba (creamos datos);
  • Observamos;
  • Analizamos: ¿se cumplen todos los requisitos?
  • Lo planteamos científicamente, formulamos una hipótesis;
  • Realizamos un experimento para probar esta hipótesis.

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Arquitectura de servidor HTTP sencilla

Para este artículo utilizaremos un pequeño servidor HTTP en Golang. Todo el código de este artículo se puede encontrar. aquí.

La aplicación que se analiza es un servidor HTTP que sondea Postgresql para cada solicitud. Además, existen Prometheus, node_exporter y Grafana para recopilar y mostrar métricas de aplicaciones y sistemas.

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Para simplificar, consideramos que para el escalamiento horizontal (y la simplificación de los cálculos), cada servicio y base de datos se implementan juntos:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Definiendo objetivos

En este paso, decidimos el objetivo. ¿Qué estamos tratando de analizar? ¿Cómo sabemos cuándo es el momento de terminar? En este artículo imaginaremos que tenemos clientes y que nuestro servicio procesará 10 solicitudes por segundo.

В Libro de Google SRE Los métodos de selección y modelado se analizan en detalle. Hagamos lo mismo y construyamos modelos:

  • Latencia: el 99% de las solicitudes deben completarse en menos de 60 ms;
  • Coste: El servicio debe consumir la mínima cantidad de dinero que creamos razonablemente posible. Para hacer esto, maximizamos el rendimiento;
  • Planificación de capacidad: requiere comprender y documentar cuántas instancias de la aplicación serán necesarias ejecutar, incluida la funcionalidad de escalamiento general, y cuántas instancias serán necesarias para cumplir con los requisitos de carga y aprovisionamiento iniciales. redundancia n+1.

La latencia puede requerir optimización además del análisis, pero claramente es necesario analizar el rendimiento. Cuando se utiliza el proceso SRE SLO, la solicitud de retraso proviene del cliente o empresa, representado por el propietario del producto. ¡Y nuestro servicio cumplirá con esta obligación desde el principio sin ninguna configuración!

Configurar un entorno de prueba

Con la ayuda de un entorno de prueba, podremos colocar una carga medida en nuestro sistema. Para su análisis se generarán datos sobre el rendimiento del servicio web.

Carga de transacciones

Este entorno utiliza Vegetar para crear una tasa de solicitud HTTP personalizada hasta que se detenga:

$ make load-test LOAD_TEST_RATE=50
echo "POST http://localhost:8080" | vegeta attack -body tests/fixtures/age_no_match.json -rate=50 -duration=0 | tee results.bin | vegeta report

Vigilancia

La carga transaccional se aplicará en tiempo de ejecución. Además de las métricas de la aplicación (cantidad de solicitudes, latencia de respuesta) y del sistema operativo (memoria, CPU, IOPS), se ejecutará la creación de perfiles de la aplicación para comprender dónde tiene problemas y cómo se consume el tiempo de la CPU.

perfilado

La creación de perfiles es un tipo de medición que le permite ver a dónde va el tiempo de la CPU cuando se ejecuta una aplicación. Le permite determinar exactamente dónde y cuánto tiempo del procesador se gasta:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Estos datos se pueden utilizar durante el análisis para obtener información sobre el tiempo de CPU desperdiciado y el trabajo innecesario que se realiza. Go (pprof) puede generar perfiles y visualizarlos como gráficos de llamas utilizando un conjunto estándar de herramientas. Hablaré sobre su uso y guía de configuración más adelante en el artículo.

Ejecución, observación, análisis.

Hagamos un experimento. Actuaremos, observaremos y analizaremos hasta que estemos satisfechos con la actuación. Elijamos un valor de carga arbitrariamente bajo para aplicarlo y obtener los resultados de las primeras observaciones. En cada paso posterior aumentaremos la carga con un determinado factor de escala, elegido con alguna variación. Cada ejecución de prueba de carga se realiza con el número de solicitudes ajustadas: make load-test LOAD_TEST_RATE=X.

50 solicitudes por segundo

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Preste atención a los dos gráficos superiores. La parte superior izquierda muestra que nuestra aplicación procesa 50 solicitudes por segundo (cree) y la parte superior derecha muestra la duración de cada solicitud. Ambos parámetros nos ayudan a mirar y analizar si estamos dentro de nuestros límites de desempeño o no. Línea roja en el gráfico Latencia de solicitud HTTP muestra SLO a 60 ms. La línea muestra que estamos muy por debajo de nuestro tiempo máximo de respuesta.

Veamos el lado de los costos:

10000 solicitudes por segundo / 50 solicitudes por servidor = 200 servidores + 1

Aún podemos mejorar esta cifra.

500 solicitudes por segundo

Cosas más interesantes empiezan a suceder cuando la carga llega a 500 solicitudes por segundo:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Nuevamente, en el gráfico superior izquierdo puedes ver que la aplicación está registrando una carga normal. Si este no es el caso, hay un problema en el servidor en el que se ejecuta la aplicación. El gráfico de latencia de respuesta se encuentra en la parte superior derecha y muestra que 500 solicitudes por segundo dieron como resultado un retraso de respuesta de 25 a 40 ms. El percentil 99 todavía encaja muy bien en el SLO de 60 ms elegido anteriormente.

En términos de costo:

10000 solicitudes por segundo / 500 solicitudes por servidor = 20 servidores + 1

Todo aún se puede mejorar.

1000 solicitudes por segundo

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

¡Gran lanzamiento! La aplicación muestra que procesó 1000 solicitudes por segundo, pero el SLO violó el límite de latencia. Esto se puede ver en la línea p99 en el gráfico superior derecho. A pesar de que la línea p100 es mucho más alta, los retrasos reales son superiores al máximo de 60 ms. Profundicemos en la creación de perfiles para descubrir qué hace realmente la aplicación.

perfilado

Para la creación de perfiles, configuramos la carga en 1000 solicitudes por segundo, luego usamos pprof para capturar datos para averiguar dónde está gastando el tiempo de CPU la aplicación. Esto se puede hacer activando el punto final HTTP. pprofy luego, bajo carga, guarde los resultados usando curl:

$ curl http://localhost:8080/debug/pprof/profile?seconds=29 > cpu.1000_reqs_sec_no_optimizations.prof

Los resultados se pueden mostrar así:

$ go tool pprof -http=:12345 cpu.1000_reqs_sec_no_optimizations.prof

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

El gráfico muestra dónde y cuánto tiempo de CPU consume la aplicación. De la descripción de Brendan Gregg:

El eje X es la población del perfil de la pila, ordenada alfabéticamente (esto no es tiempo), el eje Y muestra la profundidad de la pila, contando desde cero en [arriba]. Cada rectángulo es un marco de pila. Cuanto más ancho es el marco, más a menudo está presente en las pilas. Lo que está arriba se ejecuta en la CPU y lo que está debajo son los elementos secundarios. Los colores no suelen significar nada, sino que simplemente se eligen al azar para diferenciar los fotogramas.

Análisis - hipótesis

Para realizar ajustes, nos centraremos en tratar de encontrar tiempo de CPU desperdiciado. Buscaremos las mayores fuentes de gasto inútil y las eliminaremos. Bueno, dado que la creación de perfiles revela con mucha precisión dónde exactamente la aplicación está gastando su tiempo de procesador, es posible que tenga que hacerlo varias veces y también necesitará cambiar el código fuente de la aplicación, volver a ejecutar las pruebas y ver que el rendimiento se acerca al objetivo.

Siguiendo las recomendaciones de Brendan Gregg, leeremos el gráfico de arriba a abajo. Cada línea muestra un marco de pila (llamada a función). La primera línea es el punto de entrada al programa, el padre de todas las demás llamadas (en otras palabras, todas las demás llamadas la tendrán en su pila). La siguiente línea ya es diferente:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Si pasa el cursor sobre el nombre de una función en el gráfico, se mostrará el tiempo total que estuvo en la pila durante la depuración. La función HTTPServe estuvo ahí el 65% del tiempo, otras funciones de tiempo de ejecución runtime.mcall, mstart и gc, ocupó el resto del tiempo. Dato curioso: el 5% del tiempo total se dedica a consultas DNS:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Las direcciones que busca el programa pertenecen a Postgresql. Haga clic en FindByAge:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Curiosamente, el programa muestra que, en principio, hay tres fuentes principales que añaden retrasos: abrir y cerrar conexiones, solicitar datos y conectarse a la base de datos. El gráfico muestra que las solicitudes de DNS y la apertura y cierre de conexiones ocupan aproximadamente el 13% del tiempo total de ejecución.

Hipótesis La reutilización de conexiones mediante la agrupación debería reducir el tiempo de una única solicitud HTTP, lo que permite un mayor rendimiento y una menor latencia..

Configurar la aplicación - experimentar

Actualizamos el código fuente, intentamos eliminar la conexión a Postgresql para cada solicitud. La primera opción es utilizar grupo de conexiones a nivel de aplicación. En este experimento nosotros vamos a configurarlo agrupación de conexiones usando el controlador sql para go:

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)

if err != nil {
   return nil, err
}

Ejecución, observación, análisis.

Después de reiniciar la prueba con 1000 solicitudes por segundo, está claro que los niveles de latencia de p99 han vuelto a la normalidad con un SLO de 60 ms.

¿Cuál es el costo?

10000 solicitudes por segundo / 1000 solicitudes por servidor = 10 servidores + 1

¡Hagámoslo aún mejor!

2000 solicitudes por segundo

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Duplicar la carga muestra lo mismo, el gráfico superior izquierdo muestra que la aplicación logra procesar 2000 solicitudes por segundo, p100 es inferior a 60 ms, p99 satisface el SLO.

En términos de costo:

10000 solicitudes por segundo / 2000 solicitudes por servidor = 5 servidores + 1

3000 solicitudes por segundo

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Aquí la aplicación puede procesar 3000 solicitudes con una latencia p99 de menos de 60 ms. El SLO no se viola y el costo se acepta de la siguiente manera:

10000 solicitudes por segundo / por 3000 solicitudes por servidor = 4 servidores + 1 (el autor ha redondeado, aprox. traductor)

Intentemos otra ronda de análisis.

Análisis - hipótesis

Recopilamos y mostramos los resultados de la depuración de la aplicación a 3000 solicitudes por segundo:

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Todavía el 6% del tiempo se dedica a establecer conexiones. La configuración del grupo ha mejorado el rendimiento, pero aún puede ver que la aplicación continúa trabajando para crear nuevas conexiones a la base de datos.

Hipótesis Las conexiones, a pesar de la presencia de un grupo, aún se interrumpen y se limpian, por lo que la aplicación debe restablecerlas. Establecer la cantidad de conexiones pendientes en el tamaño del grupo debería ayudar con la latencia al minimizar el tiempo que la aplicación dedica a crear una conexión..

Configurar la aplicación - experimentar

Intentando instalar MaxIdleConns igual al tamaño de la piscina (también descrito aquí):

db, err := sql.Open("postgres", dbConnectionString)
db.SetMaxOpenConns(8)
db.SetMaxIdleConns(8)
if err != nil {
   return nil, err
}

Ejecución, observación, análisis.

3000 solicitudes por segundo

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

¡p99 dura menos de 60 ms y significativamente menos p100!

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

¡Al comprobar el gráfico de llamas se ve que la conexión ya no se nota! Comprobemos con más detalle pg(*conn).query — Tampoco notamos que se establece la conexión aquí.

SRE: Análisis de Desempeño. Método de configuración utilizando un servidor web simple en Go

Conclusión

El análisis del desempeño es fundamental para comprender que se están cumpliendo las expectativas del cliente y los requisitos no funcionales. El análisis comparando las observaciones con las expectativas del cliente puede ayudar a determinar qué es aceptable y qué no. Go proporciona potentes herramientas integradas en la biblioteca estándar que hacen que el análisis sea sencillo y accesible.

Fuente: habr.com

Añadir un comentario