Skip to content

SS2019: Technologierecherche – Microservices

S. Huschke edited this page May 28, 2019 · 7 revisions

Hauptaspekte von Microservices

Definition

Beschreibt ein Architekturmuster für komplexe Anwendungssoftware. Diese ermöglichen einen Modularen Aufbau durch voneinander entkoppelte Dienste. D.h. sie beschreiben einen Architekturstil für Software, die aus unabhängigen Prozessen komponiert wird, die untereinander über sprachunabhängige Programmierschnittstellen kommunizieren. „Do one thing and do it well“ - Jeder Microservice erledigt nur eine kleine Aufgabe. So kann der Aufwand für einen Microservice auf ein Team verteilt werden und dieser ist trotzdem noch überschaubar für dieses.

Vorteile

  • Starke Modularisierung
  • Ersetzbarkeit
  • Nachhaltige Entwicklung
  • Ergänzung Legacy-Systemen (Wachsende Systeme)
  • Skalierbarkeit
  • Technologiefreiheit
  • Continuous Delivery (Validierungspipeline)

Nachteile

  • Abhängigkeiten sind versteckt
  • Refactoring schwierig (Strukturverbesserung)
  • Fachliche Architektur wichtig
  • Betrieb ist komplex
  • Verteilte Systeme sind komplex
  • Performance und Zuverlässigkeit schwieriger umsetzbar

Projektmanagement

Das Projektmanagement hinter Microservices lässt sich durch das Gesetz von Conway erläutern. Dies besagt: „Organisationen, die Systeme designen, können nur solche Designs entwerfen, welche die Kommunikationsstruktur dieser Organisation abbilden.“

Anders als bei klassischen Monolith-Projekten empfiehlt der Autor des Buches "Microservices" Eberhard Wolff, dass Teams nicht mehr in die klassischen Kategorien Frontend, Backend, Datenbanken aufgeteilt werden, sondern jedes Team einen bestimmten Teil der Architektur entwirft - also ein Team pro Microservice. Das Modularisierungskonzept der Microservice-Architektur zeigt sich somit auch im Projektmanagement. Da die Architektur an Fachlichkeiten ausgerichtet ist, hält sich die technische und fachliche Koordination niedrig und die Kommunikation zwischen den Teams wird verringert. Dadurch kann ebenfalls einfacher an konkreteren Features gearbeitet werden. (Stichwort: Git-flow) Wie später noch vertieft wird, zeigen sich Probleme in der Architektur meist auch dadurch, dass die Kommunikation in der Organisation zu nimmt.

Domain-Driven Design

Das Domain-Driven Design von E. Evans ist eine Patternsprache, die die Entwicklung von komplexen Systemen unterstützt. Sie hilft bei der Strukturierung nach Fachlichkeiten und nutzt allgemeingültige Sprachen, um die Verbindungen zwischen Kontexten in einer Software-Architektur aufzuzeigen und zu organisieren. Dabei nutzt sie ein Domänenmodell als Basis. Dieses besteht aus den Building Blocks und dem Bounded Context. Die Building Blocks bezeichnen Entitäten, Wertobjekte, Aggregate, Assoziationen, Ereignisse und Module. Ein Bounded Context stellt die Verbindung verschiedener Domänenmodelle durch ein Strategic Design dar.

Als Beispiel können wir ein E-Commerce Beispiel nutzen: Es gibt einen Microservice für Bestellungen, einen für die Lieferung und einen für die Rechnung. Die Komponente ist für den Bestellprozess zuständig, Lieferung implementiert den Lieferprozess und der MS für Rechnungen ist für das Erstellen der Rechnung zuständig. Jeder dieser Bounded Context ist nun an Daten des Kunden interessiert. Jeder Bounded Context hat sein eigenes Modell des Kunden. Ein universelles Modell für den Kunden wäre hier nicht sinnvoll, weil es alle Informationen aus allen Bounded Contexts enthalten müsste. Da jede Änderung das gesamte System betreffen würde, müsste das System ständig verändert werden. Daher ist es einfacher Basisinformationen der Kunden zwar in einem getrennten Bounded Context zu speichern und die Bounded Contexts miteinander kooperieren zu lassen.

Um die "Abhängigkeiten" zwischen den Bounded Contexts darzustellen, kann eine Context Map erstellt werden.

Beispiel_ContextMap_Wolff Beispiel Context Map - Bestellvorgang


Architektur

  • Fachliche Architektur im Vordergrund

  • Microservices sollen in Fachlichkeiten aufgeteilt werden, die abgeschlossene Funktionalität darstellen

  • Abhängigkeiten managen:

    Extern: Lose Kopplung

    Intern: Hohe Kohäsion

  • Probleme in der Architektur zeigen sich oft auch in der Kommunikation der Organisation

  • Architektur muss änderbar sein, Änderungen müssen so früh wie möglich besprochen werden

  • START BIG - Dann in kleine Services aufteilen

Konfiguration

Die Konfiguration von Microservices-Systemen ist aufwendig, da jedem Microservice entsprechende Konfigurationsparameter mitgegeben werden müssen.

Werkzeuge mit Apache-Lizenz

  • Zookeeper Zookeeper ist ein zentralisierte Dienst für verteilte Systeme, der verwendet wird, um einen verteilten Konfigurationsdienst, Synchronisierungsdienst und eine Namensregistrierung für große verteilte Systeme bereitzustellen.
  • Etcd etcd kommt aus dem Docker/CoreOS Umfeld und bietet eine HTTP-Schnittstelle mit JSON. Ähnlich, wie Zookeeper bietet etcd ein konsistentes Datenmodell und kann für die verteilte Koordination oder auch Locking genutzt werden.
  • Spring Cloud Config Spring unterstützt die Versionierung der Daten, die verschlüsselt werden können und aus einem Git-Backend gezogen werden können. Das Framework ist in das Java-Framework Spring integriert und hat eine REST-Api.

Service Discovery

Service Discovery stellt sicher, dass die MS sich hinsichtlich der Deployments gegenseitig finden. Jede größere MS-Architektur sollte ein Service Discovery System nutzen. Dabei ist es zusätzlich für Load Balancing Features von Vorteil. (Beispiel: Die einfache Namensauflösung von Hosts ist bereits eine einfach Service Discovery)

Werkzeuge

  • Bind Bind ist ein Open-Source-Programmpaket, dass für die Namensauflösung im DNS genutzt wird. Es läuft auf verschiedenen Betriebssystemen und ist in C geschrieben.
  • Consul Consul ist ein Key/Value Store und kann neben konsistenten Abfragen auch die Verfügbarkeit prüfen und optimieren. Clients können sich beim Server registrieren und auf Events reagieren. Es hat ebenfalls ein JSON-Schnittstelle und steht unter der Mozilla-Open-Source Lizenz.
  • Eureka Eureka ist von den Netflix-Machern und steht unter der Apache-Lizenz. Dieses ist ein REST-basierter Server, bei dem sich die Services registrieren können. Jeder Service kann seinen Namen einer URL zuordnen. So können die Services sich gegenseitig REST-Nachrichten schreiben. Außerdem unterstützt es die Replikation auf mehrere Server und Caches auf dem Client. (Ausfallsicherheit) Eureka überwacht die einzelnen Server und entfernt sie, falls diese nicht mehr verfügbar sind. Eureka unterstützt Amazon Web Services und kann einfach mit der Amazon-Skalierung kombiniert werden.

Load Balancing

Load Balancing beschreibt die Lastaufteilung. Das bedeutet, wie Anfragen auf die Microservices verteilt werden. Durch Messaging Lösungen/Systeme können einfach mehrere Instanzen eines MS registriert werden, auf die die Last dann aufgeteilt werden kann. Jeder Service kann so unabhängig von einander skaliert werden. Für jeden Microservice sollte es einen Load Balancer geben, da ein zentraler Load Balancer einen Flaschenhals erzeugen könnte. Nachrichten können dann per Pub/sub oder Point-to-Point versendet werden.

Werkzeuge

  • Apache https - mod_proxy_balancer
  • Nginx - Webserver als Load Balancing nutzen
  • Amazon Cloud
  • Client seitiges Load Balancen Gibbon

Skalierbarkeit

Um mit einer hohen Last zurecht zu kommen, müssen Microservices skalierbar sein. Skalierbarkeit bedeutet, dass ein System mehr Last bearbeiten kann, wenn es mehr Ressourcen bekommt. Dabei wird zwischen der horizontalen und vertikalen Skalierbarkeit unterschieden.

  • Horizontale Skalierbarkeit: Bei der Horizontalen Skalierbarkeit wird die Anzahl der Ressourcen erhöht, die die Last bearbeiten sollen.
  • Vertikale Skalierbarkeit: Bei der Vertikalen Skalierbarkeit werden leistungsfähigere Ressourcen genutzt, die Anzahl der Ressourcen bleibt gleich.

Microservices nutzen meistens horizontale Skalierbarkeit. Um die Last mittels Load Balancing auf verschiedene Instanzen zu verteilen, müssen Microservices zustandslos sein. D.h. keinen Zustand haben, der nur für einen Nutzer gilt. Das System muss sich nun der Last anpassen. Wenn die Antwortzeit einen MS zu hoch ist, oder zu viele Anfragen eingehen, wird als automatisierten Prozess eine neue Instanz gestartet. Dazu wird z.B. Folgendes genutzt.

  • ein neuer Docker-Container gestartet werden oder
  • durch dynamische Skalierung über Service Discovery Systeme

Sicherheit

In einer Microservice-Architektur muss jeder Service wissen, wer einen Aufruf ausgelöst hat oder das System nutzen möchte. Dazu sollte eine einheitliche Sicherheitsarchitektur existieren. Dazu müssen Aspekte der Authentifizierung und der Autorisierung genutzt werden. Die Authentifizierung überprüft Benutzername und Passwort. Die Autorisierung hingegen prüft, welche Benutzergruppen, welche Features nutzen darf. Die Autoren des Buches "Microservices" empfehlen hierfür die Nutzung eines zentralen Servers für die Autorisierung und die Verwendung eines Tokens, um den Zugriff auf einzelne Microservices zu regeln.

Eine Lösung wäre die Verwendung von OAuth2 und JSON Web Token (JWT). OAuth2 ist ein Protokoll, das im Internet weit verbreitet ist. Es gibt viele Bibliotheken für alle gängigen Programmiersprachen. Mit JWT müssen ausschließlich die Token zwischen den MS übertragen werden, wenn REST genutzt wird.

alt

Andere Werkzeuge:

  • Kerberos
  • SAML / SAML 2.0
  • Selber machen über: Signierte Cookies und kryptografische Signatur
  • Spring Cloud Security
  • Hashicorp Vault

Dokumentation und Metadaten

Da Microservice-Architekturen sehr schnell, sehr groß werden können, müssen bestimmte Informationen über einen Microservice für alle verfügbar sein. Daher muss in der Architektur bereits definiert werden, wie Microservices bestimmte Informationen zur Verfügung stellen. Solche Informationen sind z.B. Name des Services, Informationen über den Source Code, verwendete Bibliotheken, mit welchen anderen MS zusammengearbeitet wird und Konfigurationsparameter. Bei der Dokumentation sollte darauf geachtet werden, dass eine einheitliche Sprache genutzt wird und die Informationen immer aktuell gehalten werden.


Integration und Kommunikation

Microservices müssen auf unterschiedlichen Ebenen integriert werden und kommunizieren miteinander. Dies findet auch verschiedenen Ebenen statt. Die erste Ebene ist für den Client bestimmt. Dort kann die Integration über eine Web-Schnittstelle über verschiedene Möglichkeiten stattfinden. Für die Kommunikation zwischen den Microservices gibt es verschiedene Lösungsansätze, wie REST oder Messaging. Die Ebene der Datenbank wird durch die Aufteilung der MS in Bounded Kontexts bestimmt. Als Letztes sind Schnittstellen eine wichtige Grundlage für die Kommunikation. Dabei sind Aspekte, wie Änderbarkeit, Routing und das Robustheitsprinzip zu beachten.

Microfrontends vs Monolith Frontends

Die Autoren des Microservices-Buches empfehlen, dass jeder Microservice seine eigene UI mitbringen solle. Die Gründe dafür sind nicht nur der Konsistenz des Architektur-Stils zuzuschreiben.

Die Autoren unterscheiden außerdem folgende Ansätze:

  • Single-Page-App(SPA) pro Microservice: Für MS mit SPAs kann jeder Microservice seine eigene SPA mitbringen. Die SPa kann den Microservice z.B. über JSON/REST ansprechen und ist mit Javascript einfach umsetzbar. Zwischen den SPAs kann ein Link genutzt werden. Dadurch sind die SPAs völlig getrennt und unabhängig. Eine enge Integration wird jedoch schwierig. Mittels eines Assetservers kann die Einheitlichkeit der verschiedenen SPAs sicher gestellt werden. Dadurch entstehend wiederum neue Abhängigkeiten zwischen dem Asset-Server und allen Microservices. Solche Abhängigkeiten sollten nicht nur im Backend vermieden werden. Außerdem muss eine einheitliche Authentifizierung und Autorisierung gewährleistet werden.

  • Eine SPA für alle MS: Die Aufteilung in mehrere SPAs ergibt eine strikte Trennung der Frontends der Microservices. Die SPA wird dazu in Module aufgeteilt, die zu verschiedenen Microservices gehören. Die Module werden getrennt deployt und können z.B. in eigenen Javascript-Dateien auf Servern liegen. AngularJS z.B. nutzt ein Modul-Konzept, mit dem einzelne Teile der SPA in getrennte Einheiten implementiert werden können. Dieses Vorgehen hat jedoch Nachteile: Das Deployment der SPA ist meistens nur als vollständige Anwendung möglich. Wenn ein Modul geändert wird, muss die gesamte SPA neu ausgeliefert werden. Das Deployment der Microservices auf dem Server muss mit dem Deployment der Module koordiniert werden. Dadurch ist eine wesentlich engere Abstimmung nötig.

  • HTML Anwendungen: Eine andere Möglichkeit sind HTML-basierte Oberflächen. Dabei hat jeder MS eine oder mehrere Webseiten. Beim Wechsel zwischen den Seiten wird eine neue HTML-Seite vom Server geladen. Dazu kann die Resource Oriented Client Architecture (ROCA) genutzt werden.

  • Frontend-Server: Die letzte Möglichkeit bietet einen Fronteid-Server, der die Seite aus verschiedenen HTML-Schnipseln zusammenfügt, die jeweils von einem MS erzeugt werden. Dazu gibt es die sogenannten Edge-Side Includes (ESI). Diese bieten eine Sprache, um HTML aus verschiedenen Quellen miteinander zu kombinieren. Damit können Cache statischen Content (z.B. ein Skelett der Seite) um dynamischen Content ergänzen. (Werkzeuge: Varnish, Squid) Eine andere Alternative sind Server Side Includes (SSI). Der einzige Unterschied zu ESI ist, dass sie nicht in Cache umgesetzt werden, sondern in Webservern. Die MS könnten so Bestandteile der Seite liefern, die dann auf dem Server zusammen gestellt werden. (Werkzeuge: Apache https - mod_include, ngingx - nix_http_ssi_module)

REST

Damit die Microservices auch untereinander Code aufrufen können, bietet REST Eine Möglichkeit Kommunikation zwischen den Microservices zu ermöglichen. Näheres dazu bietet das Lernportal Codeacademy

Das Team um innoq hat zu Rest einen interessanten Artikel geschrieben, in dem sie zusätzlich die positiven und negativen Eigenschaften von REST aufzeigen, um selbst abzuwägen, wann man REST nutzen sollte und wann nicht. innoq_auswertung Rest innoq_Auswertung Rest

Messaging

Eine weitere Möglichkeit für die Kommunikation zwischen Microservices sind Messaging-Systeme. Diese basieren darauf, Messages zwischen Services zu verschicken. Gerade bei verteilten Systemen haben Messaging-Systeme mehrere Vorteile. Nachrichten können z.B. auch bei Ausfällen des Netzwerkes übertragen werden, da Das System diese zwischen speichert. Außerdem können sie die korrekte Übertragung und Verarbeitung der Nachricht gewährleisten. In einer Messaging-Architektur werden Nachrichten asynchron übertragen und bearbeitet. Das bedeutet, die Nachricht wird an eine Message-Queue gesendet und von dort weiter verarbeitet. Dadurch sind Sender und Empfänger voneinander entkoppelt. Messaging-Systeme sind ebenfalls eine gute Basis für die Nutzung von Event-Drives Architektur.

Werkzeuge

  • AMQP - Advanced Message Queuing Model ist ein Standard, das ein Protokoll für Messaging-Lösungen implementiert werden. Eine Implementierung bietet RabbitMQ.
  • Apache Kafka zeigt seine Stärke in oben Durchsatz, Replikation und Ausfallsicherheit.
  • 0MQ ist sehr leichtegewichtig, da es ohne eigenen Server arbeitet.
  • JMS - Java Messaging Service ist eine API, mit Nachrichten innerhalb einer Java-Anwendung verschickt und empfangen werden können.

Datenreplikation

Auf Datenbankebene können Microservices sich eine Datenbank teilen und so gemeinsam auf Datenbankbestände zugreifen. Der große Nachteil dabei ist jedoch neben dem grundsätzlichen Verstoß gegen das Architekturprinzip selber, dass die Repräsentation der Daten nicht ohne Weiteres geändert werden kann. Dadurch sind diese schlechter und langsamer wart- und änderbar. Eine mögliche Alternative ist die Replikation von Daten und die Nutzung von Schemata in einer gemeinsam genutzt Datenbank. Dabei darf es keine Beziehungen zwischen den Schemata geben und die Replikation darf keine Abhängigkeit der Datenbankschemata einführen. Durch die Replikation entsteht jedoch eine redundante Speicherung der Daten. Das bedeutet, es dauert einige Zeit, bis Änderungen überall repliziert wurden. Um Lese- und Schreibfehler zu vermeiden, empfehlen die Autoren des Buches die eigene Implementierung der Replikation.

Schnittstellen

Jedes MS-System hat verschiedene Arten von Schnittstellen. Dabei gibt es interne Schnittstellen zwischen den Microservices und externe Schnittstellen, die Entwickler außerhalb der Organisation nutzen können. Die Änderungen an Schnittstellen bringen immer auch Aufwand für das Team oder die Teams mit sich. Tests können dabei helfen, dass Schnittstellenänderungen abgesichert werden. Wenn ein MS eine Schnittstelle ändern, sollte auch die alte Version noch verfügbar sein. Dadurch müssen die Microservices, die von der alten Schnittstelle abhängen nicht sofort neu deployt werden. Schnittstellen sollen sich nach dem Robustheitsprinzip verhalten. Dies besagt, dass alle Komponenten bei der Nutzung von anderen Komponenten sich an die Vorgaben für diese halten sollen, aber bei Fehlern bei der eigenen Nutzung diese kompensieren. Dies verbessert die Interoperabilität.