Analisi TSDB in Prometheus 2

Analisi TSDB in Prometheus 2

Il database di serie temporali (TSDB, time series database) in Prometheus 2 è un ottimo esempio di soluzione ingegneristica che offre significativi miglioramenti rispetto allo storage v2 in Prometheus 1 in termini di velocità di acquisizione dei dati e di esecuzione delle query, efficienza nell'uso delle risorse. Abbiamo implementato Prometheus 2 in Percona Monitoring and Management (PMM) e ho avuto l'opportunità di analizzare le performance del TSDB di Prometheus 2. In questo articolo parlerò dei risultati di queste osservazioni.

Carico di lavoro medio di Prometheus

Per coloro che sono abituati a lavorare con database di uso generale, il carico di lavoro di Prometheus è piuttosto curioso. La velocità di acquisizione dei dati tende a stabilizzarsi: generalmente i servizi che si monitorano inviano un numero di metriche abbastanza costante, e l'infrastruttura cambia abbastanza lentamente.
Le richieste di informazioni possono provenire da diverse fonti. Alcune di esse, come gli avvisi, tendono anche a stabilizzarsi su valori prevedibili. Altre, come le richieste degli utenti, possono generare picchi, anche se ciò non è caratteristico per la maggior parte del carico.

Test di carico

Durante il test mi sono concentrato sulla capacità di acquisire dati. Ho distribuito Prometheus 2.3.2, compilato con Go 1.10.1 (come parte di PMM 1.14) su un servizio Linode, utilizzando questo script: StackScript. Per generare il carico in modo il più realistico possibile, tramite questo StackScript ho avviato diverse nodi MySQL con un carico reale (Test Sysbench TPC-C), ognuno dei quali emulava 10 nodi Linux/MySQL.
Tutti i seguenti test sono stati condotti su un server Linode con otto core virtuali e 32 GB di RAM, su cui sono in esecuzione 20 simulazioni di carico per il monitoraggio di duecento istanze MySQL. Oppure, in termini di Prometheus, 800 target, 440 scrape al secondo, 380.000 campioni al secondo e 1,7 milioni di serie temporali attive.

Design

L'approccio tradizionale dei database, incluso quello usato da Prometheus 1.x, è basato su un limite di memoria. Se non è sufficiente per gestire il carico, si potrebbero verificare notevoli ritardi e alcune richieste potrebbero non essere eseguite. L'uso della memoria in Prometheus 2 è configurabile tramite la chiave storage.tsdb.min-block-duration, che determina per quanto tempo i dati saranno mantenuti in memoria prima di essere scritti su disco (di default sono 2 ore). La quantità di memoria necessaria dipenderà dal numero di serie temporali, etichette (labels) e dall'intensità della raccolta dati (scrapes) insieme al flusso di entrare pulito. In termini di spazio su disco, Prometheus cerca di utilizzare 3 byte per ogni scrittura (sample). D'altra parte, le esigenze di memoria sono molto più elevate.

Sebbene sia possibile configurare la dimensione del blocco, non è consigliato impostarla manualmente, quindi sei di fronte alla necessità di dare a Prometheus tanta memoria quanta ne richiede per il tuo carico.
Se non c'è abbastanza memoria per gestire il flusso di metriche in ingresso, Prometheus andrà in crash con out of memory o sarà raggiunto dall'OOM killer.
Aggiungere swap per ritardare il crash quando Prometheus esaurisce la memoria non aiuta molto, poiché l'uso di questa funzione provoca un consumo esplosivo di memoria. Credo che sia colpa del Go, del suo garbage collector e di come funziona con lo swap.
Un altro approccio interessante è configurare il reset del blocco head su disco in un momento specifico, invece di calcolarlo dal tempo di avvio del processo.

Analisi TSDB in Prometheus 2

Come puoi vedere dal grafico, i reset su disco avvengono ogni due ore. Se cambi il parametro min-block-duration a un'ora, questi reset avverranno ogni ora, a partire da mezz'ora.
Se desideri utilizzare questo e altri grafici nella tua installazione di Prometheus, puoi utilizzare questo dashboard. È stato progettato per PMM, ma con piccole modifiche, è adatto a qualsiasi installazione di Prometheus.
Abbiamo un blocco attivo, chiamato head block, che è mantenuto in memoria; i blocchi con dati più vecchi sono accessibili tramite mmap(). Questo elimina la necessità di configurare separatamente la cache, ma significa anche che devi lasciare abbastanza spazio per la cache del sistema operativo, se desideri eseguire query sui dati più vecchi di quelli che può contenere il head block.
E questo significa anche che il consumo di memoria virtuale di Prometheus apparirà abbastanza elevato, di cui non c'è motivo di preoccuparsi.

Analisi TSDB in Prometheus 2

Un altro interessante aspetto del design è l'uso del WAL (write ahead log). Come si può vedere dalla documentazione dello storage, Prometheus utilizza il WAL per evitare perdite in caso di crash. Sfortunatamente, i meccanismi specifici che garantiscono la persistenza dei dati non sono documentati a sufficienza. La versione 2.3.2 di Prometheus esegue il flush del WAL su disco ogni 10 secondi e questo parametro non è configurabile dall'utente.

Compattazioni

Prometheus TSDB è progettato secondo il modello di uno storage LSM (Log Structured Merge - albero strutturato per log con merge): il blocco head viene periodicamente scaricato su disco, mentre il meccanismo di compattazione unisce diversi blocchi per evitare di eseguire la scansione di un numero eccessivo di blocchi durante le query. Qui è visibile il numero di blocchi che ho osservato nel sistema di prova dopo un giorno di carico.

Analisi TSDB in Prometheus 2

Se desideri saperne di più sullo storage, puoi esaminare il file meta.json, che contiene informazioni sui blocchi presenti e su come sono stati creati.

{
       "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
}

Le compattazioni in Prometheus sono legate al momento in cui il blocco head viene scaricato su disco. In quel momento possono essere eseguite diverse di queste operazioni.

Analisi TSDB in Prometheus 2

A quanto pare, le compressioni non sono in alcun modo limitate e possono causare grandi picchi nell'I/O del disco durante l'esecuzione.

Analisi TSDB in Prometheus 2

Picchi di utilizzo della CPU

Analisi TSDB in Prometheus 2

Ovviamente, ciò influisce in modo piuttosto negativo sulla velocità del sistema e rappresenta anche una seria sfida per i database LSM: come eseguire le compressioni per supportare un'elevata velocità delle richieste senza causare un overhead eccessivo?
L'uso della memoria durante il processo di compressione appare anch'esso piuttosto interessante.

Analisi TSDB in Prometheus 2

Possiamo osservare come, dopo la compressione, gran parte della memoria cambi stato da Cached a Free: questo significa che informazioni potenzialmente preziose sono state rimosse. È interessante sapere se qui viene utilizzato fadvice() o qualche altra tecnica di minimizzazione, o se è causato dal fatto che la cache è stata liberata dai blocchi distrutti durante la compressione?

Ripristino dopo un guasto

Il ripristino dopo i guasti richiede tempo, e questo è giustificato. Per un flusso in ingresso di un milione di record al secondo, ho dovuto attendere circa 25 minuti mentre avveniva il ripristino, tenendo conto del disco SSD.

level=info ts=2018-09-13T13:38:14.09650965Z caller=main.go:222 msg="Avvio di Prometheus" version="(versione=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 (nessuno))"
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="Inizio ascoltando le connessioni" address=:9090
level=info ts=2018-09-13T13:38:14.097400393Z caller=main.go:533 msg="Inizio TSDB ..."
level=info ts=2018-09-13T13:38:14.098718401Z caller=repair.go:39 component=tsdb msg="trovato blocco sano" mint=1536530400000 maxt=1536537600000 ulid=01CQ0FW3ME8Q5W2AN5F9CB7R0R
level=info ts=2018-09-13T13:38:14.100315658Z caller=web.go:467 component=web msg="prefisso router" prefix=\/prometheus
level=info ts=2018-09-13T13:38:14.101793727Z caller=repair.go:39 component=tsdb msg="trovato blocco sano" mint=1536732000000 maxt=1536753600000 ulid=01CQ78486TNX5QZTBF049PQHSM
level=info ts=2018-09-13T13:38:14.102267346Z caller=repair.go:39 component=tsdb msg="trovato blocco sano" mint=1536537600000 maxt=1536732000000 ulid=01CQ78DE7HSQK0C0F5AZ46YGF0
level=info ts=2018-09-13T13:38:14.102660295Z caller=repair.go:39 component=tsdb msg="trovato blocco sano" mint=1536775200000 maxt=1536782400000 ulid=01CQ7SAT4RM21Y0PT5GNSS146Q
level=info ts=2018-09-13T13:38:14.103075885Z caller=repair.go:39 component=tsdb msg="trovato blocco sano" mint=1536753600000 maxt=1536775200000 ulid=01CQ7SV8WJ3C2W5S3RTAHC2GHB
level=error ts=2018-09-13T14:05:18.208469169Z caller=wal.go:275 component=tsdb msg="Corruzione WAL rilevata; troncamento" err="checksum CRC32 inaspettato d0465484, richiesto 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 avviata"
level=info ts=2018-09-13T14:05:19.471604598Z caller=main.go:603 msg="Caricamento file di configurazione" filename=\/etc\/prometheus.yml
level=info ts=2018-09-13T14:05:19.499156711Z caller=main.go:629 msg="Completato il caricamento del file di configurazione" filename=\/etc\/prometheus.yml
level=info ts=2018-09-13T14:05:19.499228186Z caller=main.go:502 msg="Il server è pronto a ricevere richieste web."

Il problema principale del processo di ripristino è l'alto consumo di memoria. Anche se in situazioni normali il server può funzionare stabilmente con la stessa quantità di memoria, in caso di crash potrebbe non riuscire a riavviarsi a causa di OOM. L'unica soluzione che ho trovato è disabilitare la raccolta dei dati, avviare il server, consentirgli di recuperarsi e poi riavviarlo con la raccolta attivata.

Riscaldamento

Un altro comportamento da tenere presente durante il riscaldamento è la relazione tra basse prestazioni e alto consumo di risorse subito dopo l'avvio. Durante alcune, ma non tutte, le accensioni ho notato un carico serio della CPU e della memoria.

Analisi TSDB in Prometheus 2

Analisi TSDB in Prometheus 2

Le fluttuazioni nell'uso della memoria indicano che Prometheus non riesce a configurare tutti i collezionisti all'avvio, e alcune informazioni risultano perse.
Non ho chiarito le cause esatte dell'elevato carico sulla CPU e sulla memoria. Sospetto che sia collegato alla creazione di nuove serie temporali nel blocco principale ad alta frequenza.

Picchi di carico sulla CPU

Oltre alle compressioni, che creano un carico abbastanza elevato in termini di I/O, ho notato significativi picchi di carico sulla CPU ogni due minuti. Gli exploit durano di più con un alto flusso di ingressi e sembra che siano causati dal garbage collector di Go, poiché almeno alcuni core sono completamente sovraccarichi.

Analisi TSDB in Prometheus 2

Analisi TSDB in Prometheus 2

Questi picchi non sono affatto trascurabili. Sembra che quando si verificano, il punto di ingresso interno e le metriche di Prometheus diventino inaccessibili, causando lacune nei dati in quegli stessi intervalli di tempo.

Analisi TSDB in Prometheus 2

Si può anche notare che l'esportatore Prometheus si blocca per un secondo.

Analisi TSDB in Prometheus 2

Possiamo notare correlazioni con la pulizia della memoria (GC).

Analisi TSDB in Prometheus 2

Conclusione

Il TSDB in Prometheus 2 opera rapidamente, in grado di gestire milioni di serie temporali e, allo stesso tempo, migliaia di scritture al secondo, utilizzando hardware piuttosto modesto. Anche l'utilizzo della CPU e dell'I/O del disco è impressionante. Il mio esempio mostrava fino a 200.000 metriche al secondo su un singolo core utilizzato.

Per pianificare un'espansione, è importante considerare volumi di memoria sufficienti, e questa deve essere memoria reale. La quantità di memoria utilizzata che ho osservato era di circa 5 GB per 100.000 scritture al secondo in entrata, che portava a circa 8 GB di memoria utilizzata complessivamente con la cache del sistema operativo.

Naturalmente, c'è ancora molto lavoro da fare per ridurre i picchi di CPU e I/O del disco, e non è sorprendente, considerando quanto sia ancora giovane il TSDB Prometheus 2 rispetto a InnoDB, TokuDB, RocksDB, WiredTiger, tutti con problemi simili all'inizio del ciclo di vita.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster