Glücklich ohne Service Mesh

Die Basis für sinnvolle Architekturentscheidungen ist die Auseinandersetzung mit dem Problem und ein guter Überblick über die Optionen.

Ich schreibe, spreche und berate zum Thema Service Mesh. In Gesprächen ist es schon häufiger vorgekommen, dass mein Gegenüber mir erzählt, dass es in seinem Projekt ein Service Mesh einführen will. Nachdem ich mich über die Architektur und Projektsituation erkundigt habe, rate ich in den meisten Fällen davon ab. Viele Gesprächspartner*innen sind daraufhin irritiert, fast enttäuscht.

Die Geschichte, die ich mehrmals gehört habe ist etwa so:

Wir haben da aktuell diesen furchtbaren Monolithen. Niemand kann die Folgen von Code-Änderungen absehen, deshalb geht die Entwicklung nicht voran. Das ist sehr frustrierend. Wir haben uns entschieden, die Anwendung neu zu bauen und diesmal alles richtig zu machen: Mit Microservices, Kubernetes und einem Service Mesh!

Die Art und Weise, wie Entscheidungen in der IT zustandekommen, hat mich beschäftigt und darum wird es in diesem Text gehen. Zunächst aber etwas zum Hintergrund.

Warum (synchrone) Microservices so populär sind

Es ist bekannt, dass Microservices die Entwicklung von komplexer Software erleichtern. Sie begegnen dazu nicht nur den technischen Herausforderungen, sondern bieten auch die Voraussetzung, um organisatorische, also menschliche, Herausforderungen zu lösen. Statt beispielsweise 50 Personen, die mit der vollen Komplexität einer Anwendung konfrontiert sind, können kleinere Teams einen Teil eigenständig verantworten und z.B. auch die geeigneten Sprachen und Frameworks wählen und ändern.

Das kann kaum jemand schlecht finden.

Die wichtige Frage ist allerdings nicht, ob Microservices gut oder schlecht sind, sondern ob sie ein vorhandenes Problem tatsächlich lösen und welche unerwünschten Konsequenzen damit einhergehen.

Für das Problem des schwer wartbaren Monolithen meiner Gesprächspartner*innen scheinen Microservices eine vielversprechende Lösung zu sein: Die Folgen von Änderungen sind innerhalb eines Microservice absehbar. Der Einfluss auf andere Microservices ist auf explizite Netzwerk-Schnittstellen minimiert. Anders als bei einem Monolithen kann Code eines anderen Service nur über eine definierte Schnittstelle genutzt werden. Tatsächlich können die Probleme aber auch ganz andere Ursachen haben, die sich nicht durch technische Änderungen beheben lassen. Beispielsweise kann das Vorgehen bei der Softwareentwicklung oder die Zusammenstellung des Teams ungeeignet sein. Häufig wird der Fachlichkeit zu wenig Beachtung geschenkt. Sicherlich ist die Struktur des Monolithen erst mit der Zeit erodiert. Wenn die Ursache dafür unklar ist, wird es den Microservices möglicherweise ähnlich schlecht ergehen.

Microservices können je nach Implementierung unerwünschte Konsequenzen haben, die nicht selten übersehen werden.

Die autonomen Microservices müssen zur Laufzeit integriert werden. Abhängigkeiten zwischen Services sind damit weniger sichtbar als in einem Monolithen. Viele Teams entscheiden sich dafür, Abhängigkeiten mit synchronen Aufrufen umzusetzen. Das ist naheliegend, denn es entspricht einem gewohnten Methodenaufruf am ehesten, der durch die Trennung in Microservices nicht mehr möglich ist. Andere Gründe könnten sein, dass Alternativen nicht bekannt sind oder dass der Aufwand für Schnitt, Implementierung und Automatisierung so hoch ist, dass bei der Integration der Weg des vermeintlich geringsten Widerstands eingeschlagen wird.

Die häufig synchrone Integration von Microservices hat viele negative Effekte. Beispielsweise dauert jeder Netzwerkaufruf länger als ein Methodenaufruf, was bei langen Aufrufketten für die Performance problematisch werden kann. Aufrufer müssen außerdem damit rechnen, dass ihre Anfrage scheitert oder zu lange dauert. Kommunikation innerhalb einer Microservice-Anwendungen muss außerdem durch Verschlüsselung, Authentifizierung und Autorisierung abgesichert werden.

Zurück zum Service Mesh…

Ein Service Mesh ist auf die Lösung von Problemen von Microservice-Architekturen „abgerichtet“. Die aktuellen Implementierungen wie Istio und Linkerd lösen aber vor allem Probleme, die bei synchronen Netzwerkaufrufen auftreten:

  • Verschlüsselung und beidseitige Authentifizierung von Anfragen

  • Erfassen von Metriken zu Netzwerkanfragen (Anzahl, Status Code, Latenz, …)

  • Maßnahmen zur Widerstandsfähigkeit gegen Netzwerk-Fehler (Retry, Timeout und Circuit Breaker)

  • Konfiguration von Routing-Regeln auf Basis von Pfaden und Headern (ermöglicht A/B-Testing und Canary Releasing)

Bezahlt wird dieses verlockende Angebot mit technischer Komplexität, noch etwas mehr Latenz und zusätzlichem Ressourcenverbrauch. Doch selbst wenn das akzeptabel ist, bleibt ein weiteres Problem: Ein Service Mesh muss von Menschen konfiguriert, betrieben, aktualisiert und im Fehlerfall untersucht werden.

Verglichen mit der Implementierung gleichwertiger Funktionen in jedem einzelnen Microservice, scheint das wenig Aufwand. Für das Team kann es trotzdem eine große Herausforderung bedeuten, wenn es zu wenig Kapazität für das Erlernen einer neuen Technologie hat. Ob also mit oder ohne Service Mesh: Microservices fügen Komplexität hinzu. Ein Service Mesh versteckt einen großen Teil dieser Komplexität. Das ist in vielen Fällen ein Vorteil, der schnell zum Nachteil wird - beispielsweise sobald Fehler auftreten oder wenn Anforderungen von der Service Mesh Implementierung nicht unterstützt werden.

Wie Entscheidungen zustande kommen

Wer mit anderen Branchen vergleicht, wird feststellen, dass die IT sehr stark durch die freie Verteilung von Informationen und Wissen geprägt ist - beispielsweise Artikel, Podcasts, Tutorials, Webseiten und Meetups. Außerdem oder genau deshalb entstehen ständig innovative Ideen und Technologien. Viele Entwickler*innen haben eine hohe Bereitschaft zu lernen und eine ausgeprägte berufliche Neugier.

Diese bemerkenswerte Situation hat neben den überwiegenden Vorteilen auch ein paar unbeabsichtigte Nebeneffekte.

Große Unternehmen wie Google, Amazon und Netflix sind starke Treiber von Innovationen und veröffentlichen aufgrund ihrer Größe viele Informationen und Softwareprodukte. Sie bekommen aufgrund ihrer besonders beeindruckenden Herausforderungen viel Aufmerksamkeit und genießen viel Vertrauen. Doch die veröffentlichten Lösungen lösen genau diese besonderen Herausforderungen. Wenn eine vorgestellte Technologie nicht in den richtigen Kontext gesetzt wird, wird sie leicht als Allheilmittel wahrgenommen und für völlig ungeeignete Zwecke eingesetzt. Microservices und Single Page Applications sind nur ein paar Beispiele dafür.

Die Informationen, auf deren Basis Teams Entscheidungen fällen, sind also häufig unvollständig und oft einseitig. Die Neugier gegenüber neuer Technologie macht es aber schwer, einen Hype kritisch zu hinterfragen und weniger intuitive oder weniger hippe Alternativen in Erwägung zu ziehen. Häufig ist es naheliegend, möglichst schnell zur Tat zu schreiten, als sich detailliert mit dem Problem und seinen Ursachen zu beschäftigen.

Bessere Entscheidungen fällen

Es klingt trivial, aber um eine gute Entscheidung zu fällen, müssen Probleme und Ziele klar sein, und die Maßnahmen ausgewählt werden, die dazu passen.

Wichtig ist, dass überhaupt erst mehrere Lösungsansätze diskutiert werden. Auch das klingt banal, ist aber gar nicht so einfach. Denn durch den konstanten Fortschritt ist es kaum möglich, sich mit allem zu beschäftigen, geschweige denn kritisch zu hinterfragen oder praktisch auszuprobieren.

Viele Entwickler*innen sind dankbar für Möglichkeiten, sich in neue Themen einzuarbeiten. Die Herausforderung ist, die vielen verfügbaren Informationen aus Artikeln, Konferenzen etc. einzuordnen. Und selbstverständlich kann ein Artikel über ein Framework oder eine Technologie nicht die praktischen Erfahrungen damit ersetzen. Genau so wichtig wie Experimente zuzulassen, ist also, den Erfahrungsaustausch zu fördern. Wenn Experimente außerhalb der Projekte ermöglicht werden, nimmt auch der Drang ab, neuen Technologien ihrer selbst Willen in Projekte einzuführen.

Organisationen müssen auf diese Herausforderungen reagieren. Bei INNOQ hat sich folgendes bewährt:

  • Workshops, die wir extern anbieten, werden regelmäßig oder bei Bedarf auch intern angeboten

  • Auf regelmäßigen Events trifft sich die gesamte Firma und tauscht sich durch Vorträge, Open Spaces oder gemeinsamer Programmierung aus. Der Organisationsaufwand dafür ist geringer als häufig angenommen.

  • In themenspezifischen Slack-Channels können jederzeit Fragen gestellt oder Informationen geteilt werden

Vieles steht und fällt mit einer Firmenkultur, die auf Vertrauen in ihre Mitarbeiter*innen basiert und kritische Auseinandersetzungen begrüßt.

Zum Abschluss noch einmal zurück zum Problem meines Gesprächspartners.

Alternativen, die ohne Service Mesh auskommen

Synchron kommunizierende Microservices mit Service Mesh ist nur ein Lösungsansatz gegen die Trägheit eines Monolithen. Je nach Kontext gibt es folgende Alternativen:

  • Eventuell kann der existierende Monolith mit einem Refactoring oder einer Modularisierung maßgeblich verbessert werden. Um die Einhaltung von Schnittstellen zu erzwingen, können statt der Kommunikation über das Netzwerk auch Module genutzt werden. In jedem Fall lohnt eine Auseinandersetzung mit der aktuellen Anwendung und ihrer Probleme für das Finden einer besseren Lösung.

  • Vielleicht ist gar nicht die monolithische Architektur „Schuld“, sondern die Wahl des Vorgehensmodells, der Infrastruktur, der Programmiersprache oder des Frameworks. Unter Umständen könnte ein neuer Monolith mit einem passenderen Arbeitsmodus oder einer anderen Technologie viele Probleme lösen. Wenn man eine Suppe versalzen hat, heißt das schließlich auch weder, dass es bei der zweiten wieder passiert, noch dass es bei einem Auflauf besser gelingt.

  • Self-Contained Systems sind mit Microservices verwandt. Sie teilen die Anwendung in eigenständige Komponenten. Integration findet häufig asynchron oder über das Frontend, beispielsweise mit Transklusion, statt.

  • Und auch wenn es Microservices sein sollen, sollte die Art der Integration sorgfältig ausgewählt werden. Je nach Zielen und Anforderungen können Feeds oder Message Queues als Alternativen zu synchronen Aufrufen in Erwägung gezogen werden.

Vielen Dank an Robert Glaser, Christoph Iserlohn, Martin Otten, Joachim Praetorius, Philipp Schirmacher, Hermann Schmidt, Tina Schönborn, Tammo van Lessen, Oliver Wolf, Eberhard Wolff und das anonyme Eichhörnchen für Feedback zu diesem Beitrag.

TAGS

Comments

Please accept our cookie agreement to see full comments functionality. Read more