Über unsMediaKontaktImpressum
Dr. Annegret Junker 06. August 2019

Wie man einen Monolithen zerschlägt

Der vorliegende Artikel berichtet über Erfahrungen beim Zerschlagen eines Monolithen. Schwierigkeiten zwangen uns, diesen Monolithen zu zerschlagen und zu einer Microservice-Architektur zu entwickeln. Die hier beschriebenen Änderungen beziehen sich auf einen Zeitraum von nahezu 5 Jahren. Während dieser Jahre haben wir nicht nur technische Schwierigkeiten überwunden, sondern auch neue Vorgehensmethoden und Prozesse eingeführt. Eine technische Architektur ist natürlich nicht unabhängig von den gewählten Vorgehensweisen. Das Gesetz von Conway machte sich auch für uns schmerzlich bemerkbar [1]. Der Artikel berichtet hauptsächlich von technischen Umbauten – aber während der Gesamtzeit war das Hauptaugenmerk auf die Weiterentwicklung der Geschäftslogik gerichtet. Technik war auch bei uns nur Mittel zum Zweck.

Ausgangspunkt – Business-Prozess

Das System, über das hier berichtet wird, war ein System, das Firmen bei Ausschreibungen und der Wahl von Lieferanten unterstützte.

Abb. 1 zeigt beispielhaft den Geschäftsprozess.

  • Auswahl von Lieferanten: Lieferanten konnten neu angelegt oder auch aus vorhandenen Listen ausgewählt werden.
  • Ausschreibung: Die entsprechende Leistung wurde durch die betroffenen Fachbereiche ausgeschrieben und die Unterlagen an die Lieferanten verschickt. Die Lieferanten konnten Angebote einreichen und ein oder mehrere Lieferanten wurden für die entsprechende Leistungserbringung ausgewählt.
  • Arbeitsmuster: Für die eigentliche Beauftragung müssen Lieferanten Arbeitsmuster erbringen, die vom Auftraggeber begutachtet werden. Solche Audits werden vor jeder Leistungserbringung aber auch während des Gesamtprozesses durchgeführt.
  • Lieferung: Der Lieferant erbringt die Leistung.
  • Rechnung: Der Lieferant erstellt die Rechnung.
  • Beschwerden: Der Auftraggeber kann auf Mängel in der Leistungserbringung reagieren. Der Lieferant beseitigt die Mängel und stellt die Leistung zu nochmaliger Prüfung bereit.

Dieser Geschäftsprozess wurde durch mehrere Applikationen unterstützt. Die entsprechenden Applikationen sind in Abb. 2 dargestellt.

Wie man hier schon erkennen kann, sind die Applikationen zwar schon entlang des Geschäftsprozesses geschnitten. Allerdings sind manche Grenzen nicht wirklich klar. Manche Geschäftsanwendungen übergreifen mehrere Geschäftsprozesse, andere wiederum decken sie nur unzureichend ab. Unterstützende Prozesse überspannen den gesamte Geschäftsprozess.

Systeminstabilitäten

Die hier gezeigten Anwendungen waren auf verschiedene Systemen verteilt. Diese Systeme sind in Abb. 3 dargestellt.

Die Applikationen Benutzer/Login, Stammdaten und Lieferantenmanagement sind auf dem ersten Cluster "Login und Stammdaten" innerhalb einer Java Virtual Machine (JVM) implementiert. Auf dem zweiten Cluster befinden sich dann die wesentlichen Geschäftsanwendungen Prozesse, Gelbe Seiten, Ausschreibungen, Beschwerden, Nachrichten und Dokumente ebenfalls in einer JVM. Beide Cluster verfügten zu diesem Zeitpunkt über eigene Datenbanken. Die Nachrichten-Anwendung als Anwendung für die Zusammenarbeit über soziale Nachrichten verfügte schon über eine eigene Datenbank.

Die Applikationen Projekte, Rechnung und Lieferung waren zu diesem Zeitpunkt schon von den hier beschriebenen Clustern getrennt, so dass wir über sie im Folgenden nicht weiter berichten wollen.

Beide Server-Cluster wurden durch die Geschäftsprozesse wesentlich belastet. Es kam immer wieder zu nicht geplanten Ausfällen. Von geplanten 95 Prozent Verfügbarkeit konnten wir nur knapp 90 Prozent erreichen. Was unabdingbar zu großen Diskussionen mit dem Betrieb und der Entwicklung führte.

Erste Hilfsmaßnahmen

So wurden zu diesem Zeitpunkt erste Hilfsmaßnahmen notwendig. Der Cluster mit der Geschäftslogik mit jeweils zwei XXL-Server-Knoten konnte die Anfragen nicht mehr verarbeiten. Durch die vielen Anwendungen auf einer Java Virtual Machine war der virtuelle Fußabdruck zu groß. Wir mussten diesen Fußabdruck schnell verringern. Daher haben wir einfach die Anzahl der Server verdoppelt.

Da aber die Applikationen auf ein solches horizontales Skalieren nicht vorbereitet waren, mussten wir an vielen Stellen kleinere Anpassungen vornehmen, um überhaupt dieses relativ kleine Skalieren zu ermöglichen. Weiteres horizontales Skalieren war aufgrund der internen Architektur dieser Java-Enterprise-Anwendungen zu diesem Zeitpunkt nicht möglich. Die interne Synchronisation der einzelnen Server-Knoten hätte bei mehr als vier Knoten zu viel Kapazitäten verbraucht und damit zu wesentlichen Performanz-Einbußen geführt. Obwohl dieser Ansatz nicht ideal war, konnten wir mit ihm mindestens die nächsten Monate überleben.

Erste kleine Ansätze

Wie schon erwähnt, blieb trotz der Instabilitäten die (Geschäfts-)Welt nicht stehen. Wir brauchten neue Applikationen und Funktionen. Die Anwendung Prozesse musste abgelöst werden und wir mussten applikations-seitig den Prozess "Audit" unterstützen. Wir wussten, dass wir diese Anwendungen nicht implementieren konnten, ohne die vorhandenen Applikationen zu gefährden. Weitere Anwendungen innerhalb der gleichen JVM hätten die gesamten Cluster gefährdet. Zu diesem Zeitpunkt waren wir Architekten schon davon überzeugt, dass nur ein (Micro-)Service-Ansatz hier helfen konnte. Allerdings konnten wir uns nicht komplett gegenüber dem Management durchsetzen. Aber die Anwendungen wurden getrennt aufgebaut, auch wenn sie (noch) nicht über eigene Datenbanken verfügten. Die sich daraus ergebene Architektur ist in Abb. 4 dargestellt.

Mit dieser Trennung machten wir erste positive Erfahrungen.

Weitere Systeminstabilitäten

Aber die Zeit bleibt nicht stehen. Erfreulicherweise hatten wir Erfolg mit unseren neuen getrennten Anwendungen. Aber dies führte wiederum zu Instabilitäten. Die Stammdaten, die in der Stammdaten-Applikation auf dem ersten Cluster erfasst wurden, wurden per Oracle-Streams auf die Geschäftslogik-Datenbank des zweiten Servers übertragen. Die neuen Instabilitäten wurden durch erhöhtes Aufkommen in der Stammdaten-Komponente ausgelöst. Wir mussten hier eine Lösung finden, die die Anforderungen sowohl bezüglich Stabilität als auch Performanz erfüllte. Auch hier konnten uns wieder Ansätze aus den Microservice-Architekturen helfen. Wir implementierten einen asynchrone Stammdaten-Verteiler, wie in Abb. 5 zu sehen.

Werden Stammdaten in der Stammdaten-Verwaltung angelegt oder geändert, werden alle Anwendungen, die sich für eine solche Änderung registriert haben, über die entsprechende Änderung informiert. Die Anwendungen können darüber entscheiden, wie sie auf die Notifikation reagieren. Falls sie nicht unter einer ungewöhnlichen Last leiden, werden sie die Daten von der Stammdatenverwaltung abrufen und in ihrer eigenen Datenbank speichern. Dieses Vorgehen hat sich ausgezahlt. Wir konnten sowohl die geforderte Geschwindigkeit als auch die geforderte Stabilität erreichen. Allerdings mussten wir Priorisierungen für bestimmte Nachrichten einbauen – wie z. B. der Entzug bestimmter Rechte – um die Geschäftslogik wie gewohnt zu unterstützen. Aber durch die Entkopplung der beiden Systeme wurde die Stabilität der Anwendungen wesentlich erhöht, wenn auch die Gesamtkommunikation komplexer als zuvor war.

Online Deployment

Durch die erreichte Stabilität hatten sich auch die Ansprüche unserer Kunden erhöht. Große Produktivstellungen 3-mal im Jahr mit geplanten Nicht-Verfügbarkeiten waren nicht mehr zeitgemäß. Wir brauchten eine Lösung, in der wir die Robustheit des Systems auf der einen und die Verfügbarkeitsanforderungen der Kunden auf der anderen Seite sicherstellen konnten. Wir haben uns zu dieser Zeit für einen Blue-Green-Ansatz mit einer Datenbank entschieden (vergl. Abb. 6).

Beide Zweige sind auf der gleichen Version. Als ersten Schritt wird die Datenbank online auf die neue Version gehoben. Voraussetzung ist, dass die notwendigen Datenbankänderungen und die gewählte Datenbanktechnologie dies zulassen. Ist die Datenbank auf der neuen Version, kann die Anwendung auf die neue Version gehoben werden. Interne Benutzer, die spezifisch auf den blauen Zweig zugreifen können, können kurze Smoke-Tests durchführen. Gleichzeitig kann der blaue Zweig auch automatisch Ende-zu-Ende getestet werden. Waren diese Tests erfolgreich, werden beide Zweige grün. Das heißt, Benutzer können sowohl auf die alte Version als auch auf die neue Version zugreifen. Dabei werden neu angemeldete Benutzer immer auf die neue Version geleitet, so dass die Benutzer auf der alten Version langsam auslaufen. Sind keine oder nur noch wenige Benutzer auf der alten Version, wird diese "blau". Das heißt, externe Benutzer können nicht mehr auf diesen Zweig zugreifen. Als letzten Schritt wird der nun der blaue Zweig auf die Version gehoben.

Ein solcher Ansatz erlaubt es, dass Benutzer immer Zugriff auf die Anwendung haben. Benutzer verlieren keine Eingaben, weil sie erst nach einer Neuanmeldung auf die neue Version zugreifen. Bei Fehlern in der neuen Version kann schnell und problemlos auf die alte Version zurückgeschaltet werden.

Voraussetzung für einen solchen Ansatz ist es, dass die Änderungen in der Software nicht zu komplexen Deployments führen. Änderungen an solchen komplexen Systemen sind in der Regel dann komplex, wenn sie zu groß werden, weil man zu lange ohne Produktionsgang entwickelt hat. Daher erlaubt das Online-Deployment nicht nur kürzere Releasezyklen sondern verlangt sie regelrecht.

Die größeren Infrastrukturkosten, die sich durch die Verdopplung der Serverknoten ergeben, werden in der Regel durch die Einsparung an Wochenendarbeiten und Sondereinsätzen bei Nichtverfügbarkeiten eingespart.

Entkopplung der Geschäftslogik

Nachdem wir Stabilität erreicht hatten und die langen Produktivstellungen Geschichte waren, mussten wir dennoch wieder mit erhöhten Instabilitäten rechnen, wenn weitere Last auf unsere Systeme kommen sollte. Diese erhöhten Lasten waren mit zunehmender Geschäftstätigkeit abzusehen. Der einzige Weg war es, die Geschäftslogik weiter zu enkoppeln. Die höchste Last war auf der Ausschreibungskomponente zu verzeichnen. Daher war es ein logischer Schluss, diese aus dem Anwendungsstapel zu entkoppeln. Wir mussten aber feststellen, dass die Ausschreibungskomponente sehr eng mit der Dokumentenkomponente gekoppelt war. Daher konnten wir nur beide zusammen aus dem Stapel herauslösen. Allerdings konnten wir nachweisen, dass wir höhere Stabilitäten nur mit getrennten Datenbanken erreichen. Daher haben wir auch die Datenbanken getrennt. Die sich daraus ergebende Architektur ist in Abb. 7 dargestellt.

Die Komponentenentkopplung brachte aber auch eine weitere Schwachstelle des ursprünglichen Designs zum Vorschein. Gemeinsame Funktionalitäten waren in Zentralen Komponenten zusammengefasst. Diese Zentralen Komponenten wurden durch alle Geschäftskomponenten genutzt, wobei auch vereinzelte Funktionalitäten Zugang zu diesen Funktionen fanden. Es spielte immer die Hoffnung mit, diese später einmal noch irgendwo anders nutzen zu können. Das Ergebnis war eine enge Kopplung aller Komponenten "durch die Hintertür". Diese enge Kopplung mussten wir auflösen.

Die in der Zwischenzeit nicht unerhebliche Anzahl von Servern und damit die zahlreichen Aktivitäten während einer Produktivstellung, verlangten nach weiterer Automatisierung. Wir bauten automatische Build-Pipelines mit weiterer Testautomatisierung. Die Deployment-Automatisierung war ein weiterer Treiber, die enge Kopplung durch die Zentralen Komponenten aufzulösen. Die Zentralen Komponenten wurden mit strenger Versionskontrolle in das automatische Bauen der Anwendungen einbezogen. So haben wir automatische Deployments bis zur Produktion erreicht, wobei Produktivstellungen manuell durch Verantwortliche im Vieraugenprinzip freigegeben werden mussten.

Weitere Zerkleinerung

Wir hatten zu dieser Zeit schon viel erreicht. Endlich konnten wir proaktiv die Architektur bestimmen. Wir wussten, dass Zugriffe auf das System weiter ansteigen würden. Wir hatten die Möglichkeit, den Stammdaten-Cluster weiter horizontal zu skalieren. Aber dann hätten wir auch die Komponenten Stammdaten und Lieferantenmanagement skaliert. Diese Komponenten erzeugten aber im Vergleich wenig Last. Die Komponente, die leicht und möglichst automatisch skalieren musste, war die Loginkomponente. Ziel war es, abhängig von der Last die Loginkomponente automatisch skalieren zu können. Folglich haben wir sie herausgelöst. Die Benutzerverwaltung wanderte damit zur Stammdatenverwaltung Auch die Loginkomponente bekam eine eigene Datenbank. Sie war damit die erste Komponente, die den Namen Microservice verdient hatte.

Alles viel zu teuer?

Der hier vorgestellte Weg sieht auf den ersten Blick sehr teuer aus. Aus 4 XXL-Servern am Anfang wurden am Ende 16 S- und 8 L-Server. Diese Server wurden dann auch noch im Blue-Green-Ansatz gedoppelt. Die Architektur in diesem Zustand ist in Abb. 9 dargestellt.

Diese Mehrkosten konnten durch massive Kosteneinsparungen gegenfinanziert werden. Die Einsparungen haben hierbei nicht nur die Mehrkosten kompensiert, sondern führten zu weiteren Kosteneinsparungen. Zum einen sind Serverkosten nicht linear. Das heißt vier S-Server sind nicht so teuer wie ein XXL-Server. Die wesentlichen Kosteneinsparungen erklären sich aber durch Einsparungen in den Personalkosten. Die wesentlichen Kostentreiber waren die Wochenendarbeit für Produktivstellungen und Arbeit außerhalb normaler Arbeitszeiten für Fehlersuchen und Störungsbehebungen. Diese konnten komplett vermieden werden. Dadurch war eine sehr schnelle Rentabilität erreichbar. Letztlich konnten die Infrastrukturkosten auf ca. 50 Prozent der Anfangskosten gesenkt werden.

Ausblick

Weiter hatten wir große Anwendungen, die den Namen Monolithen wirklich verdienten. Selbst kleinere funktionale Änderungen waren sehr teuer und risikoreich. Sie weiter zu entkoppeln machte aber wenig Sinn, da sie sehr eng gekoppelt waren und eine Entkopplung viel zu teuer war. Daher lag die Entscheidung nahe, diese Monolithen neu zu implementieren. Aus den genannten Erfahrungen war auch klar, dass ein erneuter monolithischer Ansatz nicht erfolgreich sein kann. Ein Ansatz mit Microservices in der Cloud lag sehr nahe. Voraussetzung für einen solchen Schritt ist eine gute Analyse der Geschäftsprozesse und damit ein sinnvoller Schnitt der Services.

Zusammenfassung

Die Notwendigkeit zum Zerschlagen eines Monolithen ist nicht immer gegeben. Wie der vorliegende Bericht zeigt, muss es gute Gründe geben, um einen Monolithen zu zerschlagen: Instabilitäten, zu hohe Entwicklungskosten, extrem beschränkte Möglichkeiten zur Automatisierung usw. Microservices müssen aber entlang des Geschäftsprozesses geschnitten werden, um die Vorteile zum Tragen kommen zu lassen. Die Verbesserung dieser Herausforderungen muss gegen die wachsende Komplexität abgewogen werden.

Microservices sind ein empfehlenswerter Weg, um nichtfunktionale Anforderungen zu erfüllen. Um die wachsende Komplexität zu beherrschen, muss in einem hohen Maß automatisiert werden. Am Ende muss man zwischen Stabilität, Aufwänden für die Automatisierung und der Beherrschung einer erhöhten Komplexität im Betrieb abwägen.

Quellen
  1. M. E. Conway: How Do Committees Invent? In: F. D. Thompson Publications, Inc. (Hrsg.): Datamation. Band 14, Nr. 5, April 1968, S. 28–31, 
    Wikipedia: Gesetz von Conway

Autorin

Dr. Annegret Junker

Dr. Annegret Junker ist Senior Software Architekt bei der adesso AG. Sie arbeitet seit mehr als 25 Jahren in der Software-Entwicklung in unterschiedlichen Rollen und unterschiedlichen Domänen wie Automotive und FinTech.
>> Weiterlesen
Das könnte Sie auch interessieren
botMessage_toctoc_comments_9210