Analisi TSDB in Prometeo 2

Analisi TSDB in Prometeo 2

Il database delle serie temporali (TSDB) in Prometheus 2 è un eccellente esempio di soluzione ingegneristica che offre importanti miglioramenti rispetto allo storage v2 in Prometheus 1 in termini di velocità di accumulo dei dati, esecuzione delle query ed efficienza delle risorse. Stavamo implementando Prometheus 2 in Percona Monitoring and Management (PMM) e ho avuto l'opportunità di comprendere le prestazioni di Prometheus 2 TSDB. In questo articolo parlerò dei risultati di queste osservazioni.

Carico di lavoro medio di Prometeo

Per coloro che sono abituati a gestire database generici, il tipico carico di lavoro di Prometheus è piuttosto interessante. Il tasso di accumulo dei dati tende a essere stabile: in genere i servizi monitorati inviano all'incirca lo stesso numero di parametri e l'infrastruttura cambia in modo relativamente lento.
Le richieste di informazioni possono provenire da diverse fonti. Alcuni di essi, come gli avvisi, mirano anche a un valore stabile e prevedibile. Altri, come le richieste degli utenti, possono causare picchi, sebbene ciò non sia il caso della maggior parte dei carichi di lavoro.

Test di carico

Durante i test mi sono concentrato sulla capacità di accumulare dati. Ho distribuito Prometheus 2.3.2 compilato con Go 1.10.1 (come parte di PMM 1.14) sul servizio Linode utilizzando questo script: StackScript. Per la generazione del carico più realistica, utilizzare this StackScript Ho lanciato diversi nodi MySQL con un carico reale (Test Sysbench TPC-C), ognuno dei quali emulava 10 nodi Linux/MySQL.
Tutti i seguenti test sono stati eseguiti su un server Linode con otto core virtuali e 32 GB di memoria, eseguendo 20 simulazioni di carico monitorando duecento istanze MySQL. O, in termini di Prometeo, 800 obiettivi, 440 scarti al secondo, 380mila record al secondo e 1,7 milioni di serie temporali attive.

disegno

L'approccio abituale dei database tradizionali, incluso quello utilizzato da Prometheus 1.x, è quello limite di memoria. Se non è sufficiente a gestire il carico, si verificheranno latenze elevate e alcune richieste falliranno. L'utilizzo della memoria in Prometheus 2 è configurabile tramite chiave storage.tsdb.min-block-duration, che determina per quanto tempo le registrazioni verranno conservate in memoria prima di essere scaricate su disco (il valore predefinito è 2 ore). La quantità di memoria richiesta dipenderà dal numero di serie temporali, etichette e scrap aggiunti al flusso netto in entrata. In termini di spazio su disco, Prometheus mira a utilizzare 3 byte per record (campione). D'altra parte, i requisiti di memoria sono molto più elevati.

Sebbene sia possibile configurare la dimensione del blocco, non è consigliabile configurarlo manualmente, quindi sei costretto a fornire a Prometheus tutta la memoria necessaria per il tuo carico di lavoro.
Se non c'è abbastanza memoria per supportare il flusso di parametri in arrivo, Prometheus perderà memoria o il killer OOM riuscirà a raggiungerlo.
Aggiungere lo swap per ritardare l'arresto anomalo del sistema quando Prometheus esaurisce la memoria non aiuta molto, perché l'utilizzo di questa funzione provoca un consumo esplosivo di memoria. Penso che abbia qualcosa a che fare con Go, il suo garbage collector e il modo in cui gestisce lo swap.
Un altro approccio interessante è configurare il blocco head in modo che venga scaricato sul disco in un determinato momento, invece di contarlo dall'inizio del processo.

Analisi TSDB in Prometeo 2

Come puoi vedere dal grafico, gli scaricamenti del disco avvengono ogni due ore. Se modifichi il parametro durata blocco minimo su un'ora, questi ripristini avverranno ogni ora, a partire da mezz'ora.
Se desideri utilizzare questo e altri grafici nella tua installazione Prometheus, puoi utilizzare questo pannello di controllo. È stato progettato per PMM ma, con piccole modifiche, si adatta a qualsiasi installazione Prometheus.
Abbiamo un blocco attivo chiamato blocco head che viene archiviato in memoria; i blocchi con dati più vecchi sono disponibili tramite mmap(). Ciò elimina la necessità di configurare la cache separatamente, ma significa anche che è necessario lasciare spazio sufficiente per la cache del sistema operativo se si desidera interrogare dati più vecchi di quelli che il blocco head può ospitare.
Ciò significa anche che il consumo di memoria virtuale di Prometheus sembrerà piuttosto elevato, il che non è qualcosa di cui preoccuparsi.

Analisi TSDB in Prometeo 2

Un altro punto di progettazione interessante è l'uso di WAL (write ahead log). Come puoi vedere dalla documentazione sullo storage, Prometheus utilizza WAL per evitare arresti anomali. I meccanismi specifici per garantire la sopravvivenza dei dati, purtroppo, non sono ben documentati. Prometheus versione 2.3.2 scarica WAL su disco ogni 10 secondi e questa opzione non è configurabile dall'utente.

Compattazioni

Prometheus TSDB è progettato come un archivio LSM (Log Structured Merge): il blocco head viene periodicamente scaricato su disco, mentre un meccanismo di compattazione combina più blocchi insieme per evitare la scansione di troppi blocchi durante le query. Qui puoi vedere il numero di blocchi che ho osservato sul sistema di test dopo una giornata di carico.

Analisi TSDB in Prometeo 2

Se vuoi saperne di più sullo store, puoi esaminare il file meta.json, che contiene informazioni sui blocchi disponibili 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 tempo in cui il blocco della testa viene scaricato sul disco. A questo punto è possibile eseguire diverse operazioni di questo tipo.

Analisi TSDB in Prometeo 2

Sembra che le compattazioni non siano limitate in alcun modo e possano causare grandi picchi di I/O del disco durante l'esecuzione.

Analisi TSDB in Prometeo 2

Picchi di carico della CPU

Analisi TSDB in Prometeo 2

Naturalmente, ciò ha un impatto piuttosto negativo sulla velocità del sistema e pone anche una seria sfida per lo storage LSM: come eseguire la compattazione per supportare tassi di richiesta elevati senza causare troppi costi generali?
Anche l'uso della memoria nel processo di compattazione sembra piuttosto interessante.

Analisi TSDB in Prometeo 2

Possiamo vedere come, dopo la compattazione, la maggior parte della memoria cambia stato da Cached a Free: questo significa che da lì sono state rimosse informazioni potenzialmente preziose. Curioso se è usato qui fadvice() o qualche altra tecnica di minimizzazione, oppure è perché la cache è stata liberata dai blocchi distrutti durante la compattazione?

Recupero dopo un fallimento

Il recupero dai fallimenti richiede tempo, e per una buona ragione. Per un flusso in entrata di un milione di record al secondo ho dovuto attendere circa 25 minuti mentre veniva eseguito il ripristino tenendo conto dell'unità 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."

Il problema principale del processo di ripristino è l'elevato consumo di memoria. Nonostante in una situazione normale il server possa funzionare stabilmente con la stessa quantità di memoria, in caso di arresto anomalo potrebbe non ripristinarsi a causa di OOM. L'unica soluzione che ho trovato è stata disabilitare la raccolta dati, avviare il server, lasciarlo ripristinare e riavviare con la raccolta abilitata.

Riscaldamento

Un altro comportamento da tenere presente durante il riscaldamento è la relazione tra basse prestazioni ed elevato consumo di risorse subito dopo la partenza. Durante alcuni avvii, ma non tutti, ho osservato un carico notevole sulla CPU e sulla memoria.

Analisi TSDB in Prometeo 2

Analisi TSDB in Prometeo 2

Le lacune nell'utilizzo della memoria indicano che Prometheus non può configurare tutte le raccolte dall'inizio e alcune informazioni vanno perse.
Non ho capito le ragioni esatte dell'elevato carico di CPU e memoria. Sospetto che ciò sia dovuto alla creazione di nuove serie temporali nel blocco principale con una frequenza elevata.

Il carico della CPU aumenta

Oltre alle compattazioni, che creano un carico I/O abbastanza elevato, ho notato picchi notevoli nel carico della CPU ogni due minuti. I burst sono più lunghi quando il flusso di input è elevato e sembrano essere causati dal garbage collector di Go, con almeno alcuni core completamente caricati.

Analisi TSDB in Prometeo 2

Analisi TSDB in Prometeo 2

Questi salti non sono così insignificanti. Sembra che quando ciò si verifica, il punto di ingresso interno e le metriche di Prometheus diventino non disponibili, causando lacune nei dati durante questi stessi periodi di tempo.

Analisi TSDB in Prometeo 2

Puoi anche notare che l'esportatore Prometheus si spegne per un secondo.

Analisi TSDB in Prometeo 2

Possiamo notare correlazioni con la garbage collection (GC).

Analisi TSDB in Prometeo 2

conclusione

TSDB in Prometheus 2 è veloce, in grado di gestire milioni di serie temporali e allo stesso tempo migliaia di record al secondo utilizzando hardware abbastanza modesto. Anche l'utilizzo della CPU e dell'I/O del disco è impressionante. Il mio esempio ha mostrato fino a 200 parametri al secondo per core utilizzato.

Per pianificare l'espansione, è necessario ricordare una quantità sufficiente di memoria, e questa deve essere memoria reale. La quantità di memoria utilizzata che ho osservato era di circa 5 GB per 100 record al secondo del flusso in entrata, che insieme alla cache del sistema operativo dava circa 000 GB di memoria occupata.

Naturalmente, c'è ancora molto lavoro da fare per domare i picchi di I/O della CPU e del disco, e questo non sorprende considerando quanto sia giovane TSDB Prometheus 2 rispetto a InnoDB, TokuDB, RocksDB, WiredTiger, ma avevano tutti caratteristiche simili. problemi nelle prime fasi del loro ciclo di vita.

Fonte: habr.com

Aggiungi un commento