Cómo usar Prometheus para detectar anomalías en GitLab


Una de las características básicas del lenguaje de consulta Prometheus es la agregación en tiempo real de series de tiempo . También puede usar el lenguaje de consulta Prometheus para detectar anomalías en datos de series temporales. 

El equipo de Mail.ru Cloud Solutions ha traducido un artículo del ingeniero del equipo de infraestructura de GitLab , donde encontrará ejemplos de código que puede probar en sus sistemas.

¿Para qué sirve la detección de anomalías?


Existen cuatro razones principales que determinan la importancia de la detección de anomalías para GitLab:

  1. Diagnóstico de incidentes : podemos detectar qué servicios han ido más allá de su alcance normal al reducir el tiempo de detección de incidentes (MTTD) y, en consecuencia, ofrecer una solución más rápida al problema.
  2. Detección de degradación del rendimiento : por ejemplo, si se introduce una regresión en un servicio que hace que acceda a otro servicio con demasiada frecuencia, podemos detectar y solucionar este problema rápidamente.
  3. Detección y eliminación del abuso : GitLab proporciona mecanismos de entrega e integración ( GitLab CI / CD ) y alojamiento (páginas de GitLab) y un número limitado de usuarios que pueden usar estos mecanismos.
  4. Seguridad : la detección de anomalías es importante para detectar tendencias inusuales en la serie temporal de GitLab.

Por estas y otras razones, el autor del artículo decidió averiguar cómo configurar la definición de anomalías en la serie temporal de GitLab utilizando consultas y reglas de Prometheus.

¿Cuál es el nivel de agregación correcto?


Primero, las series de tiempo deben estar correctamente agregadas. En el siguiente ejemplo, utilizamos el contador estándar http_requests_total para recuperar datos, aunque muchas otras métricas también lo harían.

http_requests_total{
 job="apiserver",
 method="GET",
 controller="ProjectsController",
 status_code="200",
 environment="prod"
}

Esta métrica de prueba tiene varios parámetros: método, controlador, código de estado (status_code), entorno, además de los parámetros agregados por Prometheus, por ejemplo, trabajo e instancia.

Ahora debe elegir el nivel correcto de agregación de datos. Demasiado, muy poco: todo esto es importante para detectar anomalías. Si los datos están demasiado agregados, hay dos problemas potenciales:

  1. Puede omitir la anomalía porque la agregación oculta los problemas que ocurren en subconjuntos de sus datos.
  2. Si encuentra una anomalía, es difícil relacionarla con una parte separada de su sistema sin un esfuerzo adicional.

Si los datos no se agregan lo suficiente, esto puede conducir a un aumento en el número de falsos positivos, así como a una interpretación incorrecta de datos válidos como erróneos.

Según nuestra experiencia, el nivel de agregación más correcto es el nivel de servicio, es decir, incluimos la etiqueta de trabajo (trabajo) y medio ambiente (medio ambiente), y descartamos todas las demás etiquetas.

La agregación, que discutiremos a lo largo del artículo, incluye: trabajo - http_request, agregación - cinco minutos, que se calcula sobre la base del trabajo y el entorno en cinco minutos.

- record: job:http_requests:rate5m
expr: sum without(instance, method, controller, status_code)
(rate(http_requests_total[5m]))
# --> job:http_requests:rate5m{job="apiserver", environment="prod"}  21321
# --> job:http_requests:rate5m{job="gitserver", environment="prod"}  2212
# --> job:http_requests:rate5m{job="webserver", environment="prod"}  53091

Del ejemplo anterior, está claro que de la serie http_requests_total se seleccionan subconjuntos en el contexto del trabajo y el entorno, entonces su número se considera durante cinco minutos.

Usando Z-score para detectar anomalías


Los principios básicos de las estadísticas se pueden aplicar para detectar anomalías.

Si conoce la media y la desviación estándar de la serie Prometheus, puede usar cualquier muestra de la serie para calcular la puntuación z. 

Una puntuación Z es una medida de la extensión relativa del valor observado o medido, que muestra cuántas desviaciones estándar es su extensión en relación con el valor promedio

Es decir, el puntaje z = 0 significa que el puntaje z es idéntico al valor promedio en el conjunto de datos con la distribución estándar, y el puntaje z = 1 significa que la desviación estándar = 1.0 del promedio.

Suponemos que los datos básicos tienen una distribución normal, lo que significa que el 99.7% de las muestras tienen un puntaje z de 0 a 3. Cuanto más lejos esté el puntaje z de cero, es menos probable que exista. 

Aplicamos esta propiedad para detectar anomalías en la serie de datos de Prometheus:

  1. Calculamos la media y la desviación estándar de la métrica utilizando datos con un gran tamaño de muestra. Para este ejemplo, usamos datos semanales. Si suponemos que los registros se evalúan una vez por minuto, en una semana tendremos un poco más de 10,000 muestras.

    # Long-term average value for the series
    - record: job:http_requests:rate5m:avg_over_time_1w
    expr: avg_over_time(job:http_requests:rate5m[1w])
    
    # Long-term standard deviation for the series
    - record: job:http_requests:rate5m:stddev_over_time_1w
    expr: stddev_over_time(job:http_requests:rate5m[1w])

  2. Podemos calcular el puntaje z para la consulta de Prometheus tan pronto como obtengamos la media y la desviación estándar para la agregación.

    # Z-Score for aggregation
    (
    job:http_requests:rate5m -
    job:http_requests:rate5m:avg_over_time_1w
    ) /  job:http_requests:rate5m:stddev_over_time_1w


Con base en los principios estadísticos de las distribuciones normales, suponga que cualquier valor fuera del rango de +3 a -3 será una anomalía. En consecuencia, podemos crear una advertencia sobre tales anomalías. Por ejemplo, recibiremos una alerta cuando nuestra agregación vaya más allá de este rango durante más de cinco minutos.


Gráfico del número de solicitudes por segundo al servicio de páginas de GitLab durante 48 horas. La puntuación Z en el rango de +3 a -3 se resalta en verde. La

puntuación Z puede ser difícil de interpretar en los gráficos, ya que este valor no tiene una unidad de medida. Pero las anomalías en este gráfico son muy simples de determinar. Todo lo que vaya más allá de la zona verde, que muestra un corredor de valores con un puntaje z de -3 a +3, será un valor anómalo.

Qué hacer si la distribución de datos no es normal


Suponemos que la distribución de datos es normal. De lo contrario, nuestro puntaje z será falso.

Hay muchos trucos estadísticos para determinar la normalidad de la distribución de datos, pero la mejor manera es verificar que su puntaje z de datos se encuentre en el rango de -4.0 a +4.0.

Dos consultas de Prometheus que muestran puntuaciones z mínimas y máximas:

(
max_over_time(job:http_requests:rate5m[1w]) - avg_over_time(job:http_requests:rate5m[1w])
) / stddev_over_time(job:http_requests:rate5m[1w])
# --> {job="apiserver", environment="prod"}  4.01
# --> {job="gitserver", environment="prod"}  3.96
# --> {job="webserver", environment="prod"}  2.96

(
 min_over_time(job:http_requests:rate5m[<1w]) - avg_over_time(job:http_requests:rate5m[1w])
) / stddev_over_time(job:http_requests:rate5m[1w])
# --> {job="apiserver", environment="prod"}  -3.8
# --> {job="gitserver", environment="prod"}  -4.1
# --> {job="webserver", environment="prod"}  -3.2

Si sus resultados están en el rango de -20 a +20, esto significa que se han utilizado demasiados datos y los resultados están distorsionados. Recuerde también que debe trabajar con filas agregadas. Las métricas que no tienen una distribución normal incluyen parámetros como la tasa de error, el retraso, la longitud de la cola, etc. Pero muchas de estas métricas funcionarán mejor con umbrales de alerta fijos.

Detección estadística de anomalías de estacionalidad


Aunque el cálculo de las puntuaciones z funciona bien con la distribución normal de los datos de series temporales, existe un segundo método que puede proporcionar resultados de detección de anomalías aún más precisos. Este es el uso de la estacionalidad estadística. 

La estacionalidad es una característica de una métrica de serie temporal cuando esta métrica sufre cambios regulares y predecibles que se repiten en cada ciclo.


Gráfico de solicitudes por segundo (RPS) de lunes a domingo durante cuatro semanas consecutivas El

gráfico anterior ilustra el RPS (número de solicitudes por segundo) durante siete días, de lunes a domingo, durante cuatro semanas consecutivas. Este rango de siete días se llama "desplazamiento", es decir, el patrón que se utilizará para la medición.

Cada semana en la tabla hay un color diferente. La estacionalidad de los datos está indicada por la secuencia en las tendencias indicadas en el gráfico: todos los lunes por la mañana observamos un aumento similar en RPS, y en las noches del viernes siempre observamos una disminución en RPS.

Usando la estacionalidad en nuestros datos de series de tiempo, podemos predecir con mayor precisión la aparición de anomalías y su detección. 

Cómo usar la estacionalidad


Prometeo utiliza varios mecanismos estadísticos diferentes para calcular la estacionalidad.

Primero, hacemos un cálculo, agregando una tendencia de crecimiento para la semana a los resultados de la semana anterior. La tendencia de crecimiento se calcula de la siguiente manera: reste el promedio móvil de la última semana del promedio móvil de la semana pasada.

- record: job:http_requests:rate5m_prediction
  expr: >
    job:http_requests:rate5m offset 1w          # Value from last period
    + job:http_requests:rate5m:avg_over_time_1w # One-week growth trend
    — job:http_requests:rate5m:avg_over_time_1w offset 1w

La primera iteración resulta ser algo "estrecha": utilizamos la ventana de cinco minutos de esta semana y la semana anterior para obtener nuestros pronósticos.

En la segunda iteración, ampliamos nuestra cobertura tomando el promedio del período de cuatro horas de la semana anterior y comparándolo con la semana actual. 

Por lo tanto, si intentamos predecir el valor de la métrica a las ocho de la mañana del lunes, en lugar de la misma ventana de cinco minutos la semana anterior, tomamos el valor promedio de la métrica de seis a diez de la mañana del lunes anterior.

- record: job:http_requests:rate5m_prediction
  expr: >
    avg_over_time(job:http_requests:rate5m[4h] offset 166h) # Rounded value from last period
    + job:http_requests:rate5m:avg_over_time_1w    # Add 1w growth trend
    - job:http_requests:rate5m:avg_over_time_1w offset 1w

La solicitud indicaba 166 horas, que son dos horas menos que una semana completa (7 * 24 = 168), ya que queremos usar un período de cuatro horas en función de la hora actual, por lo que necesitamos que la compensación sea dos horas menos que una semana completa. 


RPS real (amarillo) y predicho (azul) en dos semanas

Una comparación del RPS real con nuestro pronóstico muestra que nuestros cálculos fueron bastante precisos. Sin embargo, este método tiene un inconveniente.
 
Por ejemplo, el 1 de mayo, GitLab se usó menos de lo habitual los miércoles, ya que este día era un día libre. Dado que la tendencia de crecimiento estimada depende de cómo se utilizó el sistema en la semana anterior, nuestras previsiones para la próxima semana, es decir, el miércoles 8 de mayo, dieron un RPS más bajo que en realidad.

Este error se puede corregir haciendo tres pronósticos durante tres semanas consecutivas antes del miércoles 1 de mayo, es decir, los tres miércoles anteriores. La solicitud sigue siendo la misma, pero se ajusta el desplazamiento.

- record: job:http_requests:rate5m_prediction
  expr: >
   quantile(0.5,
     label_replace(
       avg_over_time(job:http_requests:rate5m[4h] offset 166h)
       + job:http_requests:rate5m:avg_over_time_1w — job:http_requests:rate5m:avg_over_time_1w offset 1w
       , "offset", "1w", "", "")
     or
     label_replace(
       avg_over_time(job:http_requests:rate5m[4h] offset 334h)
       + job:http_requests:rate5m:avg_over_time_1w — job:http_requests:rate5m:avg_over_time_1w offset 2w
       , "offset", "2w", "", "")
     or
     label_replace(
       avg_over_time(job:http_requests:rate5m[4h] offset 502h)
       + job:http_requests:rate5m:avg_over_time_1w — job:http_requests:rate5m:avg_over_time_1w offset 3w
       , "offset", "3w", "", "")
   )
   without (offset)


Hay tres pronósticos en el gráfico para tres semanas antes del 8 de mayo, en comparación con el RPS real para el miércoles 8 de mayo. Puede ver que los dos pronósticos son muy precisos, pero el pronóstico para la semana del 1 de mayo sigue siendo inexacto.

Además, no necesitamos tres pronósticos, necesitamos uno. El valor promedio no es una opción, ya que será borroso por nuestros datos distorsionados de RPS del 1 de mayo. En cambio, necesitas calcular la mediana. Prometeo no tiene una consulta mediana, pero podemos usar la agregación cuantil en lugar de la mediana.

El único problema es que estamos tratando de incluir tres series en la agregación, y estas tres series son en realidad la misma serie en tres semanas. En otras palabras, tienen las mismas etiquetas, por lo que es difícil juntarlas. 

Para evitar confusiones, creamos una etiqueta llamada desplazamiento y utilizamos la función de reemplazo de etiquetas para agregar un desplazamiento a cada una de las tres semanas. Luego, en la agregación cuantil, descartamos estas etiquetas, y esto nos da un promedio de tres.

- record: job:http_requests:rate5m_prediction
  expr: >
   quantile(0.5,
     label_replace(
       avg_over_time(job:http_requests:rate5m[4h] offset 166h)
       + job:http_requests:rate5m:avg_over_time_1w — job:http_requests:rate5m:avg_over_time_1w offset 1w
       , "offset", "1w", "", "")
     or
     label_replace(
       avg_over_time(job:http_requests:rate5m[4h] offset 334h)
       + job:http_requests:rate5m:avg_over_time_1w — job:http_requests:rate5m:avg_over_time_1w offset 2w
       , "offset", "2w", "", "")
     or
     label_replace(
       avg_over_time(job:http_requests:rate5m[4h] offset 502h)
       + job:http_requests:rate5m:avg_over_time_1w — job:http_requests:rate5m:avg_over_time_1w offset 3w
       , "offset", "3w", "", "")
   )
   without (offset)

Ahora nuestro pronóstico con una mediana de tres agregaciones se ha vuelto más preciso.


Pronóstico medio versus RPS real

Cómo descubrir que nuestro pronóstico es realmente preciso


Para verificar la precisión del pronóstico, podemos volver al puntaje z. Se utiliza para medir la discrepancia de la muestra con su pronóstico en desviaciones estándar. Cuanto mayor sea la desviación estándar del pronóstico, mayor será la probabilidad de que un valor particular sea un valor atípico.


El rango de desviación proyectado es de +1.5 a -1.5

Puede cambiar nuestro gráfico de Grafana para usar un pronóstico estacional, en lugar de un promedio móvil semanal. El rango de valores normales para una hora específica del día está sombreado en verde. Todo lo que va más allá de la zona verde se considera un caso atípico. En este caso, el valor atípico ocurrió el domingo por la tarde, cuando nuestro proveedor de la nube tuvo algunos problemas de red.

Es una buena práctica utilizar un puntaje z de ± 2 para los pronósticos estacionales.

Cómo configurar alertas con Prometheus


Si desea configurar una alerta para eventos anormales, puede aplicar la regla bastante simple de Prometeo, que verifica si el puntaje z de un indicador está en el rango entre +2 y -2.

- alert: RequestRateOutsideNormalRange
  expr: >
   abs(
     (
       job:http_requests:rate5m - job:http_requests:rate5m_prediction
     ) / job:http_requests:rate5m:stddev_over_time_1w
   ) > 2
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: Requests for job {{ $labels.job }} are outside of expected operating parameters

En GitLab, utilizamos una regla de enrutamiento personalizada que envía una alerta a través de Slack cuando detecta cualquier anomalía, pero no se comunica con nuestro equipo de soporte.

Cómo detectar anomalías en GitLab usando Prometheus


  1. Prometheus puede usarse para detectar algunas anormalidades.
  2. La agregación adecuada es la clave para encontrar anomalías.
  3. La puntuación Z es efectiva si sus datos tienen una distribución normal.
  4. La estacionalidad estadística es un poderoso mecanismo para detectar anomalías.

Traducido con el soporte de Mail.ru Cloud Solutions .

Sigue siendo útil :

  1. Métodos de almacenamiento en caché simples en GitLab CI: una guía de imágenes .
  2. Herramientas GitLab CE listas para usar y personalizadas en el mercado de MCS
  3. Nuestro canal de Telegram sobre transformación digital .

All Articles