TSDB-Analyse in Prometheus 2

TSDB-Analyse in Prometheus 2

Die Zeitreihendatenbank (TSDB) in Prometheus 2 ist ein hervorragendes Beispiel für eine technische Lösung, die im Hinblick auf Datenakkumulationsgeschwindigkeit, Abfrageausführung und Ressourceneffizienz erhebliche Verbesserungen gegenüber dem v2-Speicher in Prometheus 1 bietet. Wir implementierten Prometheus 2 in Percona Monitoring and Management (PMM) und ich hatte die Gelegenheit, die Leistung von Prometheus 2 TSDB zu verstehen. In diesem Artikel werde ich über die Ergebnisse dieser Beobachtungen sprechen.

Durchschnittliche Prometheus-Arbeitsbelastung

Für diejenigen, die den Umgang mit Allzweckdatenbanken gewohnt sind, ist die typische Prometheus-Arbeitslast recht interessant. Die Rate der Datenakkumulation ist tendenziell stabil: Normalerweise senden die von Ihnen überwachten Dienste ungefähr die gleiche Anzahl an Metriken, und die Infrastruktur ändert sich relativ langsam.
Informationsanfragen können aus verschiedenen Quellen stammen. Einige von ihnen, wie zum Beispiel Alerts, streben auch einen stabilen und vorhersehbaren Wert an. Andere, wie z. B. Benutzeranfragen, können Bursts verursachen, obwohl dies bei den meisten Workloads nicht der Fall ist.

Lade Test

Beim Testen habe ich mich auf die Fähigkeit konzentriert, Daten zu sammeln. Ich habe Prometheus 2.3.2, kompiliert mit Go 1.10.1 (als Teil von PMM 1.14), auf dem Linode-Dienst mit diesem Skript bereitgestellt: StackScript. Verwenden Sie dies, um die realistischste Lastgenerierung zu erzielen StackScript Ich habe mehrere MySQL-Knoten mit echter Last gestartet (Sysbench TPC-C-Test), von denen jeder 10 Linux/MySQL-Knoten emulierte.
Alle folgenden Tests wurden auf einem Linode-Server mit acht virtuellen Kernen und 32 GB Speicher durchgeführt, wobei 20 Lastsimulationen ausgeführt wurden, die zweihundert MySQL-Instanzen überwachten. Oder, in Prometheus-Begriffen, 800 Ziele, 440 Scrapes pro Sekunde, 380 Datensätze pro Sekunde und 1,7 Millionen aktive Zeitreihen.

Design

Der übliche Ansatz herkömmlicher Datenbanken, einschließlich der von Prometheus 1.x verwendeten, besteht darin, Folgendes zu tun: Speicherlimit. Wenn es nicht ausreicht, um die Last zu bewältigen, kommt es zu hohen Latenzen und einige Anfragen schlagen fehl. Die Speichernutzung in Prometheus 2 ist per Schlüssel konfigurierbar storage.tsdb.min-block-duration, die bestimmt, wie lange Aufzeichnungen im Speicher gehalten werden, bevor sie auf die Festplatte geschrieben werden (Standard ist 2 Stunden). Die erforderliche Speichermenge hängt von der Anzahl der Zeitreihen, Beschriftungen und Scrapes ab, die dem eingehenden Nettostrom hinzugefügt werden. In Bezug auf den Speicherplatz strebt Prometheus die Nutzung von 3 Bytes pro Datensatz (Sample) an. Andererseits ist der Speicherbedarf viel höher.

Obwohl es möglich ist, die Blockgröße zu konfigurieren, wird nicht empfohlen, sie manuell zu konfigurieren, sodass Sie gezwungen sind, Prometheus so viel Speicher zur Verfügung zu stellen, wie es für Ihre Arbeitslast benötigt.
Wenn nicht genügend Speicher vorhanden ist, um den eingehenden Messwertstrom zu unterstützen, fällt Prometheus der Speicher aus oder der OOM-Killer greift darauf zu.
Das Hinzufügen von Swap zur Verzögerung des Absturzes, wenn Prometheus nicht mehr über genügend Speicher verfügt, hilft nicht wirklich, da die Verwendung dieser Funktion zu einem explosionsartigen Speicherverbrauch führt. Ich denke, es hat etwas mit Go zu tun, seinem Garbage Collector und der Art und Weise, wie es mit Swap umgeht.
Ein weiterer interessanter Ansatz besteht darin, den Kopfblock so zu konfigurieren, dass er zu einem bestimmten Zeitpunkt auf die Festplatte geleert wird, anstatt ihn vom Beginn des Prozesses an zu zählen.

TSDB-Analyse in Prometheus 2

Wie Sie in der Grafik sehen können, erfolgt alle zwei Stunden eine Löschung auf die Festplatte. Wenn Sie den Parameter „Min-Block-Dauer“ auf eine Stunde ändern, erfolgen diese Zurücksetzungen jede Stunde, beginnend nach einer halben Stunde.
Wenn Sie dieses und andere Diagramme in Ihrer Prometheus-Installation verwenden möchten, können Sie dies verwenden Armaturenbrett. Es wurde für PMM entwickelt, passt aber mit geringfügigen Modifikationen in jede Prometheus-Installation.
Wir haben einen aktiven Block namens Kopfblock, der im Speicher gespeichert ist; Blöcke mit älteren Daten sind über verfügbar mmap(). Dadurch entfällt die Notwendigkeit, den Cache separat zu konfigurieren, es bedeutet aber auch, dass Sie genügend Platz für den Betriebssystem-Cache lassen müssen, wenn Sie Daten abfragen möchten, die älter sind, als der Kopfblock aufnehmen kann.
Dies bedeutet auch, dass der Verbrauch des virtuellen Speichers von Prometheus ziemlich hoch erscheinen wird, worüber man sich keine Sorgen machen muss.

TSDB-Analyse in Prometheus 2

Ein weiterer interessanter Designpunkt ist die Verwendung von WAL (Write Ahead Log). Wie Sie der Speicherdokumentation entnehmen können, verwendet Prometheus WAL, um Abstürze zu vermeiden. Spezifische Mechanismen zur Gewährleistung der Datenüberlebensfähigkeit sind leider nicht gut dokumentiert. Prometheus Version 2.3.2 schreibt WAL alle 10 Sekunden auf die Festplatte und diese Option ist nicht vom Benutzer konfigurierbar.

Verdichtungen

Prometheus TSDB ist wie ein LSM-Speicher (Log Structured Merge) konzipiert: Der Kopfblock wird regelmäßig auf die Festplatte geleert, während ein Komprimierungsmechanismus mehrere Blöcke zusammenfasst, um zu vermeiden, dass bei Abfragen zu viele Blöcke gescannt werden. Hier sehen Sie die Anzahl der Blöcke, die ich nach einem Tag Belastung auf dem Testsystem beobachtet habe.

TSDB-Analyse in Prometheus 2

Wenn Sie mehr über den Store erfahren möchten, können Sie sich die Datei meta.json ansehen, die Informationen über die verfügbaren Blöcke und deren Entstehung enthält.

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

Verdichtungen in Prometheus sind an die Zeit gebunden, zu der der Kopfblock auf die Festplatte geleert wird. An diesem Punkt können mehrere solcher Vorgänge ausgeführt werden.

TSDB-Analyse in Prometheus 2

Es scheint, dass Komprimierungen in keiner Weise eingeschränkt sind und während der Ausführung zu großen I/O-Spitzen auf der Festplatte führen können.

TSDB-Analyse in Prometheus 2

CPU-Lastspitzen

TSDB-Analyse in Prometheus 2

Dies wirkt sich natürlich eher negativ auf die Geschwindigkeit des Systems aus und stellt auch eine ernsthafte Herausforderung für den LSM-Speicher dar: Wie kann eine Komprimierung durchgeführt werden, um hohe Anforderungsraten zu unterstützen, ohne zu viel Overhead zu verursachen?
Auch die Verwendung von Speicher im Komprimierungsprozess sieht recht interessant aus.

TSDB-Analyse in Prometheus 2

Wir können sehen, wie sich nach der Komprimierung der Zustand des größten Teils des Speichers von „Cached“ in „Frei“ ändert: Das bedeutet, dass potenziell wertvolle Informationen von dort entfernt wurden. Neugierig, ob es hier verwendet wird fadvice() oder eine andere Minimierungstechnik, oder liegt es daran, dass der Cache von Blöcken befreit wurde, die während der Komprimierung zerstört wurden?

Wiederherstellung nach einem Ausfall

Die Wiederherstellung nach Ausfällen braucht Zeit, und das aus gutem Grund. Bei einem eingehenden Stream von einer Million Datensätzen pro Sekunde musste ich unter Berücksichtigung des SSD-Laufwerks etwa 25 Minuten warten, während die Wiederherstellung durchgeführt wurde.

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."

Das Hauptproblem des Wiederherstellungsprozesses ist der hohe Speicherverbrauch. Obwohl der Server im Normalfall mit der gleichen Speichermenge stabil arbeiten kann, kann es bei einem Absturz aufgrund von OOM zu keiner Wiederherstellung kommen. Die einzige Lösung, die ich gefunden habe, bestand darin, die Datenerfassung zu deaktivieren, den Server hochzufahren, ihn wiederherzustellen und mit aktivierter Erfassung neu zu starten.

Aufwärmen

Ein weiteres Verhalten, das Sie beim Aufwärmen im Auge behalten sollten, ist der Zusammenhang zwischen geringer Leistung und hohem Ressourcenverbrauch direkt nach dem Start. Bei einigen, aber nicht bei allen Starts habe ich eine starke Belastung der CPU und des Speichers festgestellt.

TSDB-Analyse in Prometheus 2

TSDB-Analyse in Prometheus 2

Lücken in der Speichernutzung weisen darauf hin, dass Prometheus nicht alle Sammlungen von Anfang an konfigurieren kann und einige Informationen verloren gehen.
Ich habe die genauen Gründe für die hohe CPU- und Speicherlast nicht herausgefunden. Ich vermute, dass dies auf die Erstellung neuer Zeitreihen im Kopfblock mit hoher Frequenz zurückzuführen ist.

CPU-Lastspitzen

Zusätzlich zu den Komprimierungen, die eine recht hohe I/O-Last erzeugen, sind mir alle zwei Minuten erhebliche Spitzen bei der CPU-Last aufgefallen. Die Bursts sind länger, wenn der Eingabefluss hoch ist, und scheinen durch den Garbage Collector von Go verursacht zu werden, wobei zumindest einige Kerne vollständig ausgelastet sind.

TSDB-Analyse in Prometheus 2

TSDB-Analyse in Prometheus 2

Diese Sprünge sind nicht so unbedeutend. Es scheint, dass in diesem Fall der interne Einstiegspunkt und die Metriken von Prometheus nicht mehr verfügbar sind, was zu Datenlücken in denselben Zeiträumen führt.

TSDB-Analyse in Prometheus 2

Sie können auch feststellen, dass der Prometheus-Exporter für eine Sekunde heruntergefahren wird.

TSDB-Analyse in Prometheus 2

Wir können Zusammenhänge mit der Garbage Collection (GC) feststellen.

TSDB-Analyse in Prometheus 2

Abschluss

TSDB in Prometheus 2 ist schnell und kann mit relativ bescheidener Hardware Millionen von Zeitreihen und gleichzeitig Tausende von Datensätzen pro Sekunde verarbeiten. Auch die CPU- und Festplatten-I/O-Auslastung ist beeindruckend. Mein Beispiel zeigte bis zu 200 Metriken pro Sekunde und verwendetem Kern.

Um eine Erweiterung zu planen, müssen Sie an ausreichend Speicher denken, und dieser muss realer Speicher sein. Die von mir beobachtete Speichernutzung betrug etwa 5 GB pro 100 Datensätze pro Sekunde des eingehenden Streams, was zusammen mit dem Betriebssystem-Cache etwa 000 GB belegten Speicher ergab.

Natürlich gibt es noch viel zu tun, um CPU- und Festplatten-I/O-Spitzen zu bändigen, und das ist nicht verwunderlich, wenn man bedenkt, wie jung TSDB Prometheus 2 im Vergleich zu InnoDB, TokuDB, RocksDB und WiredTiger ist, aber sie hatten alle ähnliche Ergebnisse Probleme früh in ihrem Lebenszyklus.

Source: habr.com

Kommentar hinzufügen