Genau an dieser Stelle unterscheiden sich Microservices von anderen Modularisierungsansätzen [1]. Microservices sind Module, die unabhängig in Produktion gebracht werden können. Um dieses Ziel zu erreichen, sind die Microservices beispielsweise eigene Prozesse, virtuelle Maschinen oder Docker Container als leichtgewichtige Alternative zur Virtualisierung. Das getrennte Deployment kann hierbei folgendermaßen funktionieren: Bei der Nutzung von Docker Containern wird eine neue Version eines Microservices ausgerollt, indem der alte Docker Container entfernt und ein neuer gestartet wird. Der Docker Container kann eine eigene Web-Oberfläche anbieten - beispielsweise für die Registrierung eines Kunden in einem eCommerce-System. Andere Microservices können andere Teile der Oberfläche anbieten. Microservices können sich aber auch gegenseitig über eine API beispielsweise mittels REST aufrufen.
Also nehmen Microservices die Idee der Modularisierung aus der Entwicklung auf und erweitern diese auf den Bereich des Deployments. Das erscheint zunächst nur eine kleine Änderung zu sein, hat aber viele Vorteile.
Damit zielen Microservices auf die Modularisierung einzelner Systeme. Sie sind im Gegensatz zu SOA kein Konzept, um die IT-Landschaft eines gesamten Unternehmens zu verbessern.
Organisation
Oft werden Microservices als eine Architektur dargestellt, die nicht zur Skalierung der Architektur selber dient, sondern zur Skalierung der Organisation:
- Dazu muss jedes Team einen fachlichen Teil verantworten, der in einem oder mehreren Microservices implementiert ist. Weil Microservices unabhängig voneinander in Produktion gebracht werden können, wird so eine große und komplexe Architektur in kleine Bestandteile aufgeteilt. Bei einem guten fachlichen Schnitt kann jedes Team unabhängig von den anderen Teams Anforderungen implementieren und sie in Produktion bringen. So wird durch eine geschickte Architektur aus einem großen Projekt mehrere kleine, ohne dass dazu umfangreiche organisatorische Maßnahmen notwendig sind.
- Jeder Microservice kann in einer eigenen Technologie implementiert werden. Da Microservices eigene Prozesse sind, können unterschiedliche Programmiersprachen und Bibliotheken genutzt werden. Auch die Datenbank kann unterschiedlich sein, denn Microservices sollen auch eine getrennte Datenhaltung haben, um möglichst gut voneinander entkoppelt zu sein. Microservices erlauben also nicht nur die fachliche Unabhängigkeit der Teams, sondern auch die technische Unabhängigkeit. Natürlich kann dennoch beispielsweise die Programmiersprache festgeschrieben werden. Aber selbst in diesem Fall gibt es Vorteile: Wenn eine neue Version einer Library genutzt werden soll, sind dazu keine Meetings notwendig. Schließlich betrifft die Änderung nur einen Microservice und damit auch nur ein Team.
- In der Praxis können Microservice-Projekte so komplex werden, dass der Gesamtüberblick über das System verloren geht. Wenn das System gut strukturiert ist und ein Use-Case wegen der fachlichen Struktur nur in einem Microservice oder einigen wenigen Microservices implementiert ist, dann müssen Entwickler für Änderungen auch nur diese Microservices anpassen und verstehen. So können komplexere Systeme entwickelt werden, weil zur Entwicklung ein Verständnis von Teilen des Systems ausreicht. Dieser Effekt stellt sich auch tatsächlich in Projekten in der Praxis ein.
Durch die Trennung in Deployment-Einheiten erreichen Microservices also eine bessere Entkopplung. Das führt zu weniger Abstimmungsbedarf, was für die Organisation vorteilhaft ist. Die Vorteile auf organisatorischer Ebene verleiten dazu, Microservices nur als Lösung für große Teams und komplexe Projekten in Erwägung zu ziehen. In der Praxis gibt es jedoch viele Projekte mit kleinen Teams, die Microservices nutzen. Es gibt also mehr Gründe für den Einsatz von Microservices.
Architektur
Auf Ebene der Architektur haben Microservices ebenfalls einige Vorteile:
- Die Integration mit Legacy-Systemen ist einfacher. Das Problem bei der Weiterentwicklung von Legacy-Systemen ist die mangelnde Code-Qualität. Daher ergänzen Microservices das System aber nicht durch Code-Änderungen. Stattdessen bearbeitet ein Microservice statt des Legacy-Systems bestimmte Requests. Das können RESTful HTTP Requests sein, bei denen JSON zurückgeliefert wird, oder auch HTML-Seiten.
- Die Architektur großer Systeme neigt dazu, über die Zeit schlechter zu werden. Eine Architektur erlaubt nur bestimmte Beziehungen zwischen Modulen, aber mit der Zeit schleichen sich ungewünschte Beziehungen ein, bis das System irgendwann keine gute oder überhaupt keine erkennbare Strukturierung mehr hat. Microservices ziehen in ein System Grenzen ein, die schwer zu überwinden sind. Ein Microservice wird beispielsweise über eine REST-Schnittstelle angesprochen. Dadurch werden Abhängigkeiten offensichtlich. Das wirkt einem Verfall der Architektur entgegen. Zwischen den Microservices muss es natürlich auch eine Aufteilung der Funktionalitäten und damit eine Architektur geben. Sie sollte sich nach fachlich-funktionalen Aspekten richten. Also sollte es beispielsweise keine technische Schichtung auf dieser Ebene geben. Auf dieser Ebene kann auch ein Architektur-Management sinnvoll sein, um die Qualität der Architektur zu garantieren.
- Weil Microservices nicht besonders groß sind, können Microservices ohne übermäßigen Aufwand neu geschrieben werden. Wenn also ein Microservice nicht mehr wartbar ist, kann er komplett ersetzt werden. Das beeinflusst die anderen Microservices nicht - also kann ein einzelner Microservice neu implementiert werden. Das wirkt ebenfalls einem Verfall der Architektur entgegen. Bei einem klassischen System müsste alles neu geschrieben werden. Das ist mit einem hohen Risiko und einem hohen Aufwand verbunden. Daher wird diese Option meistens nicht genutzt.
Die architekturellen Vorteile sind für große Projekte wichtig, können aber schon bei kleinen Systemen vorteilhaft sein.
Technische Vorteile
Schließlich haben Microservices auch technische Vorteile:
- Jeder Microservice kann einzeln skaliert werden. Das erleichtert den Umgang mit hoher Last.
- Microservice-Systeme können bei der passenden Architektur robuster als herkömmliche Systeme sein. Beispielsweise beeinflusst ein Speicherleck nur einen Microservice und kann daher auch höchstens einen Microservice zum Absturz bringen. Wenn das System den Ausfall einzelner Microservice kompensieren kann, wird das System sehr stabil sein.
- Ähnliches gilt für Sicherheit: Auch bezüglich dieses Aspekts sind die Systeme getrennt. Daher können Microservices beispielsweise mit Firewalls gegeneinander abgesichert werden. Durch solche und andere Maßnahmen kann eine mögliche Kompromittierung oft auf einen Microservice eingeschränkt werden.
- Wie schon erwähnt kann jeder Microservice andere Technologien nutzen. So können spezialisierte Lösungen in einzelnen Microservices genutzt oder ausprobiert werden, ohne den Rest des Systems zu beeinflussen.
- Schließlich reduzieren Microservices Risiken: Wenn ein Microservice in Produktion gebracht wird, ist das nur eine kleine Änderung. Neue Fehler können nur in diesem Microservice enthalten sein. Das ist eine wirkungsvolle Absicherung gegen Regressionen.
Microservices bieten also Vorteile auf ganz verschiedenen Ebenen. Daher sollte man Microservices auch nicht als eine spezielle Lösung für ein Problem verstehen, sondern als einen Ansatz, der viele verschiedene Einsatzmöglichkeiten hat (Abb. 1).
Spielarten von Microservices
Abhängig vom konkreten Szenario und den angestrebten Vorteilen, können Microservices ganz unterschiedlich umgesetzt werden:
- Unternehmen wie Netflix [2] oder Zalando [3] setzten auf Systeme, bei denen Microservices REST-Schnittstellen anbieten (siehe Abb. 2). Ein Microservice ruft andere Microservices aufrufen. Letztendlich implementieren die Microservices APIs, die verschiedenen Frontends zur Verfügung stehen. In einem solchen Szenario sind die Microservices entsprechend dem Namen kleine Services, die eine Schnittstelle anbieten. In solchen Systemen müssen die Aufrufe an abhängige Systeme so implementiert sein, dass der Ausfall eines Systems kompensiert werden kann, um Fehlerkaskaden zu vermeiden. Ebenso müssen Anfragen an andere Systeme parallel erfolgen, um zu vermeiden, dass sich Wartezeiten aufaddieren. Diese Ansätze legen einen Fokus auf Skalierung und Robustheit. Sie können auch sinnvoll sein, wenn das Gesamtsystem eine API anbieten soll oder eine Migration eines Legacy-System in eine solche Architektur besonders einfach ist. Die allermeisten Microservices-Projekte lösen ein bestehendes System ab, so dass eine einfache Migration ein wichtiger Grund für die Wahl einer bestimmten Architektur sein kann.
- Microservices können aber auch nur einige wenige Zeilen Code umfassen und in vollständig unterschiedlichen Programmiersprachen geschrieben sein. Sie können dann asynchron kommunizieren (Abb. 3). Fred George [4] hat mit diesem Architekturansatz einigen Erfolg gehabt, u.a. beim Guardian. Microservices implementieren dann jeweils einen Teil eines Algorithmus. Durch den Austausch der Microservices können unterschiedliche Algorithmen ausprobiert werden. Der Ansatz kann mit Developer Anarchy kombiniert werden. Bei diesem Ansatz versuchen die Entwickler, direkt Business KPIs durch Software zu verbessern, statt einfach Anforderungen zu implementieren. Das treibt Microservices auf die Spitze: Die Services sind sehr klein und die Autonomie der Teams ist gleichzeitig sehr hoch.
- Self-contained Systems (SCS) [5] teilen ein System in eigenständige Web-Anwendungen auf, die optional auch eine API beispielsweise für mobile Apps haben können (Abb. 4). Jedes SCS enthält also eine Web-Benutzeroberfläche und eine Datenbank. SCS sind also keine Services, die nur eine API anbieten, sondern haben immer auch eine Benutzeroberfläche. Eine fachliche Änderung betrifft also nur ein SCS - selbst wenn es auch die Benutzeroberfläche oder die Datenbank betrifft. Die SCSs integrieren sich auf der UI-Ebene. Der Browser kann dazu mit JavaScript HTML-Fragmente von verschiedenen SCS zusammensetzten. Die Alternative ist ein Web-Server oder ein Web-Cache, der die Komposition übernimmt [6]. Neben der UI-Integration ist noch asynchrone Kommunikation möglich. Ein SCS wird von einem Team verantwortet. Also sind SCS eher grobgranular - typischerweise ergeben sich nur 10–20 auch für ein großes System. Der Ansatz passt für viele Szenarien. Firmen wie Otto [7], Galeria Kaufhof [8] und Kühne+Nagel [9] nutzen ihn in der Umsetzung ihrer Systeme.
Alle diese Spielweisen sind in Projekten erfolgreich und haben unterschiedliche Vor- und Nachteile. Auch das ist ein Hinweis darauf, dass Microservices einen breiten Einsatzkontext haben und mehr sind als nur eine Modeerscheinung.
Techniken
Da Microservices nur ein unabhängiges Deployment voraussetzen, können sie unterschiedlich umgesetzt werden:
- Ein Microservice kann als virtuelle Maschine ausgeliefert werden. Das verbraucht sehr viele Ressourcen: Die Virtualisierung verbraucht CPU und RAM und die Festplattenimages Speicherplatz. Daher hat sich als Alternative Docker durchgesetzt. Docker-Container teilen sich den Kernel und haben ein sehr effizientes Dateisystem, so dass gegenüber einem Prozess kaum ein Overhead entsteht. Dennoch haben Docker-Container eine eigene Betriebssystem-Installation und ein eigenes Netzwerk-Interface.
- Theoretisch könnte ein Microservice auch einfach ein Prozess sein. Dann muss man aber aufpassen, dass der Prozess nicht dieselben Netzwerkports belegt wie ein anderer Microservice und der Server so installiert ist, dass alle Prozesse auch ablaufen können.
- Schließlich erlauben Technologien wie OSGi im Java-Bereich oder Erlang, dass in einem Prozess zur Laufzeit einzelne Teile ausgetauscht werden. Etwas ähnliches gilt für Java Application Server: Eine einzelne Anwendung auf einem Application Server kann neu deployt werden. Auch wenn auf dem Application Server mehrere Anwendungen laufen, ist der Application Server im Betriebssystem nur ein Prozess. Also verbraucht jede Anwendung weniger Ressourcen.
- Amazon Lambda [10] und konkurrierende “serverless” Ansätze erlauben noch kleinere Einheiten. In der Infrastruktur werden einzelne Funktion deployt, die zum Beispiel in JavaScript geschrieben sind. Die Infrastruktur versieht die Funktionen auch mit Monitoring, so dass ein professioneller Betrieb möglich ist. Abrechnung erfolgt nach Aufrufen. Wenn eine Funktion nicht aufgerufen wird, kostet sie auch kein Geld. So sind sehr kleine Microservices möglich, weil sie ohne sonderlichen Aufwand direkt in Produktion gebracht werden können, zunächst keine Kosten für diesen Service entstehen und auch der Betrieb unterstützt wird.
Docker Container sind fast synonym zu Microservices. Aber auch die anderen Ansätze haben sinnvolle Einsatzkontexte. Die Technologien unterscheiden sich vor allem in der Isolation: Ein Docker-Container oder eine virtuelle Maschine hat eine komplette, eigene Betriebssytem-Installation. Änderungen am Betriebssystem betreffen also nur einen Microservice. Sind die Microservices Prozesse, dann teilen sie sich das Betriebssystem. Ein Microservice auf einem Java Application Server wiederum ist wesentlich schlechter gegen andere Microservices auf demselben Server isoliert, als dies bei Prozessen der Fall wäre. Wenn ein Microservice auf dem Application Server sehr viel Speicher verbraucht oder auf der CPU eine hohe Last erzeugt, beeinflusst das die anderen Anwendung auf dem Application Server - ein Absturz des Application Servers führt sogar zum Ausfall aller Microservices.
Wenn wirklich nur ein getrenntes Deployment notwendig ist, dann kann ein Ansatz mit einem Application Server ausreichend sein. Außerdem spart der Ansatz Ressourcen: Eine Anwendung auf einem Application Server verbraucht weniger RAM und CPU als ein Docker Container. Außerdem gibt oft schon viele Erfahrungen mit Application Server. Wenn aber eine bessere Isolation notwendig ist, können Docker-Container die bessere Wahl sein.
Die Wahl der richtigen Technologie kann also das Erreichen der Architektur-Ziele unterstützen und auch die Nachteile der Microservice-Architektur minimieren.
Nachteile
Microservice-Systeme haben natürlich viel mehr Einheiten, die in Produktion gebracht werden müssen und dort auch überwacht werden müssen, als das bei klassischen Systemen der Fall ist. Das ist eine der größten Herausforderungen beim Einsatz von Microservices. Begegnen muss man der Herausforderung durch Automatisierung. Diese ist wegen der höheren Qualität der Betriebsprozesse und der besseren Reproduzierbarkeit aber sowieso wünschenswert. In gewisser Weise sorgen Microservices dafür, dass bekannte Best Practices auch tatsächlich umgesetzt werden. Eine andere Möglichkeit, mit dem Problem im Betrieb umzugehen, ist die Nutzung von Plattformen wie den schon erwähnten Serverless-Umgebungen oder PaaS (Platform as a Service).
Jeder Microservice hat auch eine eigene Datenbank oder zumindest ein getrenntes Schema in einer gemeinsamen Datenbank. Nur so können die Microservices tatsächlich unabhängig gehalten werden. Wenn eine Gesamtsicht auf die Daten notwendig ist, müssen diese repliziert werden. Das ist aber kein neuer Ansatz - Data Warehouses machen so etwas schon lange - nicht nur wegen der Performance, sondern auch weil andere Datenmodelle für die Analyse notwendig sind.
Ebenfalls sind Tests eine Herausforderung: Wenn einzelne Microservices unabhängig in Produktion gebracht werden sollen, müssen die vorher notwendigen Tests ebenfalls möglichst unabhängig sein. Sonst müssen alle Microservices gemeinsam vor der Produktionseinführung getestet werden. Diese Testphase wird dann zu einem Flaschenhals. Selbst wenn die Tests der einzelnen Microservices schnell sind - durch den Flaschenhals bremsen sie sich gegenseitig aus. Bei einer Migration in eine Microservices-Architektur ist es nicht ausreichend, das System in Komponenten aufzuteilen. Die Tests müssen ebenfalls modularisiert werden.
Schließlich ist ein Microservices-System ein verteiltes System - wenn man es nach dem Netflix-Beispiel aufbaut. Aufrufe in verteilten System sind wesentlich langsamer als Aufrufe in einem System. Außerdem kann es vorkommen, dass der Empfänger eines verteilten Aufrufs gerade nicht zur Verfügung steht. Die Performance und Zuverlässigkeit sind also zusätzliche Herausforderungen bei Microservices.
Aber nicht jede Spielart von Microservices habe dieses Problem: SCS teilen ein System in eine Vielzahl von Web-Anwendungen auf. Ein HTTP-Request eines Benutzer wird dann typischerweise von einem einzigen SCS abgearbeitet und es gibt keine Verteilung. So nimmt die Komplexität des Systems ab.
Ähnliches gilt für die Infrastruktur: SCS sind Web-Anwendungen. Das System wird zwar mehrere Web-Anwendungen enthalten, aber es sind kaum neue Elemente in der Infrastruktur notwendig. Ein Netflix-Ansatz kommt beispielsweise nicht ohne Service Discovery zur Registrierung und Suche nach Microservices aus.
Durch die Wahl eines bestimmten Architektur-Ansatzes und eine Analyse, welche Ziele die Microservices-Architektur tatsächlich hat, können die Nachteile also begrenzt werden.
Vielleicht der größte Nachteil ist, dass Microservices gerade in Mode sind. Es kann daher gut sein, dass viele Microservices-Architekturen umsetzen, weil “man heute Architektur ja so macht”. Wer so unvorbereitet eine Architektur auswählt, wird mit einem anspruchsvollen Modell wie Microservices kaum erfolgreich sein können.
Fazit
Die Idee zu Microservices ist nicht neu und Firmen wie Amazon [11] setzten die Ideen schon seit 10 Jahren um. Im Objektspektrum sind die grundlegenden Ideen auch schon vor 5 Jahren erläutert worden [12].
Microservices haben sich in den letzten Jahren zu einer Architektur-Familie entwickelt. Zahlreiche Projekte nutzen solche Ansätze. Mittlerweile sind daher Vor- und Nachteile bekannt. Ebenso wird die Umsetzung einfacher, weil mehr und mehr Technologien auf den Markt kommen, die speziell auf Microservices abgestimmt sind. Neben den Unternehmen aus dem Web-Umfeld setzten sich Microservices auch bei Finanzdienstleistern durch [13].
Ein wichtiger Faktor für den Erfolg von Microservices ist die gute Unterstützung für agile Softwareentwicklung. Aber Microservices sind mehr als nur eine Lösung für genau dieses Problem. Microservices haben nicht nur aus organisatorischer Sicht sondern auch aus architektureller und technischer Sicht Vorteile. Ein Fokus auf die gewünschten Vorteile ermöglicht eine Technologie- und Architektur-Auswahl, die Nachteile minimiert.
So sind Microservices breit einsetzbar - und sicher mehr als ein Hype und eine Eintagsfliege. Wie jeder anderer Ansatz haben Microservices aber auch Nachteile und sind nur in bestimmten Szenarien sinnvoll einsetzbar. Nachteile und Vorteile abzuwägen und dann die richtige Architektur auszuwählen, bleibt eine wesentliche Aufgabe für Software-Architekten.
Literatur & Links
-
Eberhard Wolff: Microservices: Grundlagen flexibler Softwarearchitekturen, dpunkt.verlag, 2015, ISBN 978–3864903137 ↩
-
http://www.programmableweb.com/news/why-netflix-moved-to-microservices-architecture/elsewhere-web/2016/04/02/ ↩
-
http://microxchg.io/2016/talk/Rodrigue_Schaefer_From_Monolith_to_Microservices.html/ ↩
-
http://www.se-radio.net/2016/03/se-radio-episode-253-fred-george-on-developer-anarchy/ ↩
-
https://dev.otto.de/2015/09/30/on-monoliths-and-microservices/ ↩
-
http://galeria-kaufhof.github.io/general/2015/12/15/architektur-und-organisation-im-galeria-de-produktmanagement/ ↩
-
http://www.heise.de/developer/artikel/Neun-Jahre-Microservices-2867634.html/ ↩
-
http://www.sigs-datacom.de/fileadmin/user_upload/zeitschriften/os/2011/Architekturen/tilkov_ghadir_OS_Architekturen_11.pdf/ ↩
-
https://jaxenter.de/micro-services-in-der-praxis-nie-wieder-monolithen-391/ ↩