BLOG | NGINX

Integration von OpenTelemetry in die Modern Apps Reference Architecture – Ein Fortschrittsbericht

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Elijah Zupancic Miniaturbild
Elijah Zupancic
Veröffentlicht am 08. März 2022
Jason Schmidt Miniaturbild
Jason Schmidt
Veröffentlicht am 08. März 2022

Als wir im vergangenen Herbst bei Sprint 2.0 das NGINX Modern Apps Reference Architecture (MARA)-Projekt vorstellten , betonten wir unsere Absicht, dass es kein „Spielzeug“ wie manche Architekturen sein sollte, sondern vielmehr eine Lösung, die „solide, getestet und bereit für den Einsatz in Live-Produktionsanwendungen ist, die in Kubernetes-Umgebungen ausgeführt werden“. Für ein solches Projekt sind Beobachtungswerkzeuge eine absolute Voraussetzung. Alle Mitglieder des MARA-Teams haben aus erster Hand erfahren, wie die Entwicklung und Bereitstellung von Anwendungen zu einer frustrierenden Angelegenheit wird, wenn kein Einblick in Status und Leistung vorhanden ist. Wir waren uns sofort einig, dass MARA Instrumente zum Debuggen und Tracing in einer Produktionsumgebung enthalten muss.

Ein weiterer Leitgedanke der MARA ist die Bevorzugung von Open Source-Lösungen. In diesem Beitrag beschreiben wir, wie wir bei unserer Suche nach einem multifunktionalen Open-Source-Beobachtungstool auf OpenTelemetry gestoßen sind. Anschließend gehen wir detailliert auf die Kompromisse, Designentscheidungen, Techniken und Technologien ein, die wir zur Integration von OpenTelemetry in eine mit Python, Java und NGINX erstellte Microservices-Anwendung verwendet haben.

Wir hoffen, dass Ihnen unsere Erfahrungen dabei helfen, potenzielle Fallstricke zu vermeiden und die Einführung von OpenTelemetry zu beschleunigen. Bitte beachten Sie, dass es sich bei diesem Beitrag um einen zeitkritischen Fortschrittsbericht handelt. Wir gehen davon aus, dass die besprochenen Technologien innerhalb eines Jahres ausgereift sein werden. Auch wenn wir auf aktuelle Mängel in einigen Projekten hinweisen, sind wir für die gesamte Open-Source-Arbeit ungemein dankbar und freuen uns, ihre Fortschritte zu verfolgen.

Unsere Anwendung

Als Anwendung zur Integration mit einer Observability-Lösung haben wir uns für die Bank of Sirius entschieden, unseren Fork der Beispiel-App Bank of Anthos von Google. Es handelt sich um eine Web-App mit einer Microservices-Architektur, die über eine Infrastruktur als Code bereitgestellt werden kann. Die Leistung und Zuverlässigkeit dieser Anwendung kann auf vielfältige Weise verbessert werden. Sie ist jedoch ausgereift genug, um vernünftigerweise als Brownfield -Anwendung betrachtet zu werden. Daher glauben wir, dass es ein gutes Beispiel dafür ist, wie OpenTelemetry in eine Anwendung integriert werden kann, da die verteilte Ablaufverfolgung theoretisch wertvolle Erkenntnisse über die Mängel einer Anwendungsarchitektur liefert.

Wie im Diagramm dargestellt, sind die Dienste, die die Anwendung unterstützen, relativ unkompliziert.

Bild mit freundlicher Genehmigung von Google

Warum wir uns für OpenTelemetry entschieden haben

Unser Weg zur Auswahl von OpenTelemetry war ziemlich kurvenreich und umfasste mehrere Etappen.

Erstellen einer Feature-Wunschliste

Bevor wir die verfügbaren Open-Source-Beobachtungstools selbst bewerteten, ermittelten wir, welche Aspekte der Beobachtung für uns wichtig sind. Aufgrund unserer bisherigen Erfahrungen haben wir die folgende Liste erstellt.

  • Protokollierung – So wie wir den Begriff verwenden, bedeutet dies die Generierung klassischer, durch Zeilenumbrüche getrennter Nachrichtensätze aus Anwendungen; die Bank of Sirius-App strukturiert ihre Protokolle im Bunyan-Format
  • Verteiltes Tracing – Timings und Metadaten für jede Komponente innerhalb der gesamten Anwendung, wie sie von Anbietern von Application Performance Management (APM) bereitgestellt werden.
  • Metriken – Über einen bestimmten Zeitraum erfasste und als Zeitreihendaten grafisch dargestellte Messungen
  • Ausnahme-/Fehleraggregation und -benachrichtigung – Eine aggregierte Sammlung der häufigsten Ausnahmen und Fehler, die durchsuchbar sein muss, damit wir feststellen können, welche Anwendungsfehler am häufigsten auftreten
  • Integritätsprüfungen – Regelmäßige Tests werden an die Dienste gesendet, um festzustellen, ob sie innerhalb der Anwendung ordnungsgemäß funktionieren.
  • Laufzeitstatus-Introspektion – Eine Reihe von APIs, die nur für Administratoren sichtbar sind und Informationen über den Laufzeitstatus der Anwendung zurückgeben
  • Heap/Core Dumps – Umfassende Snapshots des Laufzeitzustands eines Dienstes. Für unsere Zwecke ist entscheidend, wie einfach es ist, diese Dumps bei Bedarf oder beim Absturz eines Dienstes abzurufen.

Vergleichen der Toolfunktionen mit der Wunschliste

Natürlich haben wir nicht erwartet, dass ein einzelnes Open-Source-Tool oder -Ansatz alle diese Funktionen umfasst, aber es hat uns zumindest eine erstrebenswerte Grundlage für den Vergleich der verfügbaren Tools geboten. Wir haben die Dokumentation jedes Tools konsultiert, um herauszufinden, welche der sieben Funktionen auf unserer Wunschliste es unterstützt. Die Tabelle fasst unsere Ergebnisse zusammen.

Technologie Protokollierung Verteiltes Tracing Metriken Fehleraggregation Gesundheitschecks Laufzeit-Introspektion Heap-/Core-Dumps
ELK + Elastisches APM
Grafana
Graylog
Jaeger
OpenCensus
Offene Telemetrie Beta *
Prometheus
StatistikenD
Zipkin

* Als Erweiterung

Der Bau dieses Tisches war ein böses Erwachen. Die Fähigkeiten und Verwendungszwecke der verschiedenen Werkzeuge unterscheiden sich so sehr, dass wir sie nicht alle in dieselbe Kategorie einordnen konnten – das war, als würden wir Äpfel mit Toastern vergleichen!

Beispielsweise machen ELK ( Elasticsearch-Logstash-Kibana plus Filebeat ) und Zipkin so grundlegend unterschiedliche Dinge, dass der Versuch, sie zu vergleichen, eher verwirrend als alles andere ist. Leider macht dieser „Mission Creep“ die Sache nur noch komplizierter: Zweifellos als Reaktion auf Anfragen von Benutzern wurden Funktionen hinzugefügt, die für den Hauptzweck des Tools zweitrangig sind und zu Überschneidungen mit anderen Tools führen. Oberflächlich betrachtet übernimmt ELK die Speicherung und Visualisierung von Protokollen, wohingegen Zipkin die verteilte Ablaufverfolgung übernimmt. Wenn Sie sich jedoch etwas eingehender mit dem Elastic-Produktportfolio befassen, stoßen Sie schnell auf Elastic APM , das verteiltes Tracing unterstützt und sogar mit Jaeger kompatibel ist.

Abgesehen vom Problem der schleichenden Ausweitung des Aufgabenbereichs können viele der Tools miteinander integriert werden, sodass unterschiedliche Kombinationen von Collectoren, Aggregatoren, Dashboards usw. entstehen. Einige der Technologien sind miteinander kompatibel, andere nicht.

Durchführen einer qualitativen Untersuchung

Daher lieferte uns diese Vergleichstabelle kein ausreichend genaues Bild als Grundlage für unsere Entscheidung. Wir mussten die Ziele, Leitlinien und mögliche zukünftige Ausrichtung jedes Projekts qualitativ untersuchen. Dabei gingen wir davon aus, dass die Kompatibilität im Laufe der Zeit umso größer ist, je ähnlicher die Werte eines Projekts unseren sind. Als wir die Projektseiten besuchten, fiel uns dies auf der OpenCensus -Seite sofort auf.

OpenCensus und OpenTracing wurden zu OpenTelemetry fusioniert, das als nächste Hauptversion von OpenCensus und OpenTracing dient. OpenTelemetry bietet Abwärtskompatibilität mit vorhandenen OpenCensus-Integrationen und wir werden zwei Jahre lang weiterhin Sicherheitspatches für vorhandene OpenCensus-Bibliotheken bereitstellen.

Für uns war das ein wichtiger Datenpunkt. Wir wussten, dass wir nicht garantieren konnten, dass unsere Wahl zukunftssicher sein würde, wollten aber zumindest die Gewissheit haben, dass sie in Zukunft eine solide Unterstützung aus der Community genießen wird. Mit diesen Informationen könnten wir OpenCensus von unserer Kandidatenliste streichen. Es war wahrscheinlich auch keine gute Idee, Jaeger zu verwenden, da es sich dabei um die Referenzimplementierung für das mittlerweile offiziell veraltete OpenTracing- Projekt handelt – der Großteil der neuen Beiträge wird in OpenTelemetry landen.

Dann haben wir uns über den OpenTelemetry Collector informiert.

Der OpenTelemetry Collector bietet eine herstellerunabhängige Implementierung zum Empfangen, Verarbeiten und Exportieren von Telemetriedaten. Darüber hinaus entfällt die Notwendigkeit, mehrere Agenten/Sammler auszuführen, zu betreiben und zu warten, um Open-Source-Telemetriedatenformate (z. B. Jaeger, Prometheus usw.) zu unterstützen, die an mehrere Open-Source- oder kommerzielle Back-Ends gesendet werden.

Der OpenTelemetry Collector fungiert als Aggregator, der es uns ermöglicht, verschiedene Methoden zur Erfassung und Instrumentierung von Beobachtungsdaten mit unterschiedlichen Backends zu kombinieren. Grundsätzlich kann unsere Anwendung Traces mit Zipkin und Metriken von Prometheus sammeln, die wir dann an ein konfigurierbares Backend senden und mit Grafana visualisieren können. Es sind mehrere andere Permutationen dieses Designs möglich, sodass wir unterschiedliche Ansätze ausprobieren können, um zu sehen, welche Technologien für unseren Anwendungsfall gut geeignet sind.

Integration von OpenTelemetry als Weg in die Zukunft

Im Grunde hat uns der OpenTelemetry Collector überzeugt, weil er uns theoretisch den Wechsel zwischen Observability-Technologien ermöglicht. Obwohl das Projekt noch relativ unreif war, haben wir uns entschlossen, mutig einzusteigen und OpenTelemetry ausschließlich mit Open-Source-Integrationen zu verwenden, denn die einzige Möglichkeit, die Landschaft zu verstehen, besteht in der Nutzung der Technologien.

Da jedoch einige Teile im OpenTelemetry Collector fehlen, müssen wir für diese Funktionen auf andere Technologien zurückgreifen. In den folgenden Abschnitten fassen wir unsere Entscheidungen und die Gründe dafür zusammen.

Implementieren der Protokollierung

Die Protokollierung ist ein täuschend einfacher Teil der Beobachtbarkeit, der schnell zu komplizierten Entscheidungen führt. Dies ist einfach, da Sie lediglich die Protokollausgabe aus Ihren Containern sammeln, aber kompliziert, da Sie anschließend entscheiden müssen, wo die Daten gespeichert werden, wie sie dorthin transportiert werden, wie sie indiziert werden, um sie nutzbar zu machen, und wie lange sie aufbewahrt werden. Um nützlich zu sein, müssen Protokolldateien nach genügend unterschiedlichen Kriterien leicht durchsuchbar sein, um den Anforderungen verschiedener Sucher gerecht zu werden.

Wir haben uns die Protokollierungsunterstützung mit dem OpenTelemetry Collector angesehen und zum Zeitpunkt des Schreibens dieses Artikels handelt es sich um eine sehr frühe Version. Wir haben uns entschieden, ELK kurzfristig zum Protokollieren zu verwenden und gleichzeitig andere Optionen zu prüfen.

Da es keine zwingenden Gründe gab, dies nicht zu tun, haben wir standardmäßig die Elasticsearch- Tools verwendet. Dadurch konnten wir die Bereitstellung in Aufnahme-, Koordinierungs-, Master- und Datenknoten aufteilen. Zur einfacheren Bereitstellung haben wir ein Bitnami-Diagramm verwendet. Zum Transport der Daten haben wir Filebeat als Teil eines Kubernetes DaemonSet bereitgestellt. Die Suchfunktion wurde durch die Bereitstellung von Kibana und die Nutzung der vorinstallierten Indizes, Suchvorgänge, Visualisierungen und Dashboards hinzugefügt.

Ziemlich schnell wurde klar, dass diese Lösung zwar funktionierte, die Standardkonfiguration jedoch sehr ressourcenintensiv war, was die Ausführung auf einer Plattform mit geringerem Ressourcenbedarf wie K3S oder Microk8s erschwerte. Dieses Problem wurde behoben, indem die Möglichkeit hinzugefügt wurde, die Anzahl der Replikate für jede Komponente anzupassen. Allerdings kam es auch zu einigen Fehlern, die möglicherweise auf erschöpfte Ressourcen oder übermäßige Datenmengen zurückzuführen waren.

Wir sind darüber keineswegs enttäuscht, sondern betrachten es als eine Gelegenheit, unser Protokollierungssystem mit unterschiedlichen Konfigurationen zu vergleichen und andere Optionen wie Grafana Loki und Graylog zu untersuchen. Wir werden möglicherweise feststellen, dass einfachere Protokollierungslösungen nicht den vollständigen Funktionsumfang bieten, den manche Benutzer benötigen und den ihnen ressourcenintensivere Tools bieten können. Angesichts des modularen Charakters von MARA werden wir wahrscheinlich zusätzliche Module für diese Optionen erstellen, um den Benutzern mehr Auswahl zu bieten.

Implementieren der verteilten Ablaufverfolgung

Neben der Entscheidung, welches Tool die gewünschte Tracing-Funktionalität bietet, mussten wir überlegen, wie die Lösung implementiert werden sollte und welche Technologien darin integriert werden müssen.

Zunächst wollten wir sicherstellen, dass sich eine Instrumentierung nicht negativ auf die Servicequalität der Anwendung selbst auswirkt. Wir alle hatten schon mit Systemen gearbeitet, bei denen es beim Exportieren von Protokollen einmal pro Stunde zu einem vorhersehbaren Leistungsabfall kam, und wir wollten diese Erfahrung nicht noch einmal machen. Die Architektur des OpenTelemetry Collector war in dieser Hinsicht überzeugend, da Sie eine Instanz pro Host ausführen können. Jeder Collector empfängt Daten von Clients und Agenten in allen verschiedenen Anwendungen, die auf dem Host ausgeführt werden (containerisiert oder anderweitig). Der Collector aggregiert und komprimiert möglicherweise die Daten, bevor er sie an ein Speicher-Backend sendet, was ideal klang.

Als Nächstes haben wir die Unterstützung für OpenTelemetry in den verschiedenen Programmiersprachen und Frameworks bewertet, die wir in der Anwendung verwenden. Hier wurde es etwas kniffliger. Obwohl nur die beiden in der Tabelle aufgeführten Programmiersprachen und die zugehörigen Frameworks verwendet wurden, war der Komplexitätsgrad überraschend hoch.

Sprache Rahmen Anzahl der Dienste
Java Spring Boot 3
Python Flasche 3

Um eine Ablaufverfolgung auf Sprachebene hinzuzufügen, haben wir zuerst die automatischen Instrumentierungsagenten von OpenTelemetry ausprobiert, fanden ihre Ausgabe jedoch überladen und verwirrend. Wir sind sicher, dass sich dies mit der Weiterentwicklung der automatischen Instrumentierungsbibliotheken verbessern wird, haben die OpenTelemetry-Agenten vorerst jedoch ausgeschlossen und uns dafür entschieden, die Ablaufverfolgung in unseren Code zu integrieren.

Bevor wir uns an die direkte Implementierung der Ablaufverfolgung im Code machten, haben wir zunächst den OpenTelemetry Collector so verdrahtet, dass alle Ablaufverfolgungsdaten an eine lokal ausgeführte Jaeger-Instanz ausgegeben wurden, wo wir die Ausgabe einfacher sehen konnten. Dies war sehr nützlich, da wir mit der visuellen Darstellung der Tracing-Daten spielen konnten, während wir herausfanden, wie wir OpenTelemetry vollständig integrieren konnten. Wenn wir beispielsweise feststellen, dass eine HTTP-Clientbibliothek beim Aufrufen eines abhängigen Dienstes keine Ablaufverfolgungsdaten enthält, setzen wir das Problem sofort auf unsere Fixliste. Jaeger präsentiert eine schöne Übersicht über alle unterschiedlichen Spannen innerhalb einer einzigen Spur:

Verteiltes Tracing für Python

Das Hinzufügen von Traces zu unserem Python-Code war relativ unkompliziert. Wir haben zwei Python-Quelldateien hinzugefügt, auf die von allen unseren Diensten verwiesen wird, und die entsprechenden requirements.txt- Dateien aktualisiert, um die relevanten opentelemetry-instrumentation-*- Abhängigkeiten einzuschließen. Dies bedeutete, dass wir für alle Python-Dienste dieselbe Ablaufverfolgungskonfiguration verwenden und außerdem die Ablaufverfolgungs-ID jeder Anforderung in Protokollnachrichten aufnehmen und die Ablaufverfolgungs-ID in Anforderungen an abhängige Dienste einbetten konnten.

Verteiltes Tracing für Java

Als nächstes haben wir unsere Aufmerksamkeit den Java-Diensten zugewandt. Die direkte Verwendung von OpenTelemetry-Java-Bibliotheken in einem Greenfield-Projekt ist relativ unkompliziert. Sie müssen lediglich die erforderlichen Bibliotheken importieren und die Tracing-API direkt verwenden . Wenn Sie jedoch wie wir Spring verwenden, müssen Sie zusätzliche Entscheidungen treffen.

Spring verfügt bereits über eine verteilte Tracing-API, Spring Cloud Sleuth . Es stellt eine Fassade über der zugrunde liegenden verteilten Ablaufverfolgungsimplementierung bereit, die, wie in der Dokumentation beschrieben, Folgendes tut:

  • Fügt dem Slf4J MDC Trace- und Span-IDs hinzu, sodass Sie alle Protokolle aus einem bestimmten Trace oder Span in einem Protokollaggregator extrahieren können.
  • Instrumentiert allgemeine Eingangs- und Ausgangspunkte von Spring-Anwendungen (Servlet-Filter, Rest-Vorlage, geplante Aktionen, Nachrichtenkanäle, Feign-Client).
  • Wenn spring-cloud-sleuth-zipkin verfügbar ist, … [generiert und meldet] Zipkin-kompatible Traces über HTTP. Standardmäßig werden sie an einen Zipkin-Sammlerdienst auf dem lokalen Host (Port 9411) gesendet. Konfigurieren Sie den Speicherort des Dienstes mit spring.zipkin.baseUrl .

Die API ermöglicht es uns auch, Traces zu mit @Scheduled kommentierten Aufgaben hinzuzufügen.

Mit anderen Worten: Wenn wir nur Spring Cloud Sleuth verwenden, erhalten wir sofort Traces auf der Ebene der HTTP-Service-Endpunkte, was ein schöner Vorteil ist. Da unser Projekt bereits Spring verwendet, haben wir beschlossen, alles in diesem Framework zu belassen und die bereitgestellten Funktionen zu nutzen. Beim Zusammenbauen der einzelnen Komponenten mit Maven sind uns jedoch einige Probleme aufgefallen:

  • Das Spring Cloud Sleuth Autoconfigure-Modul befindet sich noch in einer Meilensteinversion.
  • Das Spring Cloud Sleuth Autoconfigure-Modul hängt von einer veralteten Alphaversion der Opentelemetry-instrumentation-API ab. Derzeit gibt es keine aktuelle Nicht-Alpha- 1.x -Version dieser Bibliothek.
  • Das Projekt muss das übergeordnete Projektobjektmodell (POM) „spring-cloud-build“ aus den Spring Snapshot-Repositorys abrufen, da Spring Cloud Sleuth seine Abhängigkeitsreferenzen für Meilensteinversionen auf eine bestimmte Art und Weise codiert.

Dies machte unsere Maven-Projektdefinition etwas komplizierter, da wir Maven sowohl aus den Spring-Repositorys als auch aus Maven Central ziehen mussten, ein klares Indiz dafür, in welchem frühen Stadium sich die OpenTelemetry-Unterstützung in Spring Cloud befand. Wir machten dennoch weiter und erstellten ein allgemeines Telemetriemodul zur Konfiguration der verteilten Ablaufverfolgung mit Spring Cloud Sleuth und OpenTelemetry, komplett mit verschiedenen telemetriebezogenen Hilfsfunktionen und Erweiterungen.

Im allgemeinen Telemetriemodul erweitern wir die von den Bibliotheken Spring Cloud Sleuth und OpenTelemetry bereitgestellte Tracing-Funktionalität um Folgendes:

  • Spring-fähige Autokonfigurationsklassen , die die Ablaufverfolgung und erweiterte Funktionalität für das Projekt einrichten und zusätzliche Ablaufverfolgungsressourcenattribute laden.
  • NoOp-Schnittstellenimplementierungen , sodass wir die Ablaufverfolgung beim Start deaktivieren können, indem wir NoOp- Instanzen in alle Spring Cloud Sleuth-Schnittstellen einfügen.
  • Ein Trace-Benennungs-Interceptor zum Normalisieren von Trace-Namen.
  • Ein Fehlerhandler , der Fehler über slf4j und Spring Cloud Sleuth Tracing ausgibt.
  • Eine erweiterte Implementierung von Ablaufverfolgungsattributen , die zusätzliche Informationen in jede ausgegebene Ablaufverfolgung codiert, einschließlich Dienstversion, Dienstinstanz-ID, Maschinen-ID, Pod-Name, Container-ID, Containername und Namespace-Name.
  • Ein Tracing-Anweisungsinspektor , der Trace-IDs in Kommentare einfügt, die den von Hibernate ausgegebenen SQL-Anweisungen vorangehen. Anscheinend kann dies jetzt mit SQLCommenter durchgeführt werden, wir sind jedoch noch nicht darauf umgestiegen.

Darüber hinaus haben wir einen Spring-kompatiblen HTTP-Client implementiert, der vom Apache-HTTP-Client unterstützt wird, da wir bei HTTP-Aufrufen zwischen Diensten mehr Messgrößen und Anpassbarkeit wünschten. Bei dieser Implementierung werden die Trace- und Span-Bezeichner beim Aufruf abhängiger Dienste als Header übergeben, sodass sie in die Ablaufverfolgungsausgabe einbezogen werden können. Darüber hinaus bietet diese Implementierung Metriken für den HTTP-Verbindungspool, die von OpenTelemetry aggregiert werden.

Alles in allem war es ein mühsamer Prozess, das Tracing mit Spring Cloud Sleuth und OpenTelemetry zu vernetzen, aber wir glauben, dass es sich gelohnt hat. Wir hoffen, dass dieses Projekt und dieser Beitrag anderen, die diesen Weg einschlagen möchten, den Weg erhellen.

Verteiltes Tracing für NGINX

Um Traces zu erhalten, die alle Dienste über den gesamten Lebenszyklus einer Anfrage hinweg verbinden, mussten wir OpenTelemetry in NGINX integrieren. Zu diesem Zweck haben wir das OpenTelemetry NGINX-Modul (noch in der Beta-Phase) verwendet. Da wir davon ausgingen, dass es möglicherweise schwierig sein könnte, funktionierende Binärdateien des Moduls zu erhalten, die mit allen Versionen von NGINX kompatibel sind, haben wir ein GitHub-Repository mit Container-Images erstellt, die nicht unterstützte NGINX-Module enthalten. Wir führen nächtliche Builds aus und verteilen die Modul-Binärdatei über Docker-Images, aus denen sich leicht importieren lässt.

Wir haben diesen Prozess noch nicht in den Build-Prozess für NGINX Ingress Controller im MARA-Projekt integriert, planen aber, dies bald zu tun.

Implementieren der Metrikerfassung

Nachdem wir die OpenTelemetry-Integration des Tracings abgeschlossen hatten, konzentrierten wir uns als Nächstes auf Metriken. Für unsere Python-basierten Anwendungen gab es keine vorhandenen Metriken und wir haben beschlossen, deren Hinzufügen vorerst aufzuschieben. Für die Java-Anwendungen unterstützt der ursprüngliche Quellcode der Bank of Anthos , der Micrometer in Verbindung mit Stackdriver von Google Cloud verwendet, Metriken. Allerdings haben wir diesen Code aus der Bank of Sirius entfernt, nachdem wir die Bank of Anthos geforkt hatten, da wir damit kein Metrik-Backend konfigurieren konnten. Dennoch wurde durch die Tatsache, dass bereits Metrik-Hooks vorhanden waren, die Notwendigkeit einer ordnungsgemäßen Metrikintegration deutlich.

Um eine konfigurierbare und pragmatische Lösung zu finden, haben wir uns zunächst die Metrikunterstützung in den OpenTelemetry Java-Bibliotheken und in Micrometer angesehen. Bei der Suche nach Vergleichen zwischen den Technologien wurden in einer Reihe von Ergebnissen die Mängel von OpenTelemetry bei der Verwendung als Metrik-API in der JVM aufgelistet , obwohl sich OpenTelemetry Metrics zum Zeitpunkt des Schreibens noch in der Alpha-Phase befindet. Micrometer ist eine ausgereifte Metrik-Fassadenschicht für die JVM, die ähnlich wie slf4j einen gemeinsamen API-Wrapper bietet, der einer konfigurierbaren Metrikimplementierung statt einer eigenen Metrikimplementierung vorangestellt ist. Interessanterweise ist es die Standard-Metrik-API für Spring.

Zu diesem Zeitpunkt haben wir die folgenden Fakten abgewogen:

  • Der OpenTelemetry Collector kann Metriken aus nahezu jeder Quelle nutzen, einschließlich Prometheus , StatsD und dem nativen OpenTelemetry Protocol (OTLP).
  • Micrometer unterstützt auch eine große Anzahl von Metrik-Backends, darunter Prometheus und StatsD
  • Das OpenTelemetry Metrics SDK für die JVM unterstützt das Senden von Metriken über OTLP

Nach einigen Experimenten kamen wir zu dem Schluss, dass der pragmatischste Ansatz darin besteht, die Micrometer-Fassade mit einer Prometheus-basierten Implementierung zu verwenden und den OpenTelemetry Collector so zu konfigurieren, dass er die Prometheus-API verwendet, um Metriken aus der Anwendung abzurufen. Aus zahlreichen Artikeln wussten wir, dass fehlende Metriktypen in OpenTelemetry Probleme verursachen können, für unsere Anwendungsfälle sind diese Typen jedoch nicht erforderlich, sodass der Kompromiss akzeptabel war.

Wir haben eine interessante Sache über den OpenTelemetry Collector entdeckt: Obwohl wir ihn so konfiguriert hatten, dass er Traces über OTLP und Metriken über die Prometheus-API empfängt, kann er dennoch so konfiguriert werden, dass er beide Datentypen über OTLP oder ein anderes unterstütztes Protokoll an einen externen Datenempfänger sendet. Dadurch konnten wir unsere Anwendung problemlos mit LightStep testen.

Insgesamt war das Codieren von Metriken in Java ziemlich einfach, da wir sie so geschrieben haben, dass sie der Micrometer-API entsprechen, für die zahlreiche Beispiele und Tutorials verfügbar sind. Das Schwierigste sowohl für die Metriken als auch für die Ablaufverfolgung war wahrscheinlich, das Maven-Abhängigkeitsdiagramm in der Datei pom.xml für telemetry-common genau richtig zu gestalten.

Implementieren der Fehleraggregation

Die Fehleraggregation ist per se nicht Gegenstand des OpenTelemetry-Projekts und es bietet keine so elegante Implementierung der Fehlermarkierung wie Lösungen wie Sentry oder Honeybadger.io . Dennoch haben wir uns entschieden, OpenTelemetry kurzfristig zur Fehleraggregation zu verwenden, anstatt ein weiteres Tool hinzuzufügen. Mit einem Tool wie Jaeger können wir nach error=true suchen, um alle Spuren mit einem Fehlerzustand zu finden. Dies gibt uns zumindest eine Vorstellung davon, was häufig schief läuft. In der Zukunft werden wir möglicherweise die Hinzufügung einer Sentry- Integration in Betracht ziehen.

Implementierung von Integritätsprüfungen und Laufzeitintrospektion

Im Kontext unserer Anwendung teilen Integritätsprüfungen Kubernetes mit, ob ein Dienst fehlerfrei ist oder seine Startphase abgeschlossen hat. In Fällen, in denen der Dienst nicht fehlerfrei ist, kann Kubernetes so konfiguriert werden, dass Instanzen beendet oder neu gestartet werden. Wir haben uns entschieden, in unserer Anwendung keine OpenTelemetry-Integritätschecks zu verwenden, da wir die Dokumentation für unzureichend hielten.

Stattdessen verwenden wir für die JVM-Dienste ein Spring-Projekt namens Spring Boot Actuator , das nicht nur Health-Check-Endpunkte, sondern auch Runtime-Introspection- und Heap-Dump-Endpunkte bereitstellt. Für die Python-Dienste verwenden wir das Modul Flask Management Endpoints , das eine Teilmenge der Funktionen von Spring Boot Actuator bereitstellt. Derzeit bietet es nur anpassbare Anwendungsinformationen und Integritätsprüfungen.

Spring Boot Actuator greift auf die JVM und Spring zu, um Endpunkte für Introspektion, Überwachung und Integritätsprüfung bereitzustellen. Darüber hinaus bietet es einen Rahmen zum Hinzufügen benutzerdefinierter Informationen zu den Standarddaten, die es an seinen Endpunkten präsentiert. Endpunkte ermöglichen Laufzeiteinblicke in Aspekte wie Cachestatus, Laufzeitumgebung, Datenbankmigrationen, Integritätsprüfungen, anpassbare Anwendungsinformationen, Metriken, periodische Jobs, HTTP-Sitzungsstatus und Thread-Dumps.

Von Spring Boot Actuator implementierte Integritätsprüfungsendpunkte verfügen über eine modulare Konfiguration, sodass die Integrität eines Dienstes aus mehreren einzelnen Prüfungen bestehen kann, die als Aktivität oder Bereitschaft kategorisiert werden. Außerdem ist ein vollständiger Integritätscheck verfügbar, der alle Prüfmodule anzeigt und normalerweise so aussieht.

Die Informationsendpunkte werden in einem JSON-Dokument als einzelnes JSON-Objekt hoher Ebene und als Reihe hierarchischer Schlüssel und Werte definiert. Normalerweise gibt das Dokument den Dienstnamen und die Version, die Architektur, den Hostnamen, Betriebssysteminformationen, die Prozess-ID, den ausführbaren Namen und Details zum Host an, wie beispielsweise eine Maschinen-ID oder eine eindeutige Dienst-ID.

Implementieren von Heap- und Core-Dumps

Sie erinnern sich vielleicht an die Tabelle im Abschnitt „Vergleich der Toolfunktionen mit der Wunschliste“, in der steht , dass keines der Tools Runtime Introspection oder Heap/Core Dumps unterstützt. Spring als unser zugrunde liegendes Framework unterstützt jedoch beides – es erforderte jedoch einige Arbeit, die Beobachtungsfunktionen in die Anwendung zu integrieren. Wie im vorherigen Abschnitt beschrieben, verwenden wir zur Laufzeit-Introspektion ein Python-Modul in Verbindung mit Spring Boot Actuator.

Ebenso verwenden wir für Heap-Dumps die von Spring Boot Actuator bereitgestellten Thread-Dump-Endpunkte, um eine Teilmenge der gewünschten Funktionen zu erreichen. Wir können weder Core Dumps auf Anfrage noch Heap Dumps der JVM mit der ideal feinen Granularitätsstufe erhalten, aber wir bekommen mit wenig zusätzlichem Aufwand einige der gewünschten Funktionen. Core Dumps für die Python-Dienste erfordern leider eine Menge zusätzlicher Arbeit, die wir auf einen späteren Zeitpunkt verschoben haben.

Zusammenfassung

Nach langem Weinen und Hinterfragen unserer Lebensentscheidungen haben wir uns darauf geeinigt, die folgenden Technologien zur Beobachtung in der MARA zu verwenden (im Folgenden steht OTel für OpenTelemetry):

  • Protokollierung (für alle Container) – Filebeat → Elasticsearch / Kibana
  • Verteiltes Tracing
    • Java – Spring Cloud Sleuth → Spring Cloud Sleuth-Exporteur für OTel → OTel Collector → steckbare Exporteure wie Jaeger, Lightstep, Splunk usw.
    • Python – OTel Python-Bibliotheken → OTel Collector → Pluggable Store
    • NGINX und NGINX Plus (NGINX Ingress Controller wird noch nicht unterstützt) – NGINX OTel-Modul → OTel Collector → steckbarer Exporter
  • Metrikensammlung
    • Java – Mikrometer über Spring → Prometheus-Exporteur → OTel Collector
    • Python – Noch keine Implementierung
    • Python WSGI – GUnicorn StatsD → Prometheus (über StatsD / ServiceMonitor)
    • NGINX – Prometheus-Endpunkt → Prometheus (über ServiceMonitor)
  • Fehleraggregation – OTel verteilte Traces → steckbarer Exporter → Suchfunktion des Exporters zum Auffinden von Traces, die mit Fehlern markiert sind
  • Gesundheitschecks
    • Java – Spring Boot Actuator → Kubernetes
    • Python – Flask Management Endpoints-Modul → Kubernetes
  • Laufzeit-Introspektion
    • Java – Spring Boot-Aktuator
    • Python – Flask Management Endpoints-Modul
  • Heap-/Core-Dumps
    • Java – Spring Boot Actuator-Unterstützung für Thread-Dumps
    • Python – Noch keine Unterstützung

Diese Implementierung ist eine Momentaufnahme. Es wird sich im Laufe der Entwicklung definitiv ändern und weiterentwickeln. Bald werden wir die Anwendung umfangreichen Belastungstests unterziehen. Wir erwarten, viel über die Mängel unseres Observability-Ansatzes zu lernen und zusätzliche Observability-Funktionen hinzuzufügen.

Bitte probieren Sie die Modern Apps Reference Architecture und die Beispielanwendung (Bank of Sirius) aus. Wenn Sie Ideen haben, wie wir uns verbessern können, freuen wir uns über Ihre Beiträge in unserem GitHub-Repo !

Verwandte Artikel

Dieser Beitrag ist Teil einer Serie. Wenn wir MARA im Laufe der Zeit um neue Funktionen erweitern, veröffentlichen wir die Einzelheiten im Blog:


„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."