Wie wir Prometheus verbunden haben

Irgendwie musste ich mich wie immer mit den Metriken für unsere API auseinandersetzen (keine Zeit?!), Um sie später hinzuzufügen - es ist sehr schwierig und noch nicht implementiert - es bedeutet, dass es Zeit ist, sie zu implementieren. Nach einigen Streifzügen im Netz schien mir Prometheus das beliebteste Überwachungssystem zu sein.


Mit Prometheus können wir verschiedene Computerressourcen überwachen, z. B. Speicher, Prozessor, Festplatte und Netzwerklast. Es kann für uns auch wichtig sein, die Anzahl der Aufrufe der Methoden unserer API zu berechnen oder die Ausführungszeit zu messen, da die Ausfallzeit umso teurer ist, je höher die Systemlast ist. Und hier kommt uns Prometheus zu Hilfe. Dieser Artikel enthält meines Erachtens die wichtigsten Punkte für das Verständnis der Arbeit von Prometheus und für das Hinzufügen einer Sammlung von Metriken zur API. Daher beginnen wir mit der banalsten, mit einer kleinen Beschreibung.


Prometheus ist ein Open-Source-System und ein in Go geschriebenes und von SoundCloud entwickeltes Zeitreihen-DBMS. Es hat offizielle Dokumentation und Unterstützung für Sprachen wie: Go, Java oder Scala, Python, Ruby. Es gibt inoffizielle Unterstützung für andere Sprachen wie C #, C ++, C, Bash, Lua für Nginx, Lua für Tarantool und andere. Die gesamte Liste befindet sich auf der offiziellen Prometheus-Website.


Alle Prometheus-Dienste sind als Docker-Images auf dem Docker Hub oder Quay.io verfügbar.


Prometheus wird mit dem docker run -p 9090:9090 prom/prometheus gestartet, der es mit der Standardkonfiguration startet und den Port localhost:9090 dafür festlegt. Danach ist die Prometheus-Benutzeroberfläche unter localhost:9090 verfügbar.


Prometheus ist ein Überwachungssystem, das verschiedene Tools zur Konfiguration der Überwachung von Anwendungen (Endpunkten) mithilfe des HTTP-Protokolls enthält. Beim Herstellen einer Verbindung zu Prometheus unterstützt die HTTP-API die "Basisauthentifizierung" nicht. Wenn Sie die Basisauthentifizierung für die Verbindung mit Prometheus verwenden möchten, wird empfohlen, Prometheus in Verbindung mit einem Reverse-Proxy-Server zu verwenden und die Authentifizierung auf Proxy-Ebene zu verwenden. Sie können mit Prometheus jeden Reverse-Proxy verwenden.


Die Hauptkomponenten von Prometheus:


  • ein Server, der Metriken sammelt, in der Datenbank speichert und bereinigt;
  • Pakete zum Sammeln von Metriken in der API;
  • Pushgateway - Komponente zum Empfangen von Metriken aus Anwendungen, für die eine Pull-Anforderung nicht verwendet werden kann.
  • Exporter - Tools zum Exportieren von Metriken aus Anwendungen und Diensten von Drittanbietern, die auf Zielcomputern installiert sind.
  • AlertManager - Benachrichtigungsmanager (Warnungen). Warnungen werden in der Konfigurationsdatei definiert und durch eine Reihe von Regeln für Metriken festgelegt.
    Wenn während des Betriebs die Regel eingehalten wird, wird ein Alarm ausgelöst und per E-Mail, Slack oder anderen an die angegebenen Empfänger gesendet.

Die Objekte, mit denen Prometheus arbeitet, werden Metriken genannt, die von Zielen entweder über Pushgateway oder über Exporteure empfangen werden.


Beim Sammeln von Metriken werden verschiedene Methoden zum Übertragen von Metriken verwendet:


  • Prometheus fordert Metriken vom Ziel über eine Pull-Anforderung an, deren Einstellungen in der Konfigurationsdatei im Abschnitt scrape_config für jeden Job angegeben sind.
    Wenn das System Daten erfasst, können Sie die Häufigkeit der Erfassung steuern und verschiedene Konfigurationen der Datenerfassung erstellen, um eine unterschiedliche Häufigkeit für verschiedene Objekte auszuwählen.
  • Mit Exportern können Sie Metriken von verschiedenen Objekten sammeln, z. B. von Datenbanken (MongoDB, SQL usw.), Nachrichtenbrokern (RabbitMQ, EMQ, NSQ usw.), HTTP - Load - Balancern usw .;
  • Pushgateway. Es kann bei Bedarf verwendet werden, wenn die Anwendung Prometheus keine direkten Metriken zur Verfügung stellen kann. oder wenn Sie Stapeljobs verwenden, die die Prometheus-Pull-Anforderung nicht verwenden können.

Somit werden alle empfangenen Metriken von Prometheus in einer Datenbank mit Zeitstempeln gespeichert.


Konfiguration


Prometheus wird mithilfe der Befehlszeilenflags und Konfigurationsdateien im YAML-Format konfiguriert. Mit Befehlszeilen-Flags können Sie unveränderliche Parameter konfigurieren, z. B. Pfade, auf der Festplatte und im Speicher gespeicherte Datenmengen usw. In der Konfigurationsdatei können Sie alles konfigurieren, was mit Jobs zu tun hat, und geladene Regel-Yaml-Dateien einrichten. Alles ist in der globalen Konfigurationsdatei geschrieben. Sie können allgemeine Einstellungen für alle Benutzer vornehmen und Einstellungen für verschiedene Konfigurationsabschnitte separat markieren. Die Einstellungen, die Prometheus abruft, werden in der Konfigurationsdatei im Abschnitt scrape_configs konfiguriert.


Prometheus kann Konfigurationsdateien während des Betriebs neu laden. Wenn die neue Konfiguration ungültig ist, wird sie nicht angewendet. Der Neustart der Konfigurationsdatei wird durch Senden des Befehls SIGHUP Prometheus oder Senden der HTTP-POST-Anforderung an /-/reload --web.enable-lifecycle , sofern das --web.enable-lifecycle . Es werden auch alle konfigurierten Regeldateien neu geladen.


Welche Arten von Daten werden verwendet?


Prometheus speichert ein benutzerdefiniertes mehrdimensionales Datenmodell und verwendet eine Abfragesprache für mehrdimensionale Daten namens PromQL. Prometheus speichert Daten in Form von Zeitreihen und unterstützt verschiedene Speichermöglichkeiten:


  • lokaler festplattenspeicher: Alle 2 Stunden werden Daten, die im Speicher zwischengespeichert wurden, komprimiert und auf der Festplatte gespeichert. Standardmäßig wird das Verzeichnis ./data im Arbeitsverzeichnis zum Speichern komprimierter Dateien verwendet.
  • Remote-Repository: Prometheus unterstützt die Integration in Repositorys von Drittanbietern (z. B. Kafka, PostgreSQL, Amazon S3 usw.) über den Protocol Buffer-Adapter.

Die gespeicherte Zeitreihe wird durch die Metrik und die Metadaten in Form von Schlüssel-Wert-Paaren bestimmt, obwohl der Name der Metrik gegebenenfalls nicht verwendet werden kann und die Metrik selbst nur aus Metadaten besteht. Eine Zeitreihe kann formal als <Metrikname> {<Metadaten>} definiert werden. Der Schlüssel ist <metrischer Name> {<Metadaten>} - das, was wir messen, und der Wert ist der tatsächliche Wert als Zahl mit dem Typ float64 (Prometheus unterstützt nur diesen Typ). Die Schlüsselbeschreibung enthält Metadaten (Labels), die auch durch Schlüssel-Wert-Paare beschrieben werden: <Label-Name> = "<Label-Wert>", <Label-Name> = "<Label-Wert>", ...


Beim Speichern von Metriken werden die folgenden Datentypen verwendet:


  • Zähler - Zählt den Betrag über einen bestimmten Zeitraum. Diese Art von Metriken kann nur erhöht werden (Sie können keine negativen Werte verwenden) oder den Wert zurücksetzen.
    Es kann zum Beispiel zum Zählen der Anzahl von Anfragen pro Minute oder der Anzahl von Fehlern pro Tag, der Anzahl von gesendeten / empfangenen Netzwerkpaketen usw. geeignet sein.
  • Messgerät - speichert Werte, die mit der Zeit abnehmen oder zunehmen können.
    Das Messgerät zeigt nicht die Entwicklung von Metriken über einen bestimmten Zeitraum an. Mit Gauge können Sie mit der Zeit unregelmäßige metrische Änderungen verlieren.
  • Histogramm - speichert mehrere Zeitreihen: die Gesamtsumme aller beobachteten Werte; die Anzahl der beobachteten Ereignisse;
    Akkumulationszähler (Eimer) - werden auf dem Etikett als le="<upper inclusive bound>" .
    Werte werden in Bereichen mit benutzerdefinierten Obergrenzen (Buckets) erfasst.
  • Zusammenfassung - speichert mehrere Zeitreihen: die Gesamtsumme aller beobachteten Werte; die Anzahl der beobachteten Ereignisse;
    Fluss φ-Quantile (0 ≤ φ ≤ 1) von beobachteten Ereignissen - werden auf dem Etikett als quantile="<φ>" .

Wie werden Daten gespeichert?


Prometheus empfiehlt, einer laufenden Anwendung 2/3 des Arbeitsspeichers zuzuweisen.
Prometheus verwendet zum Speichern von Daten Chunk-Dateien, wobei jede Metrik eine eigene Datei hat. Alle Chunk-Dateien sind unveränderlich, mit Ausnahme der letzten, in die Daten geschrieben werden. Neue Daten werden im Chunk gespeichert und alle 2 Stunden werden die Daten vom Hintergrundstream kombiniert und auf die Festplatte geschrieben. Jeder zweistündige Block besteht aus einem Verzeichnis mit einer oder mehreren Chunk-Dateien, die alle Zeitreihenbeispiele für diesen Zeitraum enthalten, sowie einer Metadatendatei und einer Indexdatei (die die Namen der Metriken und Bezeichnungen für Zeitreihen in den Chunk-Dateien indiziert). Wenn Prometheus innerhalb einer Stunde keine Daten in Chunck schreibt, werden diese auf der Festplatte gespeichert und es wird ein neuer Chunck zum Schreiben von Daten erstellt. Die maximale Datenaufbewahrungsdauer in Prometheus beträgt ~ 21 Tage.


Weil Wenn die Größe des Speichers festgelegt ist, wird die Schreib- und Leseleistung des Systems durch diese Speicherkapazität begrenzt. Die Größe des PTSDB-Speichers wird durch den Mindestzeitraum, den Erfassungszeitraum und die Anzahl der Zeitmetriken bestimmt.


Prometheus hat auch einen WAL-Mechanismus, um Datenverlust zu verhindern.


Write Ahead Log (WAL) serialisiert gespeicherte Vorgänge auf einem permanenten Medium in Form von Protokolldateien. Im Falle eines Fehlers können WAL-Dateien verwendet werden, um die Datenbank durch Wiederherstellen aus Protokollen in ihren konsistenten Zustand zurückzusetzen.


Protokolldateien werden in einem Verzeichnis mit 128 MB gespeichert. Diese Dateien enthalten Rohdaten, die noch nicht komprimiert wurden. Sie sind daher erheblich größer als normale Fragmentdateien.


Prometheus speichert mindestens 3 Protokolldateien, Server mit hohem Datenverkehr können jedoch mehr als drei WAL-Dateien anzeigen, da mindestens zwei Stunden Rohdaten gespeichert werden müssen.


Das Ergebnis der Verwendung von WAL ist eine signifikante Reduzierung der Anzahl der Schreibanforderungen auf die Festplatte Es muss nur eine Protokolldatei auf die Festplatte geschrieben werden, und nicht alle Daten, die infolge des Vorgangs geändert wurden. Die Protokolldatei wird sequentiell geschrieben, und daher sind die Kosten für die Synchronisierung des Protokolls viel geringer als die Kosten für das Schreiben von Fragmenten mit Daten.


Prometheus speichert regelmäßige Haltepunkte, die standardmäßig alle 2 Stunden hinzugefügt werden, indem Protokolle für den letzten Zeitraum komprimiert und auf der Festplatte gespeichert werden.


Alle Haltepunkte werden im selben Verzeichnis wie checkpoint.ddd gespeichert, wobei ddd eine monoton ansteigende Zahl ist. Bei der Wiederherstellung nach einem Fehler können daher Haltepunkte aus dem Haltepunktkatalog mit Angabe der Reihenfolge (.ddd) wiederhergestellt werden.
Durch das Schreiben von WAL-Protokollen können Sie zu jedem Prüfpunkt zurückkehren, für den das Datenprotokoll verfügbar ist.


Was ist in der Praxis passiert?


Beim Hinzufügen zum Projekt (.Net Framework) haben wir das Prometheus.Client.3.0.2-Paket zum Sammeln von Metriken verwendet. Um Metriken zu sammeln, wurden die erforderlichen Methoden und Klassen zum Projekt hinzugefügt, um Metriken zu speichern, bis sie von Prometheus empfangen werden.


Ursprünglich wurde eine IMetricsService-Schnittstelle definiert, die Timer-Methoden zum Messen der Funktionsdauer von Methoden enthielt:


 public interface IMetricsService { Stopwatch StartTimer(); void StopTimer(Stopwatch timer, string controllerName, string actionName, string methodName = "POST"); } 

Wir fügen die MetricsService-Klasse hinzu, die die IMetricsService-Schnittstelle implementiert und Metriken temporär speichert.


 public class MetricsService : IMetricsService { private static Histogram _histogram; static MetricsService() { _histogram = CreateHistogram(); } public Stopwatch StartTimer() { try { var timer = new Stopwatch(); timer.Start(); return timer; } catch (Exception exception) { Logger.Error(exception); } return null; } public void StopTimer(Stopwatch timer, string controllerName, string actionName, string methodName = "POST") { try { if (timer == null) { throw new ArgumentException($"{nameof(timer)} can't be null."); } timer.Stop(); _histogram .WithLabels(controllerName, actionName, methodName) .Observe(timer.ElapsedMilliseconds, DateTimeOffset.UtcNow); } catch (Exception exception) { Logger.Error(exception); } } public static List<string> GetAllLabels() { var metricsList = new List<string>(); try { foreach (var keyValuePair in _histogram.Labelled) { var controllerName = keyValuePair.Key.Labels[0].Value; var actionName = keyValuePair.Key.Labels[1].Value; var methodName = keyValuePair.Key.Labels[2].Value; var requestDurationSum = keyValuePair.Value.Value.Sum; var requestCount = keyValuePair.Value.Value.Count; metricsList.Add($"http_request_duration_widget_sum{{controller={controllerName},action={actionName},method={methodName}}} {requestDurationSum}"); metricsList.Add($"http_request_duration_widget_count{{controller={controllerName},action={actionName},method={methodName}}} {requestCount}"); } _histogram = CreateHistogram(); } catch (Exception exception) { Logger.Error(exception); } return metricsList; } private static Histogram CreateHistogram() { var newMetrics = Metrics .WithCustomRegistry(new CollectorRegistry()) .CreateHistogram(name: "http_request_duration_web_api", help: "Histogram metrics of Web.Api", includeTimestamp: true, labelNames: new[] { "controller", "action", "method" }); var oldValue = _histogram; for (var i = 0; i < 10; i++) { var oldValue = Interlocked.Exchange<Histogram>(ref oldValue, newMetrics); if (oldValue != null) { return oldValue; } } return null; } } 

Jetzt können wir unsere Klasse verwenden, um die zu sammelnden Metriken in den Methoden Application_BeginRequest, Application_Error, Application_EndRequest zu speichern. In der Global.cs-Klasse fügen wir den obigen Methoden eine Sammlung von Metriken hinzu.


 private IMetricsService _metricsService; protected virtual void Application_BeginRequest(object sender, EventArgs e) { var context = new HttpContextWrapper(HttpContext.Current); var metricServiceTimer = _metricsService.StartTimer(); context.Items.Add("metricsService", _metricsService); context.Items.Add("metricServiceTimer", metricServiceTimer); } protected virtual void Application_EndRequest(object sender, EventArgs e) { WriteMetrics(new HttpContextWrapper(HttpContext.Current)); } protected void Application_Error(object sender, EventArgs e) { WriteMetrics(new HttpContextWrapper(HttpContext.Current)); } private void WriteMetrics(HttpContextBase context) { try { _metricsService = context.Items["metricsService"] as IMetricsService; if (_metricsService != null) { var timer = context.Items["metricServiceTimer"] as Stopwatch; string controllerName = null; string actionName = null; var rd = RouteTable.Routes.GetRouteData(context); if (rd != null) { controllerName = rd.GetRequiredString("controller"); actionName = rd.GetRequiredString("action"); } _metricsService.StopTimer(timer, controllerName, actionName, context.Request.HttpMethod); } } catch (Exception exception) { Logger.Error("Can't write metrics.", exception); } } 

Fügen Sie einen neuen Controller hinzu, der als Referenzpunkt für das Senden der Messdaten unserer API an Prometheus dient:


 public class MetricsController : Controller { [HttpGet] public string[] GetAllMetrics() { try { var metrics = MetricsService.GetAllLabels(); return metrics.ToArray(); } catch (Exception exception) { Logger.Error(exception); } return new string[] { }; } } 

Der letzte Schritt besteht darin, die Prometheus-Konfiguration so zu konfigurieren, dass Metriken im Abschnitt scrape_configs erfasst werden. Danach können wir die erfassten Metriken bereits in der Prometheus- oder Grafana-Benutzeroberfläche anzeigen.


Wichtige Features, an denen wir bei Prometheus interessiert waren:


Mehrdimensionales Datenmodell: Metriken und Beschriftungen.
Flexible PromQL-Abfragesprache. In demselben Abfrageoperator können Operationen wie Multiplikation, Addition, Verkettung usw. Verwendet werden. kann mit mehreren Metriken durchgeführt werden.
Sammelt HTTP-basierte Daten mithilfe der Pull-Methode.
Kompatibel mit der Push-Methode über Pushgateway.
Es ist möglich, Metriken aus anderen Anwendungen über Exporteure zu erfassen.
Bietet einen Mechanismus zum Verhindern von Datenverlust.
Unterstützt verschiedene grafische Darstellungen von Daten.

Source: https://habr.com/ru/post/de475600/


All Articles