Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Beim Übergang von einer monolithischen Anwendung zu einer Microservices-Architektur stoßen wir auf neue Herausforderungen.

In einer monolithischen Anwendung ist es meist einfach zu bestimmen, wo im System ein Fehler aufgetreten ist. Wahrscheinlich liegt das Problem im Code des Monolithen oder in der Datenbank. Doch wenn wir ein Problem in einer Microservices-Architektur suchen, ist das nicht mehr so offensichtlich. Wir müssen den gesamten Weg finden, den die Anfrage von Anfang bis Ende zurückgelegt hat, und diesen aus Hunderten von Microservices herausfiltern. Viele von ihnen verfügen zudem über eigene Datenbanken, in denen sowohl logische Fehler als auch Leistungs- und Ausfallsicherheitsprobleme auftreten können.

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Lange habe ich nach einem Tool gesucht, das bei solchen Problemen hilft (ich habe darüber auf Habré geschrieben: 1, 2), aber letztendlich habe ich eine eigene Open-Source-Lösung entwickelt. In diesem Artikel beschreibe ich die Vorteile des Service-Mesh-Ansatzes und teile ein neues Tool zur Umsetzung.

Distributed Tracing ist eine gängige Lösung zur Fehlersuche in verteilten Systemen. Aber was, wenn in einem System ein solcher Ansatz zur Erfassung von Informationen über Netzwerkinteraktionen noch nicht implementiert ist oder, noch schlimmer, dieser Ansatz in einem Teil des Systems bereits funktioniert, während er in einem anderen nicht implementiert wurde? Um die genaue Wurzel des Problems zu bestimmen, ist es notwendig, ein vollständiges Bild dessen zu haben, was im System geschieht. Besonders wichtig ist es zu verstehen, welche Mikrodienste an den wichtigsten geschäftskritischen Prozessen beteiligt sind.

Hier kann uns der Ansatz des Service Mesh behilflich sein, der sich um die gesamte Mechanik der Netzwerkinformationserfassung kümmert, auf einer Ebene, die unterhalb der Funktion der Dienste arbeitet. Dieser Ansatz ermöglicht es uns, den gesamten Datenverkehr abzufangen und in Echtzeit zu analysieren. Die Anwendungen müssen dabei nicht einmal etwas darüber wissen.

Service Mesh Ansatz

Die Hauptidee des Service-Mesh-Ansatzes besteht darin, eine zusätzliche Infrastruktur-Ebene über dem Netzwerk hinzuzufügen, die es uns ermöglicht, sämtliche Aspekte der Interaktion zwischen Diensten zu verwalten. Die meisten Implementierungen funktionieren folgendermaßen: An jeden Mikrodienst wird ein zusätzlicher Sidecar-Container mit einem transparenten Proxy angefügt, über den der gesamte eingehende und ausgehende Datenverkehr des Dienstes geleitet wird. An genau dieser Stelle können wir Client-Balancing durchführen, Sicherheitsrichtlinien anwenden, Anfragen limitieren und wichtige Informationen über die Interaktion der Dienste in der Produktionsumgebung sammeln.

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Lösungen

Es gibt bereits mehrere Implementierungen dieses Ansatzes: Istio und linkerd2. Sie bieten eine Vielzahl von Funktionen direkt nach der Installation an. Gleichzeitig bringt dies jedoch einen hohen Overhead an Ressourcen mit sich. Je größer der Cluster, in dem ein solches System läuft, desto mehr Ressourcen sind erforderlich, um die neue Infrastruktur zu unterstützen. Bei Avito nutzen wir Kubernetes-Cluster, in denen sich Tausende von Serviceinstanzen befinden (und ihre Zahl wächst weiterhin schnell). In der aktuellen Implementierung benötigt Istio etwa 300 MB RAM pro Serviceinstanz. Aufgrund der umfangreichen Funktionen hat auch die transparente Lastverteilung Einfluss auf die Gesamtantwortzeit der Dienste (bis zu 10 ms).

Schließlich haben wir uns angesehen, welche Funktionen wir im Moment wirklich benötigen, und entschieden, dass der Hauptgrund für die Implementierung solcher Lösungen die Möglichkeit war, Tracing-Informationen transparent aus dem gesamten System zu sammeln. Außerdem wollten wir Kontrolle über die Interaktionen zwischen den Diensten haben und verschiedene Manipulationen mit den Headern durchführen, die zwischen den Diensten übertragen werden.

Schließlich sind wir zu unserer Lösung gekommen:  Netramesh.

Netramesh

Netramesh — ist eine leichtgewichtige Service-Mesh-Lösung, die unbegrenzte Skalierbarkeit bietet, unabhängig von der Anzahl der Dienste im System.

Die Hauptziele der neuen Lösung waren ein geringer Ressourcenoverhead und hohe Leistung. Zu den grundlegenden Funktionen wollten wir sofort die Möglichkeit haben, Tracing-Spans transparent an unser Jaeger-System zu senden.

Heute werden die meisten Cloud-Lösungen in Golang umgesetzt. Und das hat natürlich seine Gründe. Es ist bequem und relativ einfach, mit Golang Netzwerkanwendungen zu schreiben, die asynchron mit Ein- und Ausgaben arbeiten und sich bei Bedarf auf die Kerne skalieren. Zudem ist die Leistung für diese Aufgabe ausreichend. Daher haben auch wir uns für Golang entschieden.

Leistung

Wir haben unsere Bemühungen darauf konzentriert, maximale Leistung zu erreichen. Für eine Lösung, die neben jedem Instanz eines Dienstes bereitgestellt wird, sind ein geringer Verbrauch von Arbeitsspeicher und CPU-Zeit erforderlich. Und natürlich sollte auch die Antwortverzögerung so gering wie möglich sein.

Lassen Sie uns schauen, welche Ergebnisse erzielt wurden.

RAM

Netramesh benötigt ~10 Mb ohne Traffic und maximal 50 Mb bei einer Belastung von bis zu 10.000 RPS pro Instanz.

Der Istio Envoy-Proxy benötigt immer ~300 Mb in unseren Clustern mit Tausenden von Instanzen. Das macht eine Skalierung auf gesamten Cluster unmöglich.

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Mit Netramesh haben wir den Speicherverbrauch um etwa das Zehnfache reduziert.

CPU

Der CPU-Verbrauch bleibt unter Last relativ konstant. Er hängt von der Anzahl der Anfragen pro Zeiteinheit an das Sidecar ab. Werte bei 3.000 Anfragen pro Sekunde im Peak:

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Ein weiterer wichtiger Punkt: Netramesh ist eine Lösung ohne Control Plane und verbraucht ohne Last keine CPU-Zeit. Mit Istio aktualisieren die Sidecars ständig die Endpunkte der Dienste. Daher sehen wir dieses Bild ohne Last:

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Wir verwenden HTTP/1 für die Interaktion zwischen Diensten. Die Erhöhung der Antwortzeit bei Istio beim Proxying über Envoy betrug bis zu 5-10 ms, was für Dienste, die bereit sind, in Millisekunden zu reagieren, recht viel ist. Mit Netramesh verkürzte sich diese Zeit auf 0,5-2 ms.

Skalierbarkeit

Die geringe Menge an Ressourcen, die jeder Proxy benötigt, ermöglicht es, ihn in der Nähe jedes Dienstes zu platzieren. Netramesh wurde absichtlich ohne einen Control-Plane-Komponenten entwickelt, um die Leichtigkeit jedes Sidecars zu gewährleisten. Oft verbreitet in Service-Mesh-Lösungen der Control Plane Informationen zur Dienstentdeckung in jedes Sidecar. Damit kommen auch Informationen über Timeouts und Lastenausgleichs-Konfigurationen. All dies ermöglicht viele nützliche Dinge, führt jedoch leider zu einer Vergrößerung der Sidecars.

Service Entdeckung

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Netramesh fügt keine zusätzlichen Mechanismen zur Dienstentdeckung hinzu. Der gesamte Verkehr wird transparent über das Netra Sidecar geleitet.

Netramesh unterstützt das HTTP/1-Anwendungsprotokoll. Zur Definition wird eine konfigurierbare Liste von Ports verwendet. In der Regel gibt es in einem System mehrere Ports, über die HTTP-Interaktionen erfolgen. Zum Beispiel verwenden wir 80, 8890 und 8080 für die Interaktion zwischen Diensten und externen Anfragen. In diesem Fall können sie über eine Umgebungsvariable konfiguriert werden. NETRA_HTTP_PORTS.

Wenn Sie Kubernetes als Orchestrator verwenden und dessen Service-Mechanismus für die Interaktion zwischen den Diensten im Cluster nutzen, bleibt der Mechanismus unverändert. Zunächst erhält der Mikroservice die Service-IP-Adresse über kube-dns und öffnet eine neue Verbindung zu dieser. Diese Verbindung wird zuerst mit dem lokalen netra-sidecar hergestellt und alle TCP-Pakete gelangen ursprünglich genau zu netra. Anschließend stellt das netra-sidecar die Verbindung zum ursprünglichen Ziel her. NAT auf die Pod-IP auf dem Knoten bleibt genau wie ohne netra.

Verteiltes Tracing und Kontextweitergabe

Netramesh bietet die Funktionalität, die erforderlich ist, um Tracing-Spans über HTTP-Interaktionen zu senden. Das netra-sidecar analysiert das HTTP-Protokoll, misst die Anfragenlatenzen und extrahiert die erforderlichen Informationen aus den HTTP-Headern. Letztlich erhalten wir alle Traces in einem einheitlichen Jaeger-System. Für eine feine Konfiguration können auch Umgebungsvariablen verwendet werden, die von der offiziellen Bibliothek bereitgestellt werden. jaeger go library.

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Es gibt jedoch ein Problem. Solange die Dienste keinen speziellen Uber-Header generieren und übermitteln, werden wir die verbundenen Tracing-Spans im System nicht sehen. Und genau das ist notwendig, um schnell die Ursachen von Problemen zu identifizieren. Hier bietet Netramesh erneut eine Lösung an. Die Proxys lesen die HTTP-Header und wenn der Uber Trace ID nicht vorhanden ist, generieren sie ihn. Netramesh speichert ebenfalls Informationen über eingehende und ausgehende Anfragen im Sidecar und ergänzt sie mit den notwendigen Headern der ausgehenden Anfragen. Alles, was in den Diensten erforderlich ist, besteht darin, nur einen Header zu übermitteln. X-Request-Id, der über die Umgebungsvariable konfiguriert werden kann NETRA_HTTP_REQUEST_ID_HEADER_NAME. Um die Größe des Kontextes in Netramesh zu steuern, können folgende Umgebungsvariablen gesetzt werden: NETRA_TRACING_CONTEXT_EXPIRATION_MILLISECONDS (die Zeit, in der der Kontext gespeichert wird) und NETRA_TRACING_CONTEXT_CLEANUP_INTERVAL (die Häufigkeit der Kontextbereinigung).

Es ist auch möglich, mehrere Pfade in Ihrem System durch Markierung mit einem speziellen Sitzungsmarker zu kombinieren. Netra ermöglicht die Einstellung von HTTP_HEADER_TAG_MAP Um HTTP-Header in die entsprechenden Tracing-Span-Tags umzuwandeln. Dies kann besonders nützlich für Tests sein. Nach dem Abschluss eines Funktionstests kann man sehen, welcher Teil des Systems betroffen war, indem man nach dem entsprechenden Session-Schlüssel filtert.

Bestimmung der Anfragequelle

Um herauszufinden, woher die Anfrage kam, kann die Funktion zur automatischen Hinzufügung des Quell-Header verwendet werden. Mit der Umgebungsvariablen NETRA_HTTP_X_SOURCE_HEADER_NAME kann der Name des Headers festgelegt werden, der automatisch gesetzt wird. Mit NETRA_HTTP_X_SOURCE_VALUE kann der Wert festgelegt werden, der im X-Source-Header für alle ausgehenden Anfragen gesetzt wird.

Dies ermöglicht die einheitliche Verbreitung dieses nützlichen Headers im gesamten Netzwerk. Anschließend kann dieser in den Diensten verwendet und in Logs sowie Metriken hinzugefügt werden.

Traffic-Routing und die Architektur von Netramesh

Netramesh besteht aus zwei Hauptkomponenten. Die erste, netra-init, legt die Netzwerkregeln zum Abfangen des Verkehrs fest. Sie verwendet iptables-Redirect-Regeln zum Abfangen des gesamten oder eines Teils des Traffics an einem Sidecar, das den zweiten Hauptbestandteil von Netramesh darstellt. Es kann konfiguriert werden, welche Ports für eingehende und ausgehende TCP-Sitzungen abgerufen werden sollen: EINGEHENDE_ABFANG_PORTS, AUSGEHENDE_ABFANG_PORTS.

Das Tool bietet auch eine interessante Möglichkeit — probabilistisches Routing. Wenn Netramesh ausschließlich zur Sammlung von Tracing-Spans verwendet wird, kann im Produktionsumfeld Ressourcen gespart werden, indem probabilistisches Routing über Variablen aktiviert wird. NETRA_EINGEHENDE_WAHRSCHEINLICHKEIT und NETRA_AUSGEHENDE_WAHRSCHEINLICHKEIT (von 0 bis 1). Der Standardwert ist 1 (alle Daten werden abgefangen).

Nach erfolgreichem Abfangen akzeptiert der Netra Sidecar eine neue Verbindung und verwendet SO_ORIGINAL_DST die Socket-Option, um den ursprünglichen Zielpunkt zu ermitteln. Anschließend öffnet Netra eine neue Verbindung zur ursprünglichen IP-Adresse und stellt eine bidirektionale TCP-Kommunikation zwischen den Parteien her, wobei der gesamte durchlaufende Traffic überwacht wird. Wenn der Port als HTTP definiert ist, versucht Netra, ihn zu parsen und zu tracen. Wenn das Parsen von HTTP fehlschlägt, greift Netra auf TCP zurück und leitet die Bytes transparent weiter.

Erstellung eines Abhängigkeitsgraphen

Nach dem Erhalt von umfangreichen Tracing-Informationen in Jaeger möchte man ein vollständiges Interaktionsdiagramm im System erstellen. Doch wenn Ihr System stark belastet ist und im Laufe eines Tages Milliarden von Tracing-Spans anfallen, wird es schwieriger, diese zu aggregieren. Es gibt einen offiziellen Weg dafür: spark-dependencies. Dennoch wird es Stunden dauern, um das vollständige Diagramm zu erstellen und alle Daten aus Jaeger für die letzten 24 Stunden herunterzuladen.

Falls Sie Elasticsearch zur Speicherung von Tracing-Spans verwenden, können Sie ein einfaches Tool in Golang, das in wenigen Minuten dasselbe Diagramm erstellt, indem es die Funktionen und Möglichkeiten von Elasticsearch nutzt.

Netramesh – eine leichtgewichtige Service-Mesh-Lösung

Wie man Netramesh nutzt

Netra kann einfach zu jedem Service hinzugefügt werden, der unter einem beliebigen Orchestrator läuft. Ein Beispiel finden Sie hier.

Derzeit bietet Netra keine Möglichkeit zur automatischen Einführung von Sidecars für Services, aber es gibt Pläne zur Implementierung.

Die Zukunft von Netramesh

Das Hauptziel Netramesh besteht darin, die Ressourcen kosten minimal zu halten und gleichzeitig eine hohe Leistung zu gewährleisten, um grundlegende Funktionen für Observability und Kontrolle des interservice Interactions anzubieten.

In Zukunft wird Netramesh die Unterstützung weiterer Anwendungsprotokolle neben HTTP bieten. Demnächst wird die Möglichkeit des L7-Routings verfügbar sein.

Verwenden Sie Netramesh, wenn Sie auf ähnliche Probleme stoßen, und schreiben Sie uns Ihre Fragen und Vorschläge.

Quelle: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster