Architektur
24.01.2024, 10:49 Uhr
Best Practices für Microservices-Architekturen
Eine Microservices-Architektur dröselt Anwendungen in viele kleine Module auf, die über standardisierte Schnittstellen kommunizierend ein funktionales Ganzes bilden. Klingt einfach, doch beim Aufbau sollten eine Reihe von Regeln beachtet werden.
Jeder Microservice deckt einen bestimmten funktionalen Teilbereich ab, der innerhalb einer Programm-Architektur bereitgestellt wird. Microservices können auf diese Weise unabhängig voneinander entwickelt, getestet und geändert werden. Das muss nicht unbedingt innerhalb der IT-Abteilung passieren. Microservices bringen die IT näher an die Fachabteilungen, wo sie mit Hilfe von Tools wie Low- oder No-Code-Plattformen direkt von den jeweiligen Experten oder Expertenteams aufgesetzt werden können.
Das hat positiven Einfluss sowohl auf die Entwicklungs- und Bereitstellungsgeschwindigkeit, als auch die Praktikabilität der Services und vereinfacht zudem Pflege (Updates, Upgrades) und Wartung (Maintenance). Die in der Vergangenheit von den Fachabteilungen in den Unternehmen (ob nun zu Recht oder Unrecht) häufig kritisierte Nadelöhr-Funktion der IT wird damit obsolet.
Die Pros und Cons von Microservices
Da ein Microservice nur bei Bedarf gestartet wird und einzeln skalierbar ist, werden die zur Verfügung stehenden IT-Ressourcen sehr effizient genutzt. Das teure Vorhalten zusätzlicher Compute- oder Storage-Systeme für Extremfälle entfällt, zumal dafür gegebenenfalls Cloud-Ressourcen zur Verfügung stehen.
Für die Applikationsentwicklung können Abteilungen oder Teams mit ihrem Expertenwissen immer kleinere, spezialisiertere Microservices entwerfen und umsetzen, die dann für alle zur Verfügung gestellt werden. Sollte sich ein Microservice als fehlerhaft oder dysfunktional erweisen, kann er schnell identifiziert und geändert oder abgeschaltet werden, ohne das System negativ zu beeinflussen. Von dieser hohen Fehlertoleranz profitieren die Stabilität und Resilienz der Applikationen im Ganzen.
Diese Vorteile sind allerdings, wie sollte es auch anders sein, nicht zum Nulltarif zu haben. Die Konzeption und Pflege einer Microservices-Architektur ist Sache der IT-Abteilung. Schließlich müssen die vielen in der Unternehmensperipherie entwickelten Microservices ja miteinander arbeiten können.
Damit verschiebt sich ein Teil der Komplexität monolithischer Anwendungen auf die Architekturebene. Eine der anspruchsvollsten Aufgaben ist dabei die Sicherstellung der Datenkonsistenz. Ideal wäre es, wenn die Microservices dafür eine gemeinsame Datenbankplattform nutzen würden. In der Praxis ist das jedoch eher selten der Fall. Microservices nutzen in der Regel unterschiedliche Datenbanken.
Wichtig sind in diesem Szenario besonders robuste Synchronisierungsmechanismen, bis hin zur Cross-Datacenter-Replication (XDCR). Auf der Steuerungsebene darüber muss die wachsende Zahl von Microservices containerisiert, orchestriert und zur Nutzung bereitgestellt werden.
Die bekanntesten Tools dafür sind Docker und Kubernetes. Nicht zuletzt muss eine Microservices-Architektur ständig kontrolliert, und gegebenenfalls eingegriffen werden. Auch aus sicherheitstechnischen Überlegungen heraus sind Monitoring und Troubleshooting eine Daueraufgabe.
Microservices Best Practices
Aus dieser Ausgangssituation heraus hat sich eine Reihe von Best Practices für den Aufbau und den Betrieb einer Microservices-Architektur herausgeschält. Microservices kommunizieren miteinander in der Regel über REST-APIs, Event Streaming oder Nachrichtenbroker. Damit sie ihre Funktion autonom erfüllen können, sollten sie per asynchroner Kommunikation gekoppelt werden, also im Gegensatz zu synchroner Kommunikation zeitlich versetzt.
Dadurch werden gegenseitige Einflüsse etwa bei Modifikationen oder Störungen reduziert. Zur Erhöhung der Fehlertoleranz und damit der Stabilität und Resilienz, empfehlen sich zweitens sogenannte Circuit Breaker. Sie verhindern bei der Fehlfunktion oder beim Ausfall eines Microservices übermäßige Auswirkungen auf andere Services, da sie ihre Anforderungen an ihn beenden. So wird eine Überlastung der Architektur verhindert.
Die dritte Best Practice betrifft die Empfehlung, Microservices bei größeren Änderungen zu versionieren. Das ermöglicht einen einfacheren Wechsel, erleichtert die Problembehebung bei Updates und gewährleistet die Rückwärtskompatibilität.
Jeder dieser Microservices, und damit sind wir bei Best Practice Nummer vier, sollte dem Prinzip der Single Responsibility folgen. Schon beim Design ist dabei darauf zu achten, dass er eine ganz bestimmte Funktion oder Aufgabe übernimmt – nicht weniger und vor allem nicht mehr. Das würde dem grundlegenden Konzept einer verteilen Microservices-Architektur widersprechen und einen kleinen Schritt in Richtung Monolith bedeuten. Das wiederum konterkarierte einige der wichtigsten Vorteile: die hohe Skalierbarkeit und Effizienz individuell bereitgestellter Services.
Klare Grenzen setzen
Im Umkehrschluss gehört zu den Best Practices auch die Definition und Durchsetzung von funktionalen Grenzen für Microservices. Jeder von ihnen sollte sich ausschließlich um eine ganz bestimmte Business-Funktion kümmern, samt den dafür notwendigen Daten.
Auch wenn es hier und da schwer sein mag, diesen Minimalismus durchzuhalten, er ist Grundprinzip und Erfolgsfaktor von Microservices-Architekturen. API-Gateways übernehmen darin die Funktion des Mittlers zwischen den Clients und den Microservices. Sie sind mehr als nur Schnittstellenverwalter, denn sie steuern die Interaktionen der Services, aggregieren deren Antwort und übernehmen die Autorisierung und Authentifizierung.
Je größer die Zahl der Microservices, desto dringlicher wird der Einsatz von Tools zur Diensterkennung (Service Discovery) wie Consul oder Eureka. Sie sorgen dafür, dass Dienste sich in einem Netzwerk dynamisch gegenseitig finden und identifizieren. So wird der Konfigurationsaufwand reduziert und das Systemmanagement erleichtert.
In einer Microservices-Architektur ist es zudem wichtig, dass jeder Service unabhängig bereitgestellt wird. So werden gegenseitige Abhängigkeiten vermieden, die die Bereitstellung verzögern könnten. Auch hier liefern Tools wie Docker oder Kubernetes entsprechende Unterstützung. Lösungen wie ELK Stack oder Prometheus dagegen sind hilfreich beim Log Monitoring der Services. Sie erfassen und analysieren deren Logdaten und stellen eventuelle Anomalien fest.
Der zehnte Punkt betrifft die Software-Entwicklung selbst: Continuous Integration and Deployment (CI/CD) unterstützt die Entwicklung, das Testen und die Veröffentlichung jedes einzelnen Microservices. So werden Fehler konsequent entdeckt, und Microservices können schneller bereitgestellt werden. Entwurfsmuster (Saga Patterns) koordinieren Microservices mit Hilfe von Event- oder Messaging-Tools wie Kafka in Analogie zu der altbekannten Transaktionsverarbeitung aus Datenbanken, die bei einem Fehler Änderungen zurücknimmt und den ursprünglichen Zustand wiederherstellt. So wird die Datenkonsistenz gesichert.
Datenbanken für Microservices
Wie erwähnt kann eine Microservices-Architektur in der Regel nicht auf eine einheitliche Datenbankplattform zurückgreifen. Dagegen spricht schon ihr verteilter, dezentraler Ansatz. Trotzdem gibt es einige Features, die eine Datenbank besonders geeignet für Microservices-Architekturen macht.
So sollte sie etwa ultraschnelle asynchrone Replicas oder Persistent Volumes für Microservices bereitstellen können oder Cross Datacenter Replication in hybriden Architekturen unterstützen. Besonders hilfreich ist ein integrierter Operator, der die operative Komplexität reduziert. Er übernimmt beispielsweise Funktionen zur Automatisierung anspruchsvoller Verwaltungsaufgaben, wie etwa die Konfiguration des Cross Datacenter Replication Managements.
Er sollte neben den Microservices-Anwendungen auf der gleichen Kubernetes-Plattform laufen. So werden Silos vermieden die entstehen, wenn Datenbank-Applikationen getrennt von den Container-basierten Microservices ausgeführt werden, die sie unterstützen.
Quelle: Gregor Bauer