Análisis TSDB en Prometheus 2

Análisis TSDB en Prometheus 2

La base de datos de series de tiempo (TSDB) en Prometheus 2 es un excelente ejemplo de una solución de ingeniería que ofrece mejoras significativas con respecto al almacenamiento v2 en Prometheus 1 en términos de acumulación de datos y velocidad de ejecución de consultas, y eficiencia de recursos. Estábamos implementando Prometheus 2 en Percona Monitoring and Management (PMM) y tuve la oportunidad de comprender el rendimiento de Prometheus 2 TSDB. En este artículo hablaré sobre los resultados de estas observaciones.

Carga de trabajo promedio de Prometheus

Para aquellos acostumbrados a trabajar con bases de datos de propósito general, la carga de trabajo típica de Prometheus es bastante interesante. La tasa de acumulación de datos tiende a ser estable: normalmente los servicios que usted monitorea envían aproximadamente la misma cantidad de métricas y la infraestructura cambia relativamente lentamente.
Las solicitudes de información pueden provenir de diversas fuentes. Algunos de ellos, como las alertas, también buscan un valor estable y predecible. Otros, como las solicitudes de los usuarios, pueden provocar ráfagas, aunque este no es el caso para la mayoría de las cargas de trabajo.

Prueba de carga

Durante las pruebas, me concentré en la capacidad de acumular datos. Implementé Prometheus 2.3.2 compilado con Go 1.10.1 (como parte de PMM 1.14) en el servicio Linode usando este script: pilascript. Para la generación de carga más realista, usando este pilascript Lancé varios nodos MySQL con carga real (Prueba Sysbench TPC-C), cada uno de los cuales emuló 10 nodos Linux/MySQL.
Todas las siguientes pruebas se realizaron en un servidor Linode con ocho núcleos virtuales y 32 GB de memoria, ejecutando 20 simulaciones de carga monitoreando doscientas instancias de MySQL. O, en términos de Prometheus, 800 objetivos, 440 raspados por segundo, 380 mil registros por segundo y 1,7 millones de series temporales activas.

diseño

El enfoque habitual de las bases de datos tradicionales, incluida la utilizada por Prometheus 1.x, es limite de memoria. Si no es suficiente para manejar la carga, experimentará latencias altas y algunas solicitudes fallarán. El uso de la memoria en Prometheus 2 se puede configurar mediante una clave storage.tsdb.min-block-duration, que determina cuánto tiempo se conservarán las grabaciones en la memoria antes de descargarlas al disco (el valor predeterminado es 2 horas). La cantidad de memoria requerida dependerá de la cantidad de series temporales, etiquetas y fragmentos agregados al flujo entrante neto. En términos de espacio en disco, Prometheus pretende utilizar 3 bytes por registro (muestra). Por otro lado, los requisitos de memoria son mucho mayores.

Aunque es posible configurar el tamaño del bloque, no se recomienda configurarlo manualmente, por lo que se verá obligado a darle a Prometheus tanta memoria como requiera para su carga de trabajo.
Si no hay suficiente memoria para soportar el flujo entrante de métricas, Prometheus se quedará sin memoria o el asesino OOM llegará a ella.
Agregar swap para retrasar el bloqueo cuando Prometheus se queda sin memoria realmente no ayuda, porque el uso de esta función provoca un consumo explosivo de memoria. Creo que tiene algo que ver con Go, su recolector de basura y la forma en que maneja el intercambio.
Otro enfoque interesante es configurar el bloque principal para que se descargue en el disco en un momento determinado, en lugar de contarlo desde el inicio del proceso.

Análisis TSDB en Prometheus 2

Como puede ver en el gráfico, los vaciados al disco se producen cada dos horas. Si cambia el parámetro de duración mínima del bloque a una hora, estos reinicios se producirán cada hora, comenzando después de media hora.
Si desea utilizar este y otros gráficos en su instalación de Prometheus, puede utilizar este panel. Fue diseñado para PMM pero, con modificaciones menores, encaja en cualquier instalación de Prometheus.
Tenemos un bloque activo llamado bloque principal que se almacena en la memoria; Los bloques con datos más antiguos están disponibles a través de mmap(). Esto elimina la necesidad de configurar el caché por separado, pero también significa que debe dejar suficiente espacio para el caché del sistema operativo si desea consultar datos anteriores a los que el bloque principal puede acomodar.
Esto también significa que el consumo de memoria virtual de Prometheus parecerá bastante alto, lo cual no es algo de qué preocuparse.

Análisis TSDB en Prometheus 2

Otro punto de diseño interesante es el uso de WAL (registro de escritura anticipada). Como puede ver en la documentación de almacenamiento, Prometheus usa WAL para evitar fallas. Desafortunadamente, los mecanismos específicos para garantizar la supervivencia de los datos no están bien documentados. La versión 2.3.2 de Prometheus descarga WAL en el disco cada 10 segundos y esta opción no es configurable por el usuario.

Compactaciones

Prometheus TSDB está diseñado como un almacén LSM (Log Structured Merge): el bloque principal se descarga periódicamente al disco, mientras que un mecanismo de compactación combina varios bloques para evitar escanear demasiados bloques durante las consultas. Aquí puede ver la cantidad de bloques que observé en el sistema de prueba después de un día de carga.

Análisis TSDB en Prometheus 2

Si desea obtener más información sobre la tienda, puede examinar el archivo meta.json, que tiene información sobre los bloques disponibles y cómo surgieron.

{
       "ulid": "01CPZDPD1D9R019JS87TPV5MPE",
       "minTime": 1536472800000,
       "maxTime": 1536494400000,
       "stats": {
               "numSamples": 8292128378,
               "numSeries": 1673622,
               "numChunks": 69528220
       },
       "compaction": {
               "level": 2,
               "sources": [
                       "01CPYRY9MS465Y5ETM3SXFBV7X",
                       "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                       "01CPZ6NR4Q3PDP3E57HEH760XS"
               ],
               "parents": [
                       {
                               "ulid": "01CPYRY9MS465Y5ETM3SXFBV7X",
                               "minTime": 1536472800000,
                               "maxTime": 1536480000000
                       },
                       {
                               "ulid": "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                               "minTime": 1536480000000,
                               "maxTime": 1536487200000
                       },
                       {
                               "ulid": "01CPZ6NR4Q3PDP3E57HEH760XS",
                               "minTime": 1536487200000,
                               "maxTime": 1536494400000
                       }
               ]
       },
       "version": 1
}

Las compactaciones en Prometheus están ligadas al momento en que el bloque principal se lava con el disco. En este punto, se pueden llevar a cabo varias operaciones de este tipo.

Análisis TSDB en Prometheus 2

Parece que las compactaciones no están limitadas de ninguna manera y pueden provocar grandes picos de E/S del disco durante la ejecución.

Análisis TSDB en Prometheus 2

Picos de carga de CPU

Análisis TSDB en Prometheus 2

Por supuesto, esto tiene un impacto bastante negativo en la velocidad del sistema y también plantea un serio desafío para el almacenamiento LSM: ¿cómo compactar para soportar altas tasas de solicitudes sin causar demasiados gastos generales?
El uso de la memoria en el proceso de compactación también parece bastante interesante.

Análisis TSDB en Prometheus 2

Podemos ver como, tras la compactación, la mayor parte de la memoria cambia de estado de Cached a Free: esto significa que de allí se ha eliminado información potencialmente valiosa. Curioso si se usa aquí. fadvice() ¿O alguna otra técnica de minimización, o es porque el caché se liberó de los bloques destruidos durante la compactación?

Recuperación después de un fracaso

La recuperación de los fracasos lleva tiempo, y por una buena razón. Para un flujo entrante de un millón de registros por segundo, tuve que esperar unos 25 minutos mientras se realizaba la recuperación teniendo en cuenta la unidad SSD.

level=info ts=2018-09-13T13:38:14.09650965Z caller=main.go:222 msg="Starting Prometheus" version="(version=2.3.2, branch=v2.3.2, revision=71af5e29e815795e9dd14742ee7725682fa14b7b)"
level=info ts=2018-09-13T13:38:14.096599879Z caller=main.go:223 build_context="(go=go1.10.1, user=Jenkins, date=20180725-08:58:13OURCE)"
level=info ts=2018-09-13T13:38:14.096624109Z caller=main.go:224 host_details="(Linux 4.15.0-32-generic #35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 1bee9e9b78cf (none))"
level=info ts=2018-09-13T13:38:14.096641396Z caller=main.go:225 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2018-09-13T13:38:14.097715256Z caller=web.go:415 component=web msg="Start listening for connections" address=:9090
level=info ts=2018-09-13T13:38:14.097400393Z caller=main.go:533 msg="Starting TSDB ..."
level=info ts=2018-09-13T13:38:14.098718401Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536530400000 maxt=1536537600000 ulid=01CQ0FW3ME8Q5W2AN5F9CB7R0R
level=info ts=2018-09-13T13:38:14.100315658Z caller=web.go:467 component=web msg="router prefix" prefix=/prometheus
level=info ts=2018-09-13T13:38:14.101793727Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536732000000 maxt=1536753600000 ulid=01CQ78486TNX5QZTBF049PQHSM
level=info ts=2018-09-13T13:38:14.102267346Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536537600000 maxt=1536732000000 ulid=01CQ78DE7HSQK0C0F5AZ46YGF0
level=info ts=2018-09-13T13:38:14.102660295Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536775200000 maxt=1536782400000 ulid=01CQ7SAT4RM21Y0PT5GNSS146Q
level=info ts=2018-09-13T13:38:14.103075885Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536753600000 maxt=1536775200000 ulid=01CQ7SV8WJ3C2W5S3RTAHC2GHB
level=error ts=2018-09-13T14:05:18.208469169Z caller=wal.go:275 component=tsdb msg="WAL corruption detected; truncating" err="unexpected CRC32 checksum d0465484, want 0" file=/opt/prometheus/data/.prom2-data/wal/007357 pos=15504363
level=info ts=2018-09-13T14:05:19.471459777Z caller=main.go:543 msg="TSDB started"
level=info ts=2018-09-13T14:05:19.471604598Z caller=main.go:603 msg="Loading configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499156711Z caller=main.go:629 msg="Completed loading of configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499228186Z caller=main.go:502 msg="Server is ready to receive web requests."

El principal problema del proceso de recuperación es el alto consumo de memoria. A pesar de que en una situación normal el servidor puede funcionar de forma estable con la misma cantidad de memoria, si falla es posible que no se recupere debido a OOM. La única solución que encontré fue deshabilitar la recopilación de datos, abrir el servidor, dejar que se recupere y reiniciar con la recopilación habilitada.

Calentar

Otro comportamiento a tener en cuenta durante el calentamiento es la relación entre el bajo rendimiento y el alto consumo de recursos nada más empezar. Durante algunos inicios, pero no todos, observé una carga importante en la CPU y la memoria.

Análisis TSDB en Prometheus 2

Análisis TSDB en Prometheus 2

Las lagunas en el uso de la memoria indican que Prometheus no puede configurar todas las colecciones desde el principio y se pierde parte de la información.
No he descubierto las razones exactas de la alta carga de CPU y memoria. Sospecho que esto se debe a la creación de nuevas series temporales en el bloque principal con alta frecuencia.

Aumentos repentinos de carga de CPU

Además de las compactaciones, que crean una carga de E/S bastante alta, noté importantes picos en la carga de la CPU cada dos minutos. Las ráfagas son más largas cuando el flujo de entrada es alto y parecen ser causadas por el recolector de basura de Go, con al menos algunos núcleos completamente cargados.

Análisis TSDB en Prometheus 2

Análisis TSDB en Prometheus 2

Estos saltos no son tan insignificantes. Parece que cuando esto ocurre, el punto de entrada interno y las métricas de Prometheus dejan de estar disponibles, lo que provoca lagunas de datos durante estos mismos períodos de tiempo.

Análisis TSDB en Prometheus 2

También puedes notar que el exportador de Prometheus se apaga por un segundo.

Análisis TSDB en Prometheus 2

Podemos notar correlaciones con la recolección de basura (GC).

Análisis TSDB en Prometheus 2

Conclusión

TSDB en Prometheus 2 es rápido, capaz de manejar millones de series temporales y al mismo tiempo miles de registros por segundo utilizando un hardware bastante modesto. La utilización de CPU y E/S de disco también es impresionante. Mi ejemplo mostró hasta 200 métricas por segundo por núcleo utilizado.

Para planificar la expansión, es necesario recordar una cantidad suficiente de memoria, y ésta debe ser memoria real. La cantidad de memoria utilizada que observé fue de aproximadamente 5 GB por cada 100 registros por segundo del flujo entrante, lo que junto con el caché del sistema operativo dio aproximadamente 000 GB de memoria ocupada.

Por supuesto, todavía queda mucho trabajo por hacer para controlar los picos de E/S de CPU y disco, y esto no es sorprendente considerando lo joven que es TSDB Prometheus 2 en comparación con InnoDB, TokuDB, RocksDB, WiredTiger, pero todos tenían características similares. problemas en las primeras etapas de su ciclo de vida.

Fuente: habr.com

Añadir un comentario