Microservices – flexible Softwarearchitektur
Do one thing and do it well.
Douglas McIlroy
Netflix, Amazon, Ebay, LinkedIn, YouTube – sie alle tun es. Um Software zu bauen, gibt es unterschiedliche Möglichkeiten. Abhängig von den Rahmenbedingungen und Ansprüchen, ist es in manchen Fällen besser, Software als großes Ganzes, also monolithisch zu konzipieren. In vielen anderen Fällen tut man sich und anderen einen großen Gefallen, wenn die vielen einzelnen Funktionen, die zusammen die gesamte Anwendung ergeben, in kleine Päckchen mit dem Etikett Microservices gepackt werden. Warum Microservices in der Softwareentwicklung eine kleine Revolution auslösten, die sich längst nicht nur auf die Architektur, sondern auch auf die Planung und Zusammenarbeit einzelner Teams ausgewirkt hat, erfährst Du hier.
Was sind Microservices?
Lass uns dafür einen kurzen Blick zurück in die jüngste Computergeschichte werfen: In den 2010er Jahren entwickeln sich einige Technologien, die nämlich zu wahren Gamechangern in der Softwareentwicklungslandschaft wurden: Cloud Computing, Containerisierung (Docker, Kubernetes) und DevOps. Die leichtgewichtigen neuen Programmiersprachen wie Golang, Rust und Swift kommen auf die Software-Welt und gleichzeitig werden hochproduktive und verhältnismäßig einfach zu händelnde Programmiersprachen wie JavaScript und Python zum Mainstream. Auch die Art, wie Software entwickelt wird, veränderte sich. Das gute alte Wasserfallmodell wird durch eine schnellere, iterative Softwareentwicklungs-Methodik ersetzt: das agile Software Development. Auch die Computerhardware hat sich mit dem Aufkommen von preiswerterem und schnellerem Hauptspeicher und Multicore-CPUs und GPUs massiv verändert. Und in der Datenspeicherlandschaft weicht der gemütlich bis schwerfällige relationale Datenbank-Riese aktiven, hoch flexiblen Zwergen. Die neuen Datenbanktechnologien wie NoSQL und NewSQL können im Gegensatz zu relationalen Datenbanken horizontal skaliert werden und ermöglichen damit dank Cloud eine viel größere Flexibilität und Ausfallsicherheit.
Um die Komplexität moderner Softwareanwendungen zu beherrschen, die Vorteile von Cloud Computing, Containerisierung, DevOps und modernen Programmiersprachen zu nutzen und die Anforderungen (schnelle Entwicklung, horizontale Skalierung) zu erfüllen, entstand 2012 ein neuer Softwarearchitekturstil: die Microservice-Architektur.
Eine neue Philosophie der Softwarearchitektur
Microservices verkörpern eine Architektur-Philosophie, die Anwendungen in kleinen, eigenständigen Diensten organisiert. Diese Dienste können unabhängig voneinander entwickelt, deployt und gewartet werden und interagieren untereinander über plattformunabhängige Schnittstellen. Okay und was heißt das genau? Ein einzelner Microservice macht im Prinzip wenig, aber das macht er dafür ziemlich gut. Heute sind Microservice-Architekturen Standard in der Entwicklung von Webanwendungen. Vielleicht kennst Du es selbst vom Programmieren: Du möchtest am liebsten tausend Funktionen in Deiner Anwendung haben. Und dann stehst Du vor der Herausforderung, wie Du sie am besten gestaltest. Der traditionelle, klassische Weg im Software Development ist die monolithische Architektur. Bei der hängen alle Deine Funktion zusammen und Du hast ein Programm, das alles kann. Eigentlich genau das, was Du willst, oder? Aber was passiert, wenn Du Probleme mit der Datenbank hast oder Dein Design zerschossen ist? Bei Monolithen betreffen Ausfälle oft das ganze System. Und wenn das down ist, geht nichts mehr. Wäre doch schlau, die einzelnen Funktionen voneinander zu trennen und separat laufen zu lassen. Im Worst Case wäre dann z.B. in dem Webshop “nur” die Such-Funktion kaputt, die Kund:innen können aber fröhlich weiter shoppen.
Genau deshalb ist der Microservice-Ansatz heute so beliebt und verbreitet. Kaum eine Webanwendung verzichtet auf die kleinen Teile, die zusammen ein großartiges Ganzes ergeben.
Beispiele für Microservices im Software Development
Ein gutes Bespiel für den Einsatz von Microservices (und DevOps) sind Webshops. Hinter den vielen einzelnen Funktionen, wie der Suche, dem Hinzufügen von Artikeln in den Warenkorb, Artikelbeschreibungen, Preisen, Bewertungen, Bonuspunkte, weitere Kaufempfehlungen etc. steckt ein eigener kleiner Service – und je nach Umfang ein eigenes DevOps-Team.
Die einzelnen Microservices kommunizieren über Schnittstellen miteinander. Möchtest Du Dir z.B. Deinen Warenkorb mit Details zu Produkten und Preisen ansehen, werden separate Anfragen gesendet. Die häufigsten Arten sind API-Schnittstellen (Application Programming Interfaces), um Daten zu senden und zu empfangen, sowie Event-Driven-Schnittstellen. Die werden verwendet, um Ereignisse zu melden und auf diese zu reagieren. Damit die Kommunikation problemlos läuft, müssen die Schnittstellen standardisiert und ebenfalls skalierbar sein für wachsende Anforderungen. Dabei kann eine Middleware wie eine API-Gateway oder eine Nachrichtenwarteschlange sinnvoll sein und die Kommunikation zwischen Microservices optimieren. Sie vermitteln zwischen den einzelnen Services und vereinfachen die Verwaltung von Anforderungen und Antworten. Schnell kann nämlich eine synchrone Kommunikation zu Latenzen führen. Stell Dir vor, Dienst A ruft Dienst B auf, Dienst B ruft Dienst C und Dienst C wiederum Dienst D synchron auf, passiert da eine Menge und schwupps, kann es zu Wartezeiten und Fehlerkaskaden kommen.
Welche Art der Kommunikation am besten geeignet ist, hängt von den Anforderungen der Anwendung ab.
- Synchrone Kommunikation bedeutet, dass ein Microservice auf eine Antwort von einem anderen Microservice wartet, bevor er weiterarbeitet. Dies ist einfach zu implementieren und bietet eine gute Kontrolle über den Datenfluss, kann aber bei langsamen Antworten oder Fehlern zu Verzögerungen führen.
- Asynchrone Kommunikation bedeutet, dass Microservices unabhängig voneinander arbeiten und nicht auf eine Antwort warten. Dies ist effizienter, da es keine Verzögerungen durch langsame Antworten oder Fehler gibt, aber es kann schwieriger zu verwalten sein, da es mehr Kontrolle über den Datenfluss erfordert.
In der Praxis können beide Ansätze, abhängig von den Anforderungen der Anwendung, kombiniert werden. Dabei wägen Softwarearchitekt:innen Vor- und Nachteile beider Ansätze ab und wählen die beste Lösung für die spezifischen Anforderungen einer Anwendung.
Welche Vorteile haben Microservices?
Ein paar überzeigende Argumente für die modulare Anwendungsentwicklung sind schon angeklungen. Heute ist Software komplex und die Komplexität wird eher zu- als abnehmen. Das hat uns unsere kleine Rückschau gezeigt. Es ist aber nicht nur die Softwareentwicklung selbst, die sich in den letzten 10 Jahren erheblich verändert hat, sondern mit ihr das gesamte Ökosystem. Wir entwickeln heute Anwendungen nach der agilen Methodik, die Programme werden auf Containern und Cloud mit CI/CD (Continuous Integration, Continuous Delivery und Continuous Deployment) bereitgestellt, auf NoSQL-Datenbanken persistiert, auf einem modernen Browser oder Smartphone gerendert und die Maschinen sind über ein Hochgeschwindigkeitsnetzwerk verbunden.
Die Idee hinter Microservices, ist es also, das komplexe Ganze in kleinere und überschaubarere Teile zu verteilen, oder aus vielen kleinen Modulen ein komplexes Ganzes zu erschaffen. Einzelne Funktion werden mit eigener Code-Basis und Tech Stack ausgelagert und laufen vollkommen unabhängig voneinander.
- Skalierbar: Jeder einzelne Service lässt sich nach Bedarf skalieren. Im Gegensatz zu einem Monolithen spart diese feine Granulierung jede Menge Ressourcen.
- Resilient: Fällt ein Microservice aus, hat das keine Auswirkungen auf andere Funktionalitäten – vorausgesetzt, es wurde sauber gearbeitet. Hat sich doch ein Fehler eingeschlichen, ist die Codemenge der einzelnen Services relativ überschaubar und Bugs lassen sich schneller finden und fixen.
- Unabhängig: Die einzelnen Teams arbeiten autark und können sich so voll auf ihre Kernaufgabe, den einen Service konzentrieren. Die einzelnen Teams können unterschiedliche Programmiersprachen nutzen und eigene Datenbanken verwenden, da jeder Microservice seine eigene Laufzeitumgebung hat.
- Kompatibel: Dank sprachenunabhängiger APIs können die agilen Teams die am besten geeignete Sprache und die Technologien mit den notwendigen Funktionen wählen. Trotz komplett unterschiedlicher Struktur kommunizieren die einzelnen Module über die gemeinsamen Anknüpfungspunkte – meistens REST-APIs.
- Wartbar: Einzelne, überschaubare Komponenten sind für die Developer leichter zu verstehen. Damit ist nicht nur Debugging einfacher. Die Funktion kann stetig aktualisiert und verbessert werden. Jeder Service kann unabhängig von anderen deployt werden.
Du kannst mit der modularen Aufteilung in einzelnen Services mit Deinen Dev-Kolleg:innen autark an Eurer jeweiligen Baustelle arbeiten und ihr seid nicht abhängig von einer zentralen Datenbank oder der Person, die für UI-Design zuständig ist. Entwicklungsteams arbeiten wesentlich schneller und effizienter, da sie sich auf einen kleinen Teil der Anwendung konzentrieren können. Damit verkürzt sich die gesamte Entwicklungsdauer von Software und damit auch die Time-to-market. Durch die modulare Aufteilung können beliebig viele Teams an beliebig vielen Microservices arbeiten – perfekt für eine agile Arbeitsweise, bei der nicht mehr als 7 Menschen in einem Team arbeiten sollten.
Diese Herausforderungen bringen Microservices mit sich
Wo es so viel Licht gibt, fallen natürlich auch Schatten. Während monolithische Architektur genau eine Lösung für alle Probleme liefert, gibt es bei Microservices sehr viele Lösungen.
Quelle: Medium.com
Eine der Herausforderung besteht also darin, die passenden Lösungsbausteine zu finden.
Komplexität: Das Zerlegen einer Anwendung in viele unabhängige Dienste macht die Systemarchitektur komplexer. Je größer das Gesamtsystem und je mehr einzelne Services, desto komplexer das Logging und Monitoring. Die verteilten Services erzeugen unter Umständen Netzwerk-Latenzen und Fehlertoleranz.
Kommunikation: Je nachdem, wie die modularisierten Anwendungen untereinander kommunizieren, entstehen Latenzen. Synchrone Kommunikation kann im Extremfall zu verteilten Monolithen führen. Asynchrone Kommunikation bietet da mehr Flexibilität, aber zum Preis eines höheren Implementierungsaufwands.
Debugging: Fehlerbehandlung und Überwachung werden komplexer – besonders bei einer großen Anzahl von unabhängigen Diensten. Debugging mit lokaler IDE (Integrated Development Environment) ist keine Option.
Datenaustausch und Datenkonsistenz: Im Idealfall verfügt jeder Service über seine eigene Datenbank. Dadurch ist er autark und Daten sind vor Hacker:innen sicherer, weil sie verteilt sind. Bei diesem dezentrale Datenmanagement ist es wichtig, dass die Integrität der Daten sichergestellt wird, um fehlerfreie Ergebnisse zu erzielen.
IT-Security: Einerseits lassen sich die jeweils einzelnen Services besser schützen, aber je nach Größe wird es zur Herausforderung hunderte Services vor Cyber-Kriminellen zu schützen.
Integrationsaufwand: Die Integration der einzelnen Dienste kann Zeit und Ressourcen erfordern.
Neben diesen technischen Aspekten bringt die Softwareentwicklung nach dem modularen Ansatz für Unternehmen oft auch organisatorische Herausforderungen und Veränderungen in der Unternehmenskultur mit sich. Denn nicht nur die Funktionsweise des Webshops ändert sich, sondern auch die komplette Arbeitsweise der Teams. Dass die Softwarearchitektur eines Unternehmens durch die Organisationsstruktur begrenzt ist, kennst Du vielleicht als Conway’s Law. Das MIT (Massachusetts Institute of Technology) und die Harvard Business School haben in Studien herausgefunden, dass diese Beobachtung von Informatiker Melvin Conwey aus dem Jahr 1967 heute immer noch gültig ist.
Microservices, Monolith oder Modulith
Auch wenn viele Punkte für die modulare Softwarearchitektur sprechen, ist der Einsatz von Microservices nicht für jede Anwendung oder jedes Unternehmen geeignet. Gerade für kleine Anwendungen mit wenigen überschaubaren Funktionen lohnt es sich nicht, Microservices zu implementieren. Denn die einzelnen Services müssen immer up-to-date sein, gewartet und überwacht werden. Kosten und Aufwand für die Implementierung und Wartung von Microservices können in diesen Fällen höher sein als die Vorteile. Wenn man vermeiden möchte, dass verschiedene Teams bei der Entwicklung in Abhängigkeiten geraten, macht die Microservice-Architektur Sinn. Aber nur, wenn das Unternehmen über DevOps-Expertise verfügt. Gibt es keine Erfahrung mit der DevOps-Kultur, ist eine zeit- und manchmal auch kostenintensive Umstrukturierung innerhalb der Organisation angesagt. Da sich die einzelnen Teams selbst organisieren, fallen zentrale übergeordnete Positionen oft ganz weg. Neben Monolithen gibt es andere Architektur-Modelle, wie Modulithen oder Domain Driven Design, die dann in Frage kommen können.
Quelle: Entwickler.de
Eine Microservices-Architektur bietet viele Vorteile für Unternehmen, die ihre komplexen Anwendungen schnell und flexibel an die sich verändernden Geschäftsbedingungen anpassen müssen – und sich dabei den ein oder anderen Wettbewerbsvorteil sichern wollen. Sehr erfolgreich machten das die Microservice-Pioniere Spotify, Netflix, Amazon, Google und LinkedIn. Aber das modulare Softwaremodell ist keine Wunderwaffe und die Mikroservice-Architektur bringt viele Herausforderungen mit sich. Selbst Anhänger:innen dieser Philosophie raten, mit einer monolithischen Struktur zu starten, die mit steigender Komplexität verteilt werden kann. Denn der Einsatz von Microservices allein führt nicht automatisch zu korrekten Abhängigkeiten und sauberen Schnittstellen.
- Die Idee von Microservices ist es, das komplexe Ganze in kleinere und überschaubarere Teile zu verteilen. Einzelne Funktion werden mit eigener Code-Basis und Tech Stack ausgelagert und laufen vollkommen unabhängig voneinander.
- Microservices werden häufig in Kombination mit anderen Technologien wie Containerisierung und Orchestrierung eingesetzt, um die Portabilität der Dienste zu erhöhen und sie wiederzuverwenden.
- Eine der größten Herausforderungen ist die Fehlerbehandlung und die Überwachung der Dienste: Es kann schwieriger sein, Probleme in einer Anwendung zu identifizieren und zu beheben, die aus sehr vielen kleinen Diensten besteht.