Kapitel 1. Dienst Mesh 101
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Linkerd ist das erste Service Mesh - es ist das Projekt, das den Begriff "Service Mesh" geprägt hat. Es wurde 2015 von Buoyant, Inc. ins Leben gerufen, wie wir in Kapitel 2 näher erläutern werden, und hat sich die ganze Zeit darauf konzentriert, die Produktion und den Betrieb wirklich exzellenter Cloud Native Software zu erleichtern.
Aber was genau ist ein Service Mesh? Wir können mit der Definition aus dem CNCF-Glossar beginnen:
In einer Microservices-Welt werden Anwendungen in mehrere kleinere Dienste aufgeteilt, die über ein Netzwerk kommunizieren. Genau wie dein WLAN-Netzwerk sind auch Computernetzwerke von Natur aus unzuverlässig, hackbar und oft langsam. Service Meshes bewältigen diese neuen Herausforderungen, indem sie den Datenverkehr (d.h. die Kommunikation) zwischen den Diensten verwalten und Zuverlässigkeit, Beobachtbarkeit und Sicherheitsfunktionen einheitlich für alle Dienste hinzufügen.
In der Cloud-Native-Welt dreht sich alles um Computing in verschiedenen Maßstäben, von winzigen Clustern auf deinem Laptop für die Entwicklung bis hin zu den riesigen Infrastrukturen, die Google und Amazon bereitstellen. Das funktioniert am besten, wenn die Anwendungen die Microservices-Architektur nutzen, aber die Microservices-Architektur ist von Natur aus anfälliger als eine monolithische Architektur.
Im Grunde geht es bei Service Meshes darum, diese Anfälligkeit vor dem Anwendungsentwickler - und sogar vor der Anwendung selbst - zu verbergen. Dazu werden verschiedene Funktionen, die für die Entwicklung robuster Anwendungen entscheidend sind, von der Anwendung in die Infrastruktur verlagert. So können sich die Anwendungsentwickler auf das konzentrieren, was ihre Anwendungen einzigartig macht, und müssen sich nicht ständig Gedanken darüber machen, wie sie die kritischen Funktionen bereitstellen, die für alle Anwendungen gleich sein sollten.
In diesem Kapitel werfen wir einen kurzen Blick darauf, was Service-Meshes tun, wie sie funktionieren und warum sie wichtig sind. Dabei vermitteln wir dir den Hintergrund, den du für die ausführlicheren Diskussionen über Linkerd im Rest des Buches brauchst.
Grundlegende Mesh-Funktionalität
Die kritischen Funktionen, die von bereitgestellt werden, lassen sich in drei große Kategorien einteilen: Sicherheit, Zuverlässigkeit und Beobachtbarkeit. Bei der Untersuchung dieser drei Kategorien vergleichen wir, wie sie sich in einer typischen monolithischen Anwendung und in einer Microservices-Anwendung auswirken.
Natürlich kann der Begriff "Monolith" verschiedene Dinge bedeuten. Abbildung 1-1 zeigt ein Diagramm der "typischen" monolithischen Anwendung, die wir hier betrachten werden.
Der Monolith ist ein einzelner Prozess innerhalb des Betriebssystems, was bedeutet, dass er alle Schutzmechanismen des Betriebssystems nutzen kann; andere Prozesse können nichts innerhalb des Monolithen sehen und schon gar nichts verändern. Die Kommunikation zwischen den verschiedenen Teilen des Monolithen erfolgt in der Regel über Funktionsaufrufe innerhalb des einzigen Speicherbereichs des Monolithen, sodass auch hier kein anderer Prozess die Möglichkeit hat, diese Kommunikation zu sehen oder zu verändern. Es stimmt, dass ein Bereich des Monolithen den von anderen Teilen genutzten Speicher verändern kann - das ist eine große Quelle für Bugs -, aber dabei handelt es sich in der Regel um Fehler und nicht um Angriffe.
Mehrere Prozesse versus mehrere Maschinen
"Aber warte!", hören wir dich rufen. "Jedes Betriebssystem, das seinen Namen verdient, kann Schutzmechanismen anbieten, die mehr als einen Prozess umfassen! Was ist mit memory-mapped files oder System V shared memory segments? Was ist mit der Loopback-Schnittstelle und den Unix-Domain-Sockets (um es mal etwas zu übertreiben)?"
Du hast Recht: Diese Mechanismen können es mehreren Prozessen ermöglichen, zusammenzuarbeiten und Informationen auszutauschen, ohne dass sie durch das Betriebssystem geschützt werden. Sie müssen jedoch explizit in die Anwendung kodiert werden und funktionieren nur auf einem einzigen Rechner. Ein Teil der Stärke von Cloud-nativen Orchestrierungssystemen wie Kubernetes ist, dass sie Pods auf jeder Maschine in deinem Cluster einplanen können, ohne dass du im Voraus weißt, auf welcher Maschine. Das ist ungeheuer flexibel, bedeutet aber auch, dass Mechanismen, die davon ausgehen, dass sich alles auf einem einzigen Rechner befindet, in der Cloud Native-Welt einfach nicht funktionieren.
Im Gegensatz dazu zeigt Abbildung 1-2 die entsprechende Microservices-Anwendung.
Bei Microservices liegen die Dinge anders. Jeder Microservice ist ein separater Prozess, und Microservices kommunizieren nur über das Netzwerk - die Schutzmechanismen des Betriebssystems funktionieren jedoch nur innerhalbeines Prozesses. Diese Mechanismen reichen in einer Welt, in der alle Informationen, die zwischen Microservices ausgetauscht werden, über das Netzwerk übertragen werden müssen, nicht aus.
Diese Abhängigkeit von der Kommunikation über das unzuverlässige, unsichere Netzwerk wirft bei der Entwicklung von Microservices-Anwendungenviele Fragen auf.
Sicherheit
Beginnen wir mit der Tatsache, dass das Netzwerk von Natur aus unsicher ist. Daraus ergeben sich eine Reihe von möglichen Problemen, von denen einige inAbbildung 1-3 dargestellt sind.
Einige der wichtigsten Sicherheitsprobleme sind Abhören, Manipulation,Identitätsdiebstahl und Übervorteilung:
- Lauschangriff
-
Bösewichte können die Kommunikation zwischen zwei Microservices abfangen und Mitteilungen lesen, die nicht für sie bestimmt sind. Je nachdem, was genau ein Bösewicht erfährt, kann das ein kleines Ärgernis oder eine große Katastrophe sein.
Der typische Schutz gegen Abhören ist die Verschlüsselung, bei der die Daten so verschlüsselt werden, dass nur der vorgesehene Empfänger sie verstehen kann.
- Manipulationen
-
Ein Bösewicht könnte auch in der Lage sein, die Daten während der Übertragung im Netzwerk zu verändern. Im einfachsten Fall würde der Manipulationsangriff die Daten bei der Übertragung einfach beschädigen; im raffiniertesten Fall würde er die Daten so verändern, dass sie für den Angreifer von Vorteil sind.
Es ist äußerst wichtig zu verstehen, dass Verschlüsselung allein keinen Schutz vor Manipulationen bietet! Der richtige Schutz ist die Verwendung von Integritätsprüfungen wie Prüfsummen; alle gut konzipierten Kryptosysteme enthalten Integritätsprüfungen als Teil ihrer Protokolle.
- Identitätsdiebstahl
-
Wenn du Kreditkartendaten an deinen Payment Microservice weitergibst, wie kannst du sicher sein, dass du wirklich mit deinem Payment Microservice sprichst? Wenn ein Bösewicht sich erfolgreich als einer deiner Microservices ausgeben kann, öffnet das die Tür für alle möglichen unangenehmen Möglichkeiten.
Eine starke Authentifizierung ist entscheidend, um vor dieser Art von Angriffen zu schützen. Nur so kannst du sicher sein, dass der Microservice, mit dem du sprichst, auch wirklich der ist, für den du ihn hältst.
- Overreach
-
Die Kehrseite des Identitätsdiebstahls ist, dass ein Bösewicht eine Stelle ausnutzen kann, an der ein Microservice Dinge tun darf, die er einfach nicht tun sollte. Stell dir zum Beispiel vor, dass ein Bösewicht herausfindet, dass der Zahlungsmicroservice gerne Anfragen von einem Microservice annimmt, der eigentlich nur Dinge zum Verkauf anbieten sollte.
Ein sorgfältiges Augenmerk auf die Autorisierung ist hier der Schlüssel. In einer perfekten Welt kann jeder Microservice genau das tun, was er braucht, und nicht mehr (das Prinzip des geringsten Privilegs).
Verlässlichkeit
Zuverlässigkeit in der monolithischen Welt bezieht sich darauf, wie gut der Monolith funktioniert: Wenn die verschiedenen Teile des Monolithen über Funktionsaufrufe kommunizieren, musst du dir in der Regel keine Sorgen machen, dass ein Aufruf verloren geht oder dass eine deiner Funktionen plötzlich nicht mehr reagiert! Wie in Abbildung 1-4 zu sehen ist, ist eine unzuverlässige Kommunikation bei Microservices jedoch die Norm.
Es gibt eine ganze Reihe von Möglichkeiten, wie Microservices unzuverlässig sein können, darunter:
- Anfrage Fehler
-
Manchmal schlagen Anfragen, die über das Netzwerk gestellt werden, fehl. Dafür kann es viele Gründe geben, von einem abgestürzten Microservice bis hin zu einer Überlastung des Netzwerks oder einer Partition. Entweder muss die Anwendung oder die Infrastruktur etwas unternehmen, um die fehlgeschlagene Anfrage zu bearbeiten.
Im einfachsten Fall kann das Netz einfach Wiederholungsversuche für die Anwendung verwalten: Wenn der Aufruf fehlschlägt, weil der aufgerufene Dienst stirbt oder eine Zeitüberschreitung eintritt, wird die Anfrage einfach erneut gesendet. Das funktioniert natürlich nicht immer: Nicht alle Anfragen können sicher wiederholt werden, und nicht jeder Fehler ist vorübergehend. Aber in vielen Fällen kann eine einfache Wiederholungslogik sehr effektiv eingesetzt werden.
- Dienstausfall
-
Ein Sonderfall von Request Failures tritt auf, wenn nicht nur eine einzelne Instanz eines Microservices abstürzt, sondern alle Instanzen. Vielleicht wurde eine fehlerhafte Version bereitgestellt, oder ein ganzer Cluster ist abgestürzt. In diesen Fällen kann das Mesh helfen, indem es zu einem Backup-Cluster oder zu einer bekanntermaßen guten Implementierung des Dienstes fehlschlägt.
Auch dies kann nicht immer ohne die Hilfe der Anwendung geschehen (Failover von zustandsbehafteten Diensten kann z. B. ziemlich komplex sein). Aber Microservices sind oft so konzipiert, dass sie ohne Zustand auskommen, und in diesem Fall kann Mesh Failover eine große Hilfe sein.
- Dienstüberlastung
-
Ein weiterer Sonderfall: Manchmal der Ausfall, weil sich zu viele Anfragen auf denselben Dienst stürzen. In diesen Fällen kann die Kreislaufunterbrechung helfen, einen Kaskadenausfall zu vermeiden: Wenn das Netz einige Anfragen schnell fehlschlägt, bevor abhängige Dienste sich einschalten und weitere Probleme verursachen, kann es helfen, den Schaden zu begrenzen. Das ist eine etwas drastische Technik, aber diese Art von erzwungenem Lastabwurf kann die Gesamtzuverlässigkeit der Anwendung als Ganzes drastisch erhöhen.
Beobachtbarkeit
Es ist schwierig zu sehen, was in einer Computeranwendung vor sich geht : Selbst eine langsame Maschine arbeitet heutzutage auf Zeitskalen, die eine Milliarde Mal schneller sind als die, in der wir Menschen leben! In einem Monolithen wird die Beobachtbarkeit oft durch interne Protokollierung oder Dashboards sichergestellt, die globale Metriken aus vielen verschiedenen Bereichen des Monolithen sammeln. In einer Microservices-Architektur ist das viel weniger möglich, wie wir in Abbildung 1-5sehen - undselbst wenn es möglich wäre, würde es nicht die ganze Geschichte erzählen.
In der Welt der Microservices konzentriert sich die "Beobachtbarkeit" eher auf den Call Graph und die goldenen Metriken:
- Das Anrufdiagramm
-
Wenn du eine Microservices-Anwendung betrachtest, ist es in der Regel das Wichtigste zu wissen, welche Dienste von welchen anderen Diensten aufgerufen werden. Dies ist der Aufrufgraph, der in Abbildung 1-6 dargestellt ist, und ein wichtiger Punkt, den ein Service Mesh leisten kann, ist die Bereitstellung von Metriken darüber, wie viel Datenverkehr über jede Kante des Graphen läuft, wie viel erfolgreich ist, wie viel fehlschlägt, usw.
Der Call Graph ist ein wichtiger Ausgangspunkt, denn Probleme, die der Nutzer von außerhalb des Clusters sieht, können in Wirklichkeit durch Probleme mit einem einzelnen Dienst verursacht werden, der tief im Graphen verborgen ist. Es ist sehr wichtig, den gesamten Graphen im Blick zu haben, um Probleme lösen zu können.
Es ist auch erwähnenswert, dass in bestimmten Situationen bestimmte Pfade durch den Graphen relevant sind, wie in Abbildung 1-7 dargestellt. So können zum Beispiel verschiedene Nutzeranfragen unterschiedliche Pfade im Graphen nutzen, um verschiedene Aspekte der Arbeitslast zu trainieren.
- Die goldenen Metriken
-
Es gibt eine Vielzahl von Metriken, die wir für jeden Microservice erheben könnten. Im Laufe der Zeit haben sich drei von ihnen in einer Vielzahl von Situationen als besonders nützlich erwiesen, so dass wir sie jetzt als die "goldenen Metriken" bezeichnen (wie in Abbildung 1-8 dargestellt):
- Latenz
-
Wie lange dauert es, bis eine Anfrage abgeschlossen ist? Dies wird in der Regel als Zeit angegeben, die ein bestimmter Prozentsatz der Anfragen benötigt, um abgeschlossen zu werden. Zum Beispiel gibt die P95-Latenz die Zeit an, in der 95 % der Anfragen abgeschlossen werden. Du kannst also "5 ms P95" so interpretieren, dass 95 % der Anfragen in 5 ms oder weniger abgeschlossen werden.
- Verkehr
-
Wie viele Anfragen werden von einem bestimmten Dienst bearbeitet? Dies wird normalerweise als Anfragen pro Sekunde (RPS) angegeben.
- Erfolgsquote
-
Wie viele Anfragen sind erfolgreich? (Diese Zahl kann auch als ihr Gegenteil, die Fehlerquote, angegeben werden.) Sie wird in der Regel als Prozentsatz der gesamten Anfragen angegeben, wobei die "Erfolgsquote" oft mit SR abgekürzt wird.
Die ursprünglichen "Goldenen Signale"
Diese wurden ursprünglich in Googles Beitrag "Monitoring Distributed Systems" als die vier "goldenen Signale" beschrieben: Latenz, Anfragerate, Fehlerrate und Sättigung. Wir bevorzugen "goldene Metriken", weil Metriken Dinge sind, die du direkt messen kannst; du leitest Signale (wie "Sättigung") von Metriken ab.
Wir werden in Kapitel 10 ausführlicher darauf eingehen, aber es lohnt sich, an dieser Stelle zu erwähnen, dass sich diese Metriken als so nützlich erwiesen haben, dass viele Meshes einen beträchtlichen Aufwand betreiben, um sie aufzuzeichnen - und dass das Service-Mesh ein idealer Ort ist, um sie zu verfolgen.
Wie funktionieren Meshes eigentlich?
Zum Schluss noch ein kurzer Blick darauf, wie Servicemaschen eigentlich funktionieren.
Im Großen und Ganzen haben alle Meshes die gleiche Aufgabe: Sie fügen sich in den Netzwerkstack des Betriebssystems ein, übernehmen das Low-Level-Networking, das die Anwendung nutzt, und vermitteln alles, was die Anwendung im Netzwerk tut. Nur so kann das Mesh alle Funktionen bereitstellen, für die es entwickelt wurde, ohne dass die Anwendung selbst geändert werden muss.
Die meisten Meshes - einschließlich Linkerd - verwenden das Sidecar-Modell, bei dem ein Proxy-Container neben jedem Anwendungscontainer installiert wird (siehe Abbildung 1-9).1 Sobald der Proxy läuft, konfiguriert er die Netzwerk-Routing-Regeln des Hosts so um, dass der gesamte Verkehr in und aus dem Anwendungscontainer über den Proxy läuft. So kann der Proxy alles kontrollieren, was für die Funktionalität des Meshes notwendig ist.
Es gibt auch andere Modelle, aber das Seitenwagenmodell hat enorme Vorteile in Bezug auf die Einfachheit der Bedienung und die Sicherheit:
-
Aus der Perspektive aller anderen Komponenten des Systems verhält sich der Sidecar so, als wäre er Teil der Anwendung. Das bedeutet insbesondere, dass alle Maßnahmen, die das Betriebssystem zur Gewährleistung der Sicherheit der Anwendung ergreift, auch für den Sidecar gelten. Das ist eine sehr, sehr wichtige Eigenschaft: Die Beschränkung des Sidecars auf genau einen Sicherheitskontext schränkt die Angriffsfläche des Sidecars stark ein und macht es viel einfacher zu entscheiden, ob die Dinge, die der Sidecar tut, sicher sind.
-
Die Verwaltung des Sidecars ist genau dasselbe wie die Verwaltung jeder anderen Anwendung oder jedes anderen Dienstes. Unter
kubectl rollout restart
kannst du zum Beispiel einen Anwendungs-Pod und seinen Sidecar als Einheit neu starten.
Natürlich gibt es auch Nachteile. Der größte ist, dass jeder Anwendungs-Pod einen Sidecar-Container braucht - selbst wenn deine Anwendung Tausende von Pods hat. Ein weiteres häufiges Problem ist die Latenz: Der Sidecar braucht per Definition eine gewisse Zeit, um den Netzwerkverkehr zu verarbeiten. Auch hierauf werden wir später noch eingehen, aber es lohnt sich, schon jetzt zu erwähnen, dass Linkerd sich große Mühe gibt, die Auswirkungen des Sidecars zu minimieren, und in der Praxis ist Linkerd sehr schnell und leichtgewichtig.
Warum brauchen wir das also?
Um es ganz klar zu sagen: Die Funktionen, die das Netz bietet, sind nicht optional. Du wirst nie hören, dass das Entwicklungsteam sagt: "Oh, wir brauchen keine Sicherheit" oder "Oh, Zuverlässigkeit ist nicht wichtig" (obwohl du die Leute vielleicht von der Notwendigkeit der Beobachtbarkeit überzeugen musst - hoffentlich hilft dieses Buch dabei!)
Mit anderen Worten: Es geht nicht darum, ob diese drei Funktionen vorhanden sind oder nicht, sondern darum, ob sie vom Netz bereitgestellt werden oder ob sie in der Anwendung bereitgestellt werden müssen.
Sie in der Anwendung bereitzustellen, ist kostspielig. Deine Entwickler könnten sie von Hand schreiben, aber das bedeutet eine Menge komplizierten Anwendungscode, der in jedem Microservice repliziert wird, was sehr leicht zu Fehlern führen kann (vor allem, weil die Versuchung immer groß sein wird, dass sich die leitenden Entwickler auf die Kronjuwelen der geschäftsspezifischen Logik konzentrieren, anstatt auf die langweilige, weniger sichtbare, aber ebenso kritische Arbeit, die Wiederholungen richtig zu machen). Außerdem kann es zu Inkompatibilitäten zwischen verschiedenen Teilen der Anwendung kommen, vor allem, wenn die Anwendung wächst.
Alternativ kannst du auch Bibliotheken finden, die die Funktionalität für dich implementieren, was definitiv Entwicklungszeit spart. Auf der anderen Seite muss jeder einzelne deiner Entwickler lernen, wie man diese Bibliotheken benutzt, du bist auf die Sprachen und Laufzeiten beschränkt, für die du die Bibliotheken finden kannst, und Inkompatibilitäten sind immer noch ein ernstes Problem (z.B. wenn ein Microservice die Bibliothek aktualisiert, bevor ein anderer es tut).
Im Laufe der Zeit ist uns klar geworden, dass es der beste Weg ist, all diese Funktionen in das Mesh zu integrieren, wo die Anwendungsentwickler nicht einmal wissen müssen, dass es sie gibt - und wir denken, dass Linkerd das beste Mesh ist. Wenn wir dich am Ende des Buches noch nicht überzeugt haben, sag uns bitte, wo wir versagt haben!
Zusammenfassung
Zusammenfassend lässt sich sagen, dass Service Meshes eine Infrastruktur auf Plattformebene sind, die Sicherheit, Zuverlässigkeit und Beobachtbarkeit einheitlich für die gesamte Anwendung bietet, ohne dass Änderungen an der Anwendung selbst erforderlich sind. Linkerd war das erste Service Mesh und wir glauben, dass es immer noch das beste Gleichgewicht zwischen Leistung, Geschwindigkeit und Einfachheit im Betrieb bietet.
1 Der Name kommt von der Analogie, dass man einen Beiwagen an ein Motorrad anschraubt.
Get Linkerd: Auf und davon now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.