Microservices: Konzept, Vorteile und Umsetzung
- Website
Microservices sind ein modernes Element in der Systemarchitektur. Sie sollen große Prozesse in kleinere Aufgaben zerlegen und so unter anderem den Wartungsaufwand verringern und die Performance verbessern. Wir zeigen Ihnen, was genau Microservices sind und wann sie angewendet werden sollten.
Was sind Microservices?
Wenn man eine Definition haben möchte, die in vielen Fällen unverständlich daherkommt, fragt man gerne Wikipedia: “Microservices sind ein Architekturmuster der Informationstechnik, bei dem komplexe Anwendungssoftware aus kleinen, unabhängigen Prozessen komponiert wird, die untereinander mit sprachunabhängigen Programmierschnittstellen kommunizieren. Die Dienste sind klein, weitgehend entkoppelt und erledigen eine kleine Aufgabe. So ermöglichen sie einen modularen Aufbau von Anwendungssoftware.”
Aha. So weit, so unverständlich – zumindest für Nicht-Informatiker. Unabhängig davon, was nun genau Microservices sind, fällt einem in der Beschreibung von Wikipedia auf, dass das Microservices Muster wohl nur bei großen Anwendungen zum Einsatz kommt. Und das ist auch richtig. Bei kleineren Anwendungen macht dieser Ansatz wenig Sinn: Die Microservice Architektur erzeugt einen gewissen Overhead und fügt der Anwendung Komplexität hinzu. Dies macht bei mittelgroßen Anwendungen wenig Sinn, da der Aufwand für die Implementierung von Microservices nicht im Verhältnis zu der Projektgröße steht.
Das Microservices Muster ist also ein Konzept, eine Strukturvorgabe, eine Blaupause um große komplexe Anwendungen zu entwerfen. Dabei wird die Anwendung aus kleineren, im Idealfall voneinander unabhängigen Prozessen, die eigenständige Anwendungen sind, zusammengesetzt. Das Zusammensetzen nennt man in diesem Fall Komposition. Letztlich wird die Anwendung in kleinere, voneinander unabhängige Anwendungen (Microservices) aufgeteilt.
Das Pendant zu Microservices ist die monolithische Architektur: Bei letzterer besteht die Anwendung aus nur einem Prozess – und so sind auch die meisten Anwendungen bis dato konzipiert. Die monolithische Architektur ist also der Normalfall.
Microservices kurz zusammengefasst:
- Microservices sind keine Technologie oder Sprache, sondern ein Muster (Pattern).
- Nur im Kontext von komplexen Anwendungen sind Microservices sinnvoll. Bekannte Beispiele, die die Microservices Architektur intensiv nutzen, sind Netflix, Amazon und eBay.
- Eine große Anwendung wird durch das Microservices Muster in kleinere Anwendungen zerlegt. Die einzelnen Anwendungen erledigen eine für sich abgeschlossene Aufgabe, z.B. Produktkatalog, Suche, Accounting.
- Micorservices sind insofern ein Ansatz, ein Versuch der Frage beizukommen, wie man mit der zunehmenden Größe und Komplexität von modernen Anwendungen umgeht, um weiterhin effektiv und effizient zu arbeiten.
Vor und Nachteile von Microservices
Die meisten offensichtlichen Vorteile wurden bereits erwähnt. Abschließend noch ein paar Punkte, die vielleicht nicht sofort jedem klar sind:
- Wenn ein einzelner Microservice abschmiert, betrifft das nicht gleich die gesamte Anwendung. Beim monolitischen Ansatz ist die Verfügbarkeit der gesamten Anwendung nicht mehr gewährleistet.
- Der Technologie-Stack ist nicht gesetzt. Einzelne Microservices können in jeder beliebigen Sprache implementiert werden, da sie letztlich eigene kleine Anwendungen sind und über z.B. REST Schnittstellen mit anderen Microservices kommunizieren können.
- Neue Entwickler können sich schneller einarbeiten.
Wie so oft gibt es auch hier eine Kehrseite der Medaille:
- Die Entwicklung von verteilten Systemen (und genau das ist der Ansatz von Microservices) kann sehr komplex sein. Die Tatsache, dass alle Komponenten, die normalerweise in einem Prozess laufen (z.B. Authentifizierung, Payment Service, …), nun unabhängige Anwendungen sind, erhöht die Anforderungen an die Kommunikation. Was ist, wenn eine Anwendung über das Netzwerk nicht erreichbar ist? Latenzzeiten können ebenfalls zum Problem werden.
- Grundsätzlich können verteilte Datenbanken und das entsprechende Transaktionsmanagement sehr unangenehm sein. 🙂
- Das Testen einer Micorservice basierten Anwendung ist komplexer. Jeder abhängige Service muss hochgefahren und verifiziert werden. Das kann schnell anstrengend werden, wenn über 100 Services ihr Unwesen treiben.
- Das Deployen ist natürlich im ersten Schritt auch komplexer im Vergleich zum monolitschen Ansatz, wo nur eine Anwendung deployed wird. Nun müssen die einzelnen Microservices untereinander koordiniert werden.
Allen genannten Nachteilen ist jedoch mit den entsprechenden Tools und einer cleveren Automatisierung beizukommen. Das ist jedoch alles andere als trivial und muss gut geplant werden.
Grundgerüst erzeugen mit JHipster 3.0
Nun haben wir uns die Konzepte sowie einige Vor- und Nachteile der Microservice Architektur angeguckt. Jetzt wollen wir Schritt für Schritt eine Applikation erzeugen, die viele Paradigmen der Microservice Architektur anwendet. Dabei setzen wir diverse Tools wie z.B. docker ein, die uns das Leben erleichtern. Das Grundgerüst erzeugen wir mit JHipster 3.0, einem Generator auf Basis von yeoman.io. Für alle Anforderungen im Bereich Virtualisierung kommt docker zum Einsatz.
Damit Ihr das Beispiel auf eurem System nachvollziehen könnt, sollten NodeJS, Java SDK und docker installiert und entsprechend konfiguriert sein. Wenn es dazu Fragen gibt, meldet euch gerne bei uns. Öffnet nun die Shell eures Vertrauens und wir legen los.
Installation von yeoman mit npm:
npm install -g yo // -g steht für global npm install -g generator-jhipster
Die Kommandos installieren yeoman und den JHipster Generator. Nun brauchen wir noch eine geeignete Verzeichnisstruktur. Unsere Applikation besteht aus einem gateway, der auf Netflix Zuul basiert und ein AngularJS Frontend beinhaltet. Momentan wird für das Frontend noch AngularJS 1.x verwendet. Das Team von JHipster ist allerdings daran, AngularJs 2.0 zu integrieren, und wird dies mit JHipster 4.0 hoffentlich bald veröffentlichen. Ferner enthält unsere Applikation noch zwei unabhängige Microservices: authorization und catalog. Der letzte Ordner docker beinhaltet alle deployment Informationen.
mkdir jhipster-app && cd $_ mkdir gateway authorization-service catalog-service docker
Als erstes erzeugen wir den authorization service.
cd authorization-service yo jhipster
Der Generator stellt dir einige Fragen, die wir wie folgt beantworten: Wir möchten eine Microservice Applikation erstellen, die auf Port 8081 mit einer Standard SQL Datenbank läuft. Caching und Testing brauchen wir fürs erste nicht. Für den Einstig müssen wir es uns nicht komplizierter machen, als es ohnehin schon ist. Nachdem ihr alle Fragen beantwortet habt, generiert JHipster ein Grundgerüst für die Applikation.
Okay – nun haben wir viel Code, aber noch keine Tabellen in der Datenbank. Wir wollen es einfach halten, dementsprechend sieht unsere Tabelle User aus: Sie hat genau eine Spalte – “Name” – vom Typ String.
yo jhipster:entity User <Fragen> ./gradlew build -Pprod buildDocker
Das gradle Kommando lädt alle notwendigen dependencies (Spring, Hibernate, … ) für die Applikation herunter und baut am Ende daraus ein docker image, das in der lokalen docker registry persistiert wird. Zeit für einen Kaffee; dies kann unter Umständen relativ lange dauern.
Damit ist der erste Service fertig gebaut und wir widmen uns dem catalog-service.
cd .. cd catalog-service yo jhipster <Viele Fragen> yo jhipster:entity Product <Fragen> ./gradlew build -Pprod buildDocker
Der Aufbau ist genau gleich, die Fragen werden wie beim authorization Service beantwortet, nur dass wir für den catalog-service Port 8082 wählen. Okay, nun haben wir zwei Microservices, die unabhängig voneinander gestartet werden und ihre Ressourcen (Tabelle User respektive Product) über eine gesicherte REST Schnittstelle zur Verfügung stellen – aber noch keine GUI, um die Daten anzuzeigen. Dafür brauchen wir den gateway. Letzteren erzeugen wir in unserem gateway Verzeichnis.
cd .. cd gateway yo jhipster <Viele Fragen>
Diesmal entscheiden wir uns für microservice gateway der bitte auf Port 8080 horcht. Wir verwenden eine normale SQL Datenbank und als build tool setzen wir abermals auf gradle.
yo jhipster:entity User <Fragen> yo jhipster:entity Product <Fragen> ./gradlew build -Pprod buildDocker
Als nächstes können und sollten wir die Tabellen aus den eben erstellten Microservices direkt importieren. Der Generator fragt, ob wir die Tabelle aus einem anderen Microservice importieren möchten, und falls ja, wo denn der Microservice liegt (relativ zum aktuellen Pfad, also “../authorization-service bzw. ../catalog-service”).
Nachdem wir beide Tabellen (User und Product) erfolgreich importiert haben, können wir das docker image mit dem bekannten gradle Kommando erzeugen. Voilá! Sind wir jetzt fertig? Leider noch nicht ganz. Um tatsächlich loszulegen und auch etwas Sinnvolles im Browser zu sehen, brauchen wir eine weitere Komponenten aus dem Microservice Universum: JHipster registry. Sie dient als service discovery und configuration server. Die registry basiert auf dem Netflix Eureka Server und ist mit Sicherheit einen eigenen Beitrag wert. Heute aber nur so viel: Die registry dient dazu, den Überblick zu behalten. Welche services sind gerade online (discovery)? Wie verteile ich die Last am effizientesten (Load balancing)? Was passiert, wenn Service x ausfällt (fail over)? Für die Beantwortung solcher Fragen (und noch ein bisschen mehr) ist die registry zuständig.
Starten der gesamten Infrastruktur
Fast geschafft. Als letztes orchestrieren wir unsere Microservices und die registry mit docker-compose. Letzteres ist ein sehr mächtiges Tool, um verschiedene docker container bzw. images zu verwalten und parallel zu starten. Wenn ihr mehr über docker erfahren wollt, dann schaut unbedingt auf docker.io vorbei. Um die Konfigurationsdatei für docker-compose zu erzeugen, sind folgende Schritte notwendig:
cd .. cd docker yo JHipster:docker-compose <Fragen>
Ein letztes Mal antworten wir brav auf die Fragen: Wir wollen eine microservice application deployen, das Hauptverzeichnis befindet sich “../”, also eine Ebene höher, für das monitoring wählen wir JHipster Console und das admin Passwort lassen wir auf “admin”.
Wunderbar. Am Ende erhalten wir folgende Information: “You can launch all your infrastructure by running :docker-compose up -d”
docker-compose up -d
Mit diesem Befehl wird die eben erzeugte docker-compose.yml geladen und entsprechend ausgeführt. Und das kann dauern. Insbesondere wenn der Computer schon etwas älter oder die Internetverbindung nicht die schnellste ist. Nach langen zehn Minuten ist alles fertig und die einzelnen container sind mit ihren images hochgefahren. Welche container gerade aktiv sind, könnt ihr mit dem Kommando docker ps anzeigen lassen.
docker ps
Unter https://localhost:8080 erreicht ihr das Frontend vom gateway. Login ist admin / admin. Die registry ist unter https://localhost:8761 zu erreichen. In der gateway Applikation sollten beide Microservices auftauchen wie im Bild zu sehen. Das gleiche gilt für die registry.
Fazit
Geschafft! Das war zwar erst die Spitze des Eisbergs, aber wir haben nun immerhin eine lauffähige Cloud API mit einfacher Authentifizierung, eine automatisch erzeugte Dokumentation für das API (https://localhost:8080/#/docs) sowie eine komplett implementierte Testabdeckung. Nun sind der Kreativität keine Grenzen gesetzt und ihr könnt euer Produkt auf einer sehr robusten Plattform entwickeln.