Microservice-Architectures: Mehr als die Summe ihrer Teile?
Es gibt verschiedene Möglichkeiten, wie man Software aufbauen kann. Statt ein Projekt beispielsweise als großes Ganzes zu realisieren, kann es sinnvoll sein, die Aufgabe zu verteilen und kleine Pakete zu schnüren – sogenannte Microservices. Der Begriff hat dabei nicht nur viel mit dem Aufbau einer Computeranwendung zu tun, sondern spielt auch bei der Planung im Sinne des agilen Projektmanagements eine große Rolle. Welche Vorteile hat die Microservice-Architektur, wie funktioniert sie und wo setzt man die Technik bereits ein?
„Do one thing and do it well“: eine Microservice-Definition
Es gibt keine klare Abgrenzung, wann man von einer Microservice-Architektur sprechen kann und wann nicht. Um die Begriffsverwirrung noch zu unterstützen, geht es bei Microservices nicht ausschließlich um eine Software-Technik, sondern in großem Maße auch um eine Arbeitsweise von Entwicklern: Wie lassen sich große Programmierungsprojekte am besten realisieren? Als generelle Regel kann man sich merken, dass Projekte, die Microservices einsetzen, immer Ken Thompsons Unix-Philosophie folgen: „Do one thing and do it well“. Man soll sich also auf eine Aufgabe konzentrieren, diese aber zur Perfektion bringen. Die Aussage ist nicht nur ein Ratschlag für die Programmierarbeit, sondern beschreibt auch die Funktionsweise von einzelnen Microservices.
Im Sinne der Programmentwicklung verstanden, soll man kleine Teams bilden, die sich um einen einzelnen Dienst kümmern – realisiert in einem Microservice. Im Sinne des Projektmanagements geht es also um eine Fokussierung der Teams und um deren Unabhängigkeit. Statt einer zentralen Administration ist jedes Team voll für sein Endprodukt verantwortlich, und zwar im kompletten Entwicklungszyklus: von der Entstehung bis zur Auslieferung und der anschließenden Überwachung. Diese Arbeitsweise bringt viele Vorteile mit sich und im Ergebnis eine modulare Software-Architektur hervor.
Die Kombination von Arbeitsweise und Produkt geht auf das Gesetz von Conway zurück: Der Informatiker Melvin Conway hatte bereits 1967 beobachtet, dass die Strukturen von Programmen und anderen Systemen immer den Strukturen der Gruppe ähneln, die mit der Entwicklung betraut ist.
Eine Microservice-Architektur ist prinzipiell eine Weiterentwicklung von serviceorientierter Architektur (SOA): Auch bei diesem Architekturmuster spielen kleine Services eine Rolle. Diese sind aber immer noch eingebettet in ein großes System und nicht so unabhängig, wie man es von einer Microservice-Architektur erwartet. Genau wie es für letztere keine klare Definition gibt, ist auch SOA ein eher schwammiger Begriff. Daher sind die Übergänge zwischen den beiden Mustern fließend.
Microservice-Architecture vs. Monolithic Architecture
Traditionelle Programmentwicklung funktioniert nach dem Prinzip des Monolithen: Man realisiert alle Aufgaben in einer großen Anwendung. Alle einzelnen Services greifen auf eine große Datenbank zu und werden über ein Nutzerinterface ausgegeben – alles realisiert innerhalb der einen Anwendung. Der Ansatz von Microservices geht von Modulen aus: Jeder Microservice ist nur für die Erledigung einer einzelnen Aufgabe zuständig. So verschieden das Ergebnis bei beiden Ansätzen ist, so unterschiedlich sind auch die Arbeitsprozesse.
Während bei einer Microservice-Architektur ein Team sich nur um die Entwicklung eines Microservices kümmert, ist die Teambildung bei Monolithen anders organisiert. Teams organisieren sich nach der Technologie, mit der sie sich auseinandersetzen: Ein Team befasst sich mit Datenbanken, ein anderes programmiert die einzelnen Services und ein drittes befasst sich mit der Gestaltung eines User-Interfaces. Für die Veröffentlichung von Updates, Wartungsarbeiten und Analysen sind wiederum andere Arbeitsgruppen zuständig. Allerdings sind bei einem Monolithen alle Teams voneinander abhängig. In einer Microservice-Architektur sollen Abhängigkeiten so gut es geht vermieden werden.
Was sind die Vorteile einer Microservice-Architektur?
Bei einer Microservice-Architektur wird eine große Anwendung in Form von kleinen, monofunktionalen Modulen (den Microservices) realisiert. Die Bausteine selbst werden unabhängig voneinander entwickelt und bilden zusammen das Gesamtprodukt. Dieser Architekturstil hat einige Vorteile.
Unabhängigkeit
Bei der Entwicklung eines Microservices agieren Teams in der Regel vollkommen autonom. Weder gibt eine übergeordnete, autoritäre Instanz eine Vorgehensweise vor, noch müssen sich die einzelnen Projektgruppen untereinander ständig koordinieren. Im Mittelpunkt des Teams steht einzig die Aufgabenstellung, der Nutzen des Microservices. Der Vorteil bei dieser Arbeitsweise: Das Entwicklungsteam kann den Lösungsweg einschlagen, der am besten für den Microservice geeignet ist, und nicht den, den andere vorgegeben haben. Das geht so weit, dass es möglich ist, für verschiedene Microservices auch unterschiedliche Programmiersprachen zu verwenden oder eigene Datenbanken bzw. Datenbankverwaltungssysteme einzusetzen. Dies ist möglich, da jeder Microservice seine eigene Laufzeitumgebung besitzt.
Robustheit
Ein weiterer Vorteil der Unabhängigkeit ist, dass das komplette System dadurch sehr viel robuster wird. Sollte ein Microservice ausfallen, bricht nicht die komplette Anwendung zusammen, nur der Teilaspekt funktioniert nicht mehr. Da es sich hierbei um einen überschaubaren Prozess handelt, ist außerdem die Fehlersuche sehr viel einfacher: Statt den Quellcode eines großen Monolithen zu durchsuchen, braucht nur ein relativ kleines, in sich geschlossenes Programm analysiert zu werden.
In diesem Zusammenhang lässt sich auch Continuous Delivery nennen: Hierbei werden Software-Produkte ständig weiterentwickelt. Microservices geben Herstellern die Möglichkeit, nicht in großen Etappen updaten zu müssen. Stattdessen werden Weiterentwicklungen eines Microservices direkt – natürlich nach einer entsprechenden Testphase – veröffentlicht, unabhängig von den restlichen Prozessen. Auch kleinere Änderungen in einem Deployment-Monolithen vorzunehmen, kann sehr aufwendig sein. Einen Microservice, der ja nur eine einzige Aufgabe erfüllt, abzuändern, ist viel einfacher zu bewerkstelligen, schließlich verbraucht er viel weniger Ressourcen.
Der Continuous Delivery kommt auch die agile Arbeitsweise zugute: Das Team, dass sich um diesen Microservice kümmert, ist absoluter Spezialist und kann Änderungen ohne große Probleme vornehmen. Darüber hinaus wird ohnehin nur eine Änderung im Quelltext (ein neues Feature oder ein Bugfix) pro Version vorgenommen. Dies hilft zusätzlich dabei, zügige Änderungen vorzunehmen und damit die Stabilität des Gesamtsystems langfristig zu sichern.
Kompatibilität
Am Ende fügt man alle Bausteine zusammen: So verschieden die Microservices im Aufbau auch sind, am Ende müssen sie gemeinsame Anknüpfungspunkte haben. Diese sollen möglichst einfach gestaltet sein, damit die Verbindung wenig Einfluss auf den eigentlichen Prozess nimmt. Deshalb setzen die meisten Entwickler von Microservices auf REST-APIs. Über die einheitlichen und schlanken HTTP-Methoden wie GET oder POST können die einzelnen Microservices einfach miteinander kommunizieren und die entsprechenden Informationen austauschen.
Skalierbar
Wenn ein Monolith (also ein geschlossenes System, das alle Prozesse in sich vereint) nach oben skaliert werden muss, ist man gezwungen, das komplette System zu spiegeln. Eine Microservice-Architektur gibt Entwicklern die Möglichkeit, sehr feingranular zu skalieren. Man braucht nur den Service zu stärken, der dies benötigt. Das hält schließlich auch das Endprodukt sehr viel schlanker und spart Ressourcen. Ebenfalls ist es nicht so aufwendig, einen komplett neuen Service in das System zu integrieren.
So werden Microservices-Architectures umgesetzt
Microservices sind untereinander vollkommen isoliert und laufen in einer eigenen Umgebung. Nur über Schnittstellen kommunizieren die einzelnen Anwendungen miteinander. Um eine solche Isolation durchzuführen, gibt es verschiedene Möglichkeiten:
- Container: Die vielleicht häufigste Art, eine Microservice-Architektur aufzubauen, funktioniert mit Containern. Diese stellen eine sehr schlanke Form von Virtualisierung dar, denn es werden nicht komplette virtuelle Maschinen erzeugt. Stattdessen baut man auf einem bestehenden Betriebssystem auf und nutzt dessen Kernel. In den Containern laufen die Microservices komplett autark: Alles was sie benötigen, um zu funktionieren, steckt mit im Container.
- Virtuelle Maschinen: Es ist möglich, für jeden Microservice eine eigene virtuelle Maschine zu erzeugen. Auch hierbei können die Microservices isoliert voneinander agieren. Der Nachteil gegenüber einer Container-Technologie wie Docker ist allerdings, dass jede virtuelle Maschine ein eigenes Betriebssystem benötigt und damit sehr viele Ressourcen.
Denkbar wäre sogar, für jeden Microservice eine eigene, physische Serverinstanz aufzusetzen. In der Praxis dürfte dies allerdings einer großen Ressourcenverschwendung gleichkommen, weshalb man in der Regel auf Virtualisierung setzt. Ganz wichtig ist aber, egal für welche Realisierung man sich entscheidet, dass die Isolierung wirklich gegeben ist. Es ist weder anzuraten, mehrere Microservices auf einem Server laufen zu lassen, noch sie zusammen in einen Container zu stecken. Das könnte zu Konflikten zwischen den einzelnen Anwendungen führen. Damit es im gesamten System nicht zu Überlastungen kommt, setzt man Load Balancer ein. Diese verteilen die Last automatisch auf unterschiedliche Instanzen, um Ausfällen vorzubeugen.
Arbeiten mit Microservices: 3 Beispiele
Inzwischen haben Microservice-Architekturen Einzug in die Systeme von großen Unternehmen gehalten. Die Unternehmen haben es so geschafft, gewisse Probleme aus dem Weg zu schaffen oder ihre Abläufe zu optimieren. Die Beispiele von Netflix, Spotify und eBay zeigen, warum sich große Unternehmen mit etablierten monolithischen Systemen entscheiden, die Architektur aufzubrechen und Microservices zu bilden. Auch andere IT-Firmen wie Google und Amazon arbeiten auf diese Art. Sie setzten modulare Systeme teilweise schon ein, als es dafür noch gar keinen Namen gab.
Netflix
Wie bei den meisten Unternehmen basierte der Service von Netflix früher (zu Zeiten, als Netflix noch kein Onlinestreaming-Dienst war, sondern nur DVDs per Post verschickte) auf einem monolithischen System. Im Jahr 2008 gab es einen Fehler in einer Datenbank, der dazu führte, dass der komplette Dienst vier Tage lang ausfiel. Daraufhin entschied man sich, das alte System aufzubrechen und in Microservices aufzuteilen. Damit erreichte man, dass das Unternehmen Änderungen sehr viel schneller live schalten konnte und so auch Reparaturen viel zügiger vonstattengingen. Da das System von Netflix enorm umfangreich ist, entwickelte man ein eigenes Programm, um die einzelnen Microservices untereinander zu organisieren: Conductor. Conductor gibt Netflix u. a. die Möglichkeit, Microservices zentral zu steuern (pausieren oder neustarten) oder zu skalieren. Im Kern des Programms arbeitet ein Service namens Decider. Dieser kann automatisiert Prozesse planen und so auf Ereignisse im Workflow reagieren. Weitere Programme, die Netflix entwickelt hat, um effektiv mit Microservices arbeiten zu können, sind Mantis (Stream-processing), Dynomite (datastore) und Vizceral (traffic intuition).
Netflix greift sehr häufig auf Open-Source-Programme zurück und stellt daher auch die eigenen Entwicklungen offen ins Netz. In ihrem GitHub-Profil können alle genannten Programme eingesehen werden.
Spotify
Ein weiterer Streaming-Dienst, Spotify, setzt bei seinem Angebot ebenfalls auf Microservices. Die Herausforderung, die Spotify in seinem Entwicklungsalltag begegnen muss, ist die starke Konkurrenz. Der Audiostreaming-Markt hat mit Amazon, Apple und Google einige der größten IT-Unternehmen der Welt als Mitspieler. Gleichzeitig müssen die Entwickler aufgrund steigender Nutzerzahlen ständig einen höheren Bedarf abdecken und bestimmte Geschäftsregeln (etwa Lizenzrechte) beachten. Um schnell auf neue Entwicklungen der Konkurrenz reagieren zu können und eigene Innovationen schneller zu veröffentlichen – damit wiederum die Konkurrenz reagieren muss –, sind Microservices die richtige Lösung für Spotify.
Beispielsweise ist die Funktion, dass Nutzer bereits beim Eintippen eines Suchbegriffs Vorschläge erhalten, ein in sich geschlossener Microservice, um den sich ein eigenes Team kümmert. Zusätzlich profitiert Spotify von der Robustheit einer Microservice-Architektur: Wenn ein einzelner Microservice ausfällt, wird nicht das komplette Produkt unbrauchbar. Insgesamt sind bei Spotify über 800 Microservices aktiv. Der Streaming-Dienst nutzt übrigens für einen Großteil der Microservices Java. Dies hat allerdings nichts damit zu tun, dass Microservices nicht in unterschiedlichen Programmiersprachen geschrieben werden können. Tatsächlich hat es mit den Arbeitsprozessen zu tun: Entwickler wechseln ständig von einem Team ins andere – da ist es einfacher, wenn alle die gleiche Sprache verwenden.
eBay
Die Verkaufsplattform eBay begann – wie die meisten Systeme – als Monolith: Zwischenzeitlich hatte eBay 3,4 Millionen Zeilen Code in nur einer einzigen Datei. Aus dieser Situation heraus entschied man sich, den Monolithen aufzubrechen und Microservices (in Java) zu entwickeln. Auch bei eBay kommunizieren die einzelnen Services via REST miteinander.
Die Tatsache, dass eBay und viele andere Unternehmen den Weg von einer monolithischen zu einer Microservice-Architektur erfolgreich gegangen sind, beweist den Nutzen der moderneren Herangehensweise. Während in den Anfangstagen eines Onlineprojekts mit wenigen aktiven Nutzern und einem überschaubaren Angebot ein Monolith vollkommen ausreicht, wird dieser mit steigenden Anforderungen zum wachstumshemmenden Ungetüm.
Fazit: Ist Microservice-Architecture grundsätzlich besser?
Auch wenn vieles dafür spricht, Systeme auf Basis von Microservice-Architekturen aufzubauen, muss das moderne Vorgehen nicht für jedes Unternehmen bzw. jedes Projekt die richtige Lösung sein. Gerade für kleinere Computerprogramme, die ohnehin nur wenige Aufgaben bewältigen, kann die Etablierung von Microservices einen erhöhten Aufwand bedeuten. Nicht nur die Erstellung der Services, sondern auch Wartung, Weiterentwicklung und Monitoring sind vergleichsweise aufwendig. Gerade auch bei der Überwachung der Prozesse (Monitoring) muss genauestens abgewogen werden, ob diese mit Microservices besser oder schlechter gelingt: Auf der einen Seite sind einzelne Microservices sehr einfach zu analysieren und zu messen. Durch die Masse an Microservices wächst diese Aufgabe aber enorm.
Schaut man sich die Vorteile der Arbeitsabläufe an, wird ersichtlich, dass dies nicht für jedes Projekt – vor allem nicht kurzfristig – sinnvoll ist. Ein Pluspunkt bei der Arbeit mit Microservices ist die Unabhängigkeit der einzelnen Teams. Man möchte z. B. vermeiden, dass ein Team auf die Ergebnisse eines anderen warten muss. Wenn das komplette Entwicklerteam aber ohnehin nur aus wenigen Personen besteht, hat diese Trennung wenig Sinn. Zudem wird – wenn man dem Gesetz von Conway folgt – ein kleines Team, in dem man aus pragmatischen Gründen keine klaren Trennlinien ziehen kann und vieles in Personalunion erledigt, ohnehin ein anderes Ergebnis erzielen.
Und auch bei größeren Teams ist eine umfangreiche Umstellung nötig: Positionen, die die Entwicklung zentral steuern, fallen vermehrt weg, stattdessen organisieren sich Entwicklerteams selbst. Eine solche Umstrukturierung ist sowohl zeit- als auch kostenintensiv. Auch dies darf man bei einer eventuellen Umstellung des Systems nicht außer Acht lassen.
Einige Befürworter von Microservice-Architekturen empfehlen daher eine Monolith-first-Strategie. Demnach ist es sinnvoll, ein Programmierprojekt zunächst als Monolithen anzugehen und die Vorteile dieser Herangehensweise vor allem in den Anfangstagen auszuschöpfen. Erst wenn das Projekt einen entsprechenden Umfang angenommen hat, sollte man auf eine Microservice-Architektur umsteigen. Zwischen beiden Systemen findet man die serviceorientierte Architektur (SOA), die als guter Zwischenschritt geeignet ist. Auch hierbei wird modularisiert vorgegangen. Die einzelnen Dienste sollen dabei Geschäftsprozesse abbilden.