Varios métodos de depuración en OpenResty

API7.ai

December 16, 2022

OpenResty (NGINX + Lua)

En el grupo de comunicación de OpenResty, los desarrolladores suelen hacer esta pregunta: ¿Cómo depurar en OpenResty? Hasta donde sé, hay algunas herramientas en OpenResty que admiten la depuración con puntos de interrupción, incluyendo un complemento en VSCode, pero hasta ahora no son ampliamente utilizadas. Incluso el autor, agentzh, y algunos colaboradores que conozco, todos usan el método más simple de ngx.log y ngx.say para depurar.

Esto no es amigable para la mayoría de los principiantes. ¿Significa esto que los muchos mantenedores principales de OpenResty solo tienen el método primitivo de imprimir registros cuando se encuentran con un problema difícil?

Por supuesto que no. En el mundo de OpenResty, SystemTap y los gráficos de llamadas (flame graphs) son las herramientas estándar para lidiar con problemas difíciles y de rendimiento. Si tienes una pregunta sobre esto en una lista de correo o en un issue, el mantenedor del proyecto te pedirá que subas un gráfico de llamadas y solicitará una descripción gráfica en lugar de textual.

En los próximos dos artículos, hablaré contigo sobre la depuración y el conjunto de herramientas que OpenResty creó específicamente para la depuración. Hoy comenzaremos viendo qué está disponible para depurar programas.

Puntos de interrupción e impresión de registros

Durante mucho tiempo en mi trabajo, dependí de las funciones avanzadas de depuración del IDE (entorno de desarrollo integrado) para rastrear programas, lo cual parecía natural. Para problemas que pueden reproducirse en un entorno de prueba, no importa cuán complejos sean, estoy seguro de que puedo llegar a la raíz del problema. La razón es que el error se puede reproducir repetidamente, y la causa se puede encontrar estableciendo puntos de interrupción e imprimiendo registros. Todo lo que necesitas es paciencia.

Desde este punto de vista, resolver errores que ocurren de manera constante en un entorno de prueba es un trabajo físico. La mayoría de los errores que resuelvo en mi trabajo entran en esta categoría.

Sin embargo, ten en cuenta que hay dos requisitos previos: un entorno de prueba y una reproducción estable. La realidad siempre es menos que ideal. Si el error solo se reproduce en el entorno de producción, ¿hay alguna manera de depurarlo?

Aquí recomiendo una herramienta: Mozilla RR. Puedes tomarla como un repetidor, que registra el comportamiento del programa y luego lo reproduce repetidamente. Para ser honesto, independientemente del entorno de producción o de prueba, siempre que puedas registrar la "evidencia" del error, puede usarse como "evidencia en el tribunal" para analizarla lentamente.

Algoritmo de búsqueda binaria y comentarios

Sin embargo, para algunos proyectos grandes, por ejemplo, el error puede provenir de uno de los múltiples servicios, o puede haber un problema con la consulta SQL a la base de datos, en este caso, incluso si el error se puede reproducir de manera estable, no puedes estar seguro en qué parte ocurrió el error. Por lo tanto, herramientas de registro como Mozilla RR fallan.

En este punto, puedes recordar el clásico "algoritmo de búsqueda binaria". Primero comentamos la mitad de la lógica en el código, y si el problema persiste, el error está en el código no comentado, por lo que comentamos la otra mitad de la lógica y continuamos el ciclo. En pocas veces, el problema se reduce a un tamaño completamente manejable.

Este enfoque puede sonar un poco tonto, pero es eficiente en muchos escenarios. Por supuesto, a medida que la tecnología avanza y la complejidad del sistema aumenta, recomendamos usar un estándar como OpenTracing para el rastreo distribuido.

OpenTracing puede enterrarse en varias partes del sistema y reportar la cadena de llamadas y el seguimiento de eventos compuestos por múltiples Spans al servidor a través de Trace ID para su análisis y presentación gráfica. Esto puede ayudar a los desarrolladores a encontrar muchos problemas ocultos, y los datos históricos se guardarán para que podamos compararlos y verlos en cualquier momento.

Además, si tu sistema es más complejo, como en un entorno de microservicios, entonces Zipkin, Apache SkyWalking son buenas opciones.

Depuración dinámica

Los métodos de depuración que he descrito anteriormente son suficientes para resolver la mayoría de los problemas. Sin embargo, si te encuentras con una falla que solo ocurre ocasionalmente en producción, tomará bastante tiempo rastrearla agregando registros y seguimiento de eventos.

Hace años, yo era responsable de un sistema que se quedaba sin recursos de la base de datos alrededor de la 1:00 a.m. todos los días y causaba que todo el sistema colapsara. En ese momento, revisamos las tareas programadas en el código durante el día, y por la noche, el equipo esperaba que el error se reprodujera en la empresa, y luego revisaba el estado de ejecución de los submódulos cuando se reproducía. No encontramos la causa del error hasta la tercera noche.

Mi experiencia es similar al contexto de varios ingenieros del sistema Solaris que crearon Dtrace. En ese momento, los ingenieros de Solaris también pasaron días y noches solucionando un problema extraño en producción, solo para descubrir que era porque una configuración estaba mal escrita. Pero a diferencia de mí, los ingenieros de Solaris decidieron evitar este problema por completo e inventaron Dtrace, específicamente para la depuración dinámica.

A diferencia de las herramientas de depuración estática como GDB, la depuración dinámica puede depurar servicios en línea. Todo el proceso de depuración es no sensible y no intrusivo para el programa depurado, sin modificar el código, y mucho menos reiniciar. Para usar una analogía, la depuración dinámica es como una radiografía, que puede examinar el cuerpo del paciente sin la necesidad de tomar muestras de sangre y gastroscopias.

Dtrace fue uno de los primeros marcos de rastreo dinámico, y su influencia ha llevado a la aparición de herramientas de depuración dinámica similares en otros sistemas. Por ejemplo, los ingenieros de Red Hat crearon Systemtap en Linux, que es de lo que voy a hablar a continuación.

Systemtap

Systemtap tiene su DSL, que se puede usar para configurar puntos de sondeo. Antes de entrar en más detalles, instalemos Systemtap para ir más allá de lo abstracto. Aquí, simplemente usa el administrador de paquetes del sistema para instalarlo.

sudo apt install systemtap

Veamos cómo es un programa hello world escrito en Systemtap:

# cat hello-world.stp
probe begin
{
  print("hello world!")
  exit()
}

¿No parece fácil? Necesitas usar privilegios de sudo para ejecutarlo.

sudo stap hello-world.stp

Imprimirá hello world!. En la mayoría de los escenarios, no necesitamos escribir nuestros scripts stap para hacer el análisis, porque OpenResty ya tiene muchos scripts stap listos para hacer el análisis regular, y te los presentaré en el próximo artículo. Entonces, hoy necesitamos tener una breve comprensión de los scripts stap.

Después de algo de práctica, volvamos a nuestro concepto, Systemtap funciona convirtiendo el script stap anterior a C y ejecutando el compilador C del sistema para crear el módulo del kernel. Cuando el módulo se carga, activa todos los eventos de sondeo enganchando el kernel.

Por ejemplo, begin se ejecutará al comienzo del sondeo, y el correspondiente end, por lo que el programa hello world anterior también se puede escribir de la siguiente manera:

probe begin
{
  print("hello ")
  exit()
}

probe end
{
print("world!")

Aquí, solo he dado una introducción muy superficial a Systemtap. Frank Ch. Eigler, el autor de Systemtap, escribió un libro electrónico Systemtap tutorial que introduce Systemtap en detalle. Si quieres aprender más y entender Systemtap en profundidad, sugiero comenzar con este libro como la mejor ruta de aprendizaje.

Otros marcos de rastreo dinámico

Systemtap no es suficiente para los ingenieros de análisis de kernel y rendimiento.

  1. Systemtap no entra en el kernel del sistema por defecto.
  2. Funciona de tal manera que es lento para arrancar y puede tener un impacto en el funcionamiento normal del sistema.

eBPF (extended BPF) es una nueva característica agregada al kernel de Linux en los últimos años. En comparación con Systemtap, eBPF tiene las ventajas de soporte directo del kernel, sin caídas y arranque rápido. Al mismo tiempo, no usa DSL, sino sintaxis C directamente, por lo que es mucho más fácil de comenzar.

Además de las soluciones de código abierto, VTune de Intel también es una de las mejores herramientas. Su interfaz intuitiva y presentación de datos te permiten analizar los cuellos de botella de rendimiento sin escribir código.

Gráfico de llamadas (Flame Graph)

Finalmente, recordemos el gráfico de llamadas mencionado en el artículo anterior. Como mencionamos antes, los datos generados por herramientas como perf y Systemtap se pueden mostrar de manera más visual usando el gráfico de llamadas. El siguiente diagrama es un ejemplo de un gráfico de llamadas.

flame graph

En el gráfico de llamadas, el color y la intensidad de los bloques de color no tienen significado, solo sirven para hacer una distinción simple entre diferentes bloques de color. El gráfico de llamadas es una superposición de los datos muestreados cada vez, por lo que los datos del usuario son el ancho y la longitud de los bloques.

Para el gráfico de llamadas en la CPU, el ancho del bloque de color es el porcentaje de tiempo de CPU ocupado por la función: cuanto más ancho sea el bloque, mayor será el drenaje de rendimiento. Si hay un pico plano, ese es el cuello de botella de rendimiento. La longitud del bloque de color, por otro lado, representa la profundidad de la llamada de la función, con el cuadro superior mostrando la función en ejecución y todos los que están debajo siendo los llamadores de esa función. Entonces, la función debajo es el supertipo de la función arriba: cuanto más alto sea el pico, más profunda será la llamada de la función.

Resumen

Es esencial saber que incluso una técnica no intrusiva como el rastreo dinámico no es perfecta. Solo puede detectar un proceso individual en particular; en general, solo lo activamos brevemente para usar los datos muestreados durante ese tiempo. Entonces, si necesitas detectar a través de múltiples servicios o durante largos períodos, aún necesitas una técnica de rastreo distribuido como opentracing.

¿Qué herramientas y técnicas de depuración usas en tu trabajo regular? Bienvenido a dejar un comentario y discutir conmigo, también te invito a compartir este artículo con tus amigos, para que podamos aprender y progresar juntos.