Eberhard Wolff arbeitet seit mehr als fünfzehn Jahren als Architekt und Berater – oft an der Schnittstelle zwischen Business und Technologie. Er ist Fellow bei der innoQ. Als Autor hat er über hundert Artikel und Bücher geschrieben – u.a. über Continuous Delivery – und als Sprecher auf internationalen Konferenzen vorgetragen. Sein technologischer Schwerpunkt liegt auf modernen Architekturansätzen – Cloud, Continuous Delivery, DevOps, Microservices oder NoSQL spielen oft eine Rolle.
Sie können dieses E-Book ebenfalls und kostenlos in der englischen Version hier herunterladen.
|
Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+: www.dpunkt.plus |
Grundlagen, Konzepte und Rezepte
Eberhard Wolff
eberhard.wolff@gmail.com
Lektorat: René Schönfeldt
Projektmanagement: Miriam Metsch
Copy-Editing: Petra Kienle, Fürstenfeldbruck
Satz: III-satz, www.drei-satz.de
Herstellung: Susanne Bröckelmann
Umschlaggestaltung: Helmut Kraus, www.exclam.de
Druck und Bindung: M.P. Media-Print Informationstechnologie GmbH, 33100 Paderborn
Bibliografische Information der Deutschen Nationalbibliothek
Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
ISBN:
Buch 978-3-86490-526-1
PDF 978-3-96088-461-3
ePub 978-3-96088-462-0
mobi 978-3-96088-463-7
1. Auflage 2018
Copyright © 2018 dpunkt.verlag GmbH
Wieblinger Weg 17
69123 Heidelberg
Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.
Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.
Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.
5 4 3 2 1
Einleitung
Teil IArchitekturgrundlagen
1Microservices
2Mikro- und Makro-Architektur
3Self-contained System (SCS)
4Migration
Teil IITechnologie-Stacks
5Docker-Einführung
6Technische Mikro-Architektur
7Konzept: Frontend-Integration
8Rezept: Links und clientseitige Integration
9Rezept: serverseitige Integration mit Edge Side Includes (ESI)
10Konzept: Asynchrone Microservices
11Rezept: Messaging und Kafka
12Rezept: Asynchrone Kommunikation mit Atom und REST
13Konzept: Synchrone Microservices
14Rezept: REST mit dem Netflix-Stack
15Rezept: REST mit Consul und Apache httpd
16Konzept: Microservices-Plattformen
17Rezept: Docker-Container mit Kubernetes
18Rezept: PaaS mit Cloud Foundry
Teil IIIBetrieb
19Konzept: Betrieb
20Rezept: Monitoring mit Prometheus
21Rezept: Log-Analyse mit dem Elastic Stack
22Rezept: Tracing mit Zipkin
23Und nun?
Anhang
AInstallation der Umgebung
BMaven-Kommandos
CDocker- und Docker-Compose-Kommandos
Index
Einleitung
Teil IArchitekturgrundlagen
1Microservices
1.1Microservices: Definition
1.2Gründe für Microservices
1.3Herausforderungen
1.4Independent-Systems-Architecture-Prinzipien (ISA)
1.5Bedingungen
1.6Prinzipien
1.7Bewertung
1.8Variationen
1.9Fazit
2Mikro- und Makro-Architektur
2.1Bounded Context und Strategic Design
2.2Technische Mikro- und Makro-Architektur
2.3Betrieb: Mikro- oder Makro-Architektur
2.4Mikro-Architektur bevorzugen!
2.5Organisatorische Aspekte
2.6Variationen
2.7Fazit
3Self-contained System (SCS)
3.1Gründe für den Begriff Self-contained Systems
3.2Self-contained Systems: Definition
3.3Ein Beispiel
3.4SCS und Microservices
3.5Herausforderungen
3.6Variationen
3.7Fazit
4Migration
4.1Gründe für eine Migration
4.2Typische Migrationsstrategie
4.3Alternative Strategien
4.4Build, Betrieb und Organisation
4.5Variationen
4.6Fazit
Teil IITechnologie-Stacks
5Docker-Einführung
5.1Docker für Microservices: Gründe
5.2Docker-Grundlagen
5.3Docker-Installation und Docker-Kommandos
5.4Docker-Hosts mit Docker Machine installieren
5.5Dockerfiles
5.6Docker Compose
5.7Variationen
5.8Fazit
6Technische Mikro-Architektur
6.1Anforderungen
6.2Reactive
6.3Spring Boot
6.4Go
6.5Variationen
6.6Fazit
7Konzept: Frontend-Integration
7.1Frontend: Monolith oder modular?
7.2Optionen
7.3Resource-oriented Client Architecture (ROCA)
7.4Herausforderungen
7.5Vorteile
7.6Variationen
7.7Fazit
8Rezept: Links und clientseitige Integration
8.1Überblick
8.2Beispiel
8.3Rezept-Variationen
8.4Experimente
8.5Fazit
9Rezept: serverseitige Integration mit Edge Side Includes (ESI)
9.1ESI: Konzepte
9.2Beispiel
9.3Varnish
9.4Rezept-Variationen
9.5Experimente
9.6Fazit
10Konzept: Asynchrone Microservices
10.1Definition
10.2Events
10.3Herausforderungen
10.4Vorteile
10.5Variationen
10.6Fazit
11Rezept: Messaging und Kafka
11.1Message-oriented Middleware (MOM)
11.2Die Architektur von Kafka
11.3Events mit Kafka
11.4Beispiel
11.5Rezept-Variationen
11.6Experimente
11.7Fazit
12Rezept: Asynchrone Kommunikation mit Atom und REST
12.1Das Atom-Format
12.2Beispiel
12.3Rezept-Variationen
12.4Experimente
12.5Fazit
13Konzept: Synchrone Microservices
13.1Definition
13.2Herausforderungen
13.3Vorteile
13.4Variationen
13.5Fazit
14Rezept: REST mit dem Netflix-Stack
14.1Beispiel
14.2Eureka: Service Discovery
14.3Router: Zuul
14.4Lastverteilung: Ribbon
14.5Resilience: Hystrix
14.6Rezept-Variationen
14.7Experimente
14.8Fazit
15Rezept: REST mit Consul und Apache httpd
15.1Beispiel
15.2Service Discovery: Consul
15.3Routing: Apache httpd
15.4Consul Template
15.5Consul und Spring Boot
15.6DNS und Registrator
15.7Rezept-Variationen
15.8Experimente
15.9Fazit
16Konzept: Microservices-Plattformen
16.1Definition
16.2Variationen
16.3Fazit
17Rezept: Docker-Container mit Kubernetes
17.1Kubernetes
17.2Das Beispiel mit Kubernetes
17.3Beispiel im Detail
17.4Weitere Kubernetes-Features
17.5Rezept-Variationen
17.6Experimente
17.7Fazit
18Rezept: PaaS mit Cloud Foundry
18.1PaaS: Definition
18.2Cloud Foundry
18.3Das Beispiel mit Cloud Foundry
18.4Rezept-Variationen
18.5Experimente
18.6Serverless
18.7Fazit
Teil IIIBetrieb
19Konzept: Betrieb
19.1Warum Betrieb wichtig ist
19.2Ansätze für den Betrieb von Microservices
19.3Auswirkungen der behandelten Technologien
19.4Fazit
20Rezept: Monitoring mit Prometheus
20.1Grundlagen
20.2Metriken bei Microservices
20.3Metriken mit Prometheus
20.4Beispiel mit Prometheus
20.5Rezept-Variationen
20.6Experimente
20.7Fazit
21Rezept: Log-Analyse mit dem Elastic Stack
21.1Grundlagen
21.2Logging mit dem Elastic Stack
21.3Beispiel
21.4Rezept-Variationen
21.5Experimente
21.6Fazit
22Rezept: Tracing mit Zipkin
22.1Grundlagen
22.2Tracing mit Zipkin
22.3Beispiel
22.4Rezept-Variationen
22.5Fazit
23Und nun?
Anhang
AInstallation der Umgebung
BMaven-Kommandos
CDocker- und Docker-Compose-Kommandos
Index
Microservices sind einer der wichtigsten Software-Architektur-Trends, grundlegende Werke über Microservices gibt es schon. Unter anderem auch das Microservices-Buch (http://microservices-buch.de)1 vom Autor dieses Werks. Warum noch ein weiteres Buch über Microservices?
Es ist eine Sache, eine Architektur zu definieren. Sie umzusetzen, ist eine ganz andere Sache. Dieses Buch stellt technologische Ansätze für die Umsetzung von Microservices vor und zeigt die jeweiligen Vor- und Nachteile.
Dabei geht es um Technologien für ein Microservices-System als Ganzes. Jeder Microservice kann anders implementiert werden. Daher sind die technologischen Entscheidungen für die Frameworks innerhalb der Microservices nicht so wichtig wie die Entscheidungen für das gesamte System. Die Entscheidung für ein Framework kann in jedem Microservice revidiert werden. Technologien für das Gesamtsystem sind kaum änderbar.
Um Microservices zu verstehen, ist eine Einführung in die Architektur, ihre Vor- und Nachteile und Spielweisen unerlässlich. Die Grundlagen sind in dem Buch soweit erläutert, wie sie für das Verständnis der praktischen Umsetzungen notwendig sind.
Microservices benötigen Lösungen für verschiedene Herausforderungen. Dazu zählen Konzepte zur Integration (Frontend-Integration, synchrone und asynchrone Microservices) und zum Betrieb (Monitoring, Log-Analyse, Tracing). Microservices-Plattformen wie PaaS oder Kubernetes stellen vollständige Lösungen für den Betrieb von Microservices dar.
Das Buch nutzt Rezepte als Metapher für die Technologien, mit denen die Konzepte umgesetzt werden können. Jeder Ansatz hat viel mit einem Rezept gemeinsam:
Für jedes Rezept gibt es ein ablauffähiges Beispiel mit der konkreten Technologie. Die Beispiele sind einzeln ablauffähig und bauen nicht aufeinander auf. So kann der Leser sich mit den für ihn interessantesten Rezepten und Beispielen beschäftigen, ohne sich dabei mit anderen Beispielen befassen zu müssen.
So liefert das Buch einen Einstieg, um einen Überblick über die Technologien zu bekommen und einen Technologie-Stack auszuwählen. Danach kann der Leser sich anhand der im Buch enthaltenen Links weiter in die relevanten Technologien vertiefen.
Dieses Buch besteht aus drei Teilen.
Teil I gibt eine Einführung in die Architektur-Grundlagen, die mit Kapitel 1 beginnt.
Technologie-Stacks stehen im Mittelpunkt von Teil II, der mit Kapitel 5 beginnt.
Den Betrieb einer Vielzahl von Microservices sicherzustellen, ist eine große Herausforderung. Teil III (ab Kapitel 19) diskutiert mögliche Rezepte zur Lösung.
Abschließend bietet das Kapitel 23 noch einen Ausblick.
Die Anhänge erklären die Installation der Software (Anhang A), die Benutzung des Build-Werkzeugs Maven (Anhang B) sowie Docker und Docker Compose (Anhang C), mit denen die Umgebungen für die Beispiele betrieben werden.
Das Buch erläutert Grundlagen und technische Aspekte von Microservices. Es ist für verschiedene Zielgruppen interessant:
Das Buch setzt grundlegendes Wissen über Software-Architektur und Software-Entwicklung voraus. Die praktischen Beispiele sind so dokumentiert, dass sie mit wenig Vorwissen ausgeführt werden können. Das Buch fokussiert auf Technologien, die für Microservices in verschiedenen Programmiersprachen genutzt werden können. Die Beispiele sind in Java mit den Frameworks Spring Boot und Spring Cloud geschrieben, sodass für Änderungen an dem Code Java-Kentnisse notwendig sind.
Das Buch vermittelt vor allem Technologien. Zu jeder Technologie in jedem Kapitel gibt es ein Beispiel. Um schnell praktische Erfahrungen mit den Technologien zu sammeln und anhand der Beispiele nachzuvollziehen, gibt es einen Quick Start:
Sowohl für den Build mit Maven als auch für Docker und Docker Compose enthalten die Kapitel Anleitungen zum Troubeshooting.
Die Beispiele sind in folgenden Abschnitten erläutert:
Konzept |
Rezept |
Abschnitt |
Frontend-Integration |
Links & clientseitige Integration |
8.2 |
Frontend-Integration |
Edge Side Includes (ESI) |
9.2 |
Asynchrone Microservices |
Kafka |
11.4 |
Asynchrone Microservices |
REST & Atom |
12.2 |
Synchrone Microservices |
Netflix-Stack |
14.1 |
Synchrone Microservices |
Consul & Apache httpd |
15.1 |
Microservices-Plattform |
Kubernetes |
17.3 |
Microservices-Plattform |
Cloud Foundry |
18.3 |
Betrieb |
Monitoring mit Prometheus |
20.4 |
Betrieb |
Log-Analyse mit Elastic Stack |
21.3 |
Betrieb |
Tracing mit Zipkin |
22.2 |
Die Projekte sind alle auf GitHub verfügbar. In den Projekten gibt es jeweils eine Datei WIE-LAUFEN.md mit einer Schritt-für-Schritt-Anleitung, wie die Demos installiert und gestartet werden können.
Die Beispiele bauen nicht aufeinander auf. Dadurch ist es möglich, mit einem beliebigen Beispiel loszulegen.
Ich möchte allen danken, mit denen ich über Microservices diskutiert habe, die mir Fragen gestellt oder mit mir zusammengearbeitet haben. Es sind viel zu viele, um sie alle zu nennen. Der Dialog hilft sehr und macht Spaß!
Viele der Ideen und auch die Umsetzungen sind ohne meine Kollegen bei der innoQ nicht denkbar. Insbesondere möchte ich Alexander Heusingfeld, Christian Stettler, Christine Koppelt, Daniel Westheide, Gerald Preissler, Jörg Müller, Lucas Dohmen, Marc Giersch, Michael Simons, Michael Vitz, Philipp Neugebauer, Simon Kölsch, Sophie Kuna und Stefan Lauer danken.
Weiteres wichtiges Feedback kam von Merten Driemeyer und Olcay Tümce.
Schließlich habe ich meinen Freunden, Eltern und Verwandten zu danken, die ich für das Buch oft vernachlässigt habe – insbesondere meiner Frau.
Und natürlich gilt mein Dank all jenen, die an den in diesem Buch erwähnten Technologien gearbeitet und so die Grundlagen für Microservices gelegt haben.
Bei den Entwicklern der Werkzeuge von https://www.softcover.io/ möchte ich mich ebenfalls bedanken.
Last but not least möchte ich dem dpunkt.verlag und René Schönfeldt danken, der mich sehr professionell bei der Erstellung des Buchs unterstützt hat.
Die Website zum Buch ist http://microservices-praxisbuch.de/. Dort finden sich die Errata und Links zu den Beispielen.
Der erste Teil des Buchs stellt die grundlegenden Ideen der Microservices-Architektur vor.
Das Kapitel 1 klärt die Grundlagen von Microservices: Was sind Microservices? Welche Vor- und Nachteile hat diese Architektur?
Das Kapitel 3 beschreibt Self-contained Systems. Sie sind eine Sammlung von Best Practices für Microservices-Architekturen, bei der eine starke Unabhängigkeit und Web-Anwendungen im Mittelpunkt stehen. Neben Vor- und Nachteilen geht es um mögliche Variationen dieser Idee.
Microservices bieten viele Freiheiten. Dennoch müssen einige Entscheidungen übergreifend über alle Microservices eines Systems getroffen werden. Das Kapitel 2 stellt das Konzept der Mikro- und Makro-Architektur vor. Die Mikro-Architektur umfasst alle Entscheidungen, die für jeden Microservice anders getroffen werden können. Die Makro-Architektur sind die Entscheidungen, die für alle Microservices gelten. Neben den Bestandteilen einer Mikro- und Makro-Architektur stellt das Kapitel auch vor, wer eine Makro-Architektur entwirft.
Die meisten Microservices-Projekte migrieren ein vorhandenes System in eine Microservices-Architektur. Daher stellt das Kapitel 4 mögliche Ziele einer Migration und verschiedene Migrationsstrategien vor.
Dieses Kapitel bietet eine Einführung in das Thema »Microservices«. Das Studium dieses Kapitels vermittelt dem Leser:
Leider gibt es für den Begriff »Microservice« keine allgemein anerkannte Definition. Im Rahmen dieses Buchs gilt folgende Definition:
Microservices sind unabhängig deploybare Module.
Beispielsweise kann ein E-Commerce-System in Module für den Bestellprozess, die Registrierung oder die Produktsuche aufgeteilt werden. Normalerweise wären alle diese Module gemeinsam in einer Anwendung implementiert. Dann kann eine Änderung in einem der Module nur in Produktion gebracht werden, indem eine neue Version der Anwendung und damit aller Module in Produktion gebracht wird. Wenn die Module aber als Microservices umgesetzt sind, kann der Bestellprozess nicht nur unabhängig von den anderen Modulen geändert werden, sondern er kann sogar unabhängig in Produktion gebracht werden.
Das beschleunigt das Deployment und verringert die Anzahl der notwendigen Tests, da nur ein Modul deployt wird. Im Extremfall wird durch die größere Entkopplung ein großes Projekt zu einer Menge kleinerer Projekte, die jeweils einen der Microservices verantworten.
Technisch ist es dazu notwendig, dass der Microservice ein eigener Prozess ist. Besser wäre eine eigene virtuelle Maschine oder ein Docker-Container, die Microservices noch stärker entkoppeln. Ein Deployment ersetzt dann den Docker-Container durch einen neuen Docker-Container, fährt die neue Version hoch und lässt dann die Request auf die Version umschwenken. Die anderen Microservices bleiben davon unbeeinflusst.
Diese Definition von Microservices als unabhängig deploybare Module hat mehrere Vorteile:
Ein System, das nicht aus Microservices besteht, kann nur als Ganzes deployt werden. Es ist ein Deployment-Monolith. Natürlich kann der Deployment-Monolith in Module aufgeteilt sein. Über den internen Aufbau sagt dieser Begriff nichts aus.
Die Definition von Microservices trifft keine Aussage über die Größe eines Microservice. Der Name »Microservice« legt den Verdacht nahe, dass es um besonders kleine Services geht. Aber in der Praxis findet man sehr unterschiedliche Größen von Microservices. Einige Microservices beschäftigen ein ganzes Team, während andere nur hundert Zeilen lang sind. Die Größe eignet sich also tatsächlich nicht als Teil der Definition.
Für die Nutzung von Microservices gibt es eine Vielzahl von Gründen.
Ein Grund für den Einsatz von Microservices ist die Skalierung der Entwicklung. Große Teams sollen gemeinsam an einem komplexen Projekt arbeiten. Mithilfe von Microservices können die Teams weitgehend unabhängig arbeiten:
Durch Microservices können die Teams somit fachlich und technisch unabhängig arbeiten. Das erlaubt es, auch große Projekte ohne großen Koordinierungsaufwand zu stemmen.
Die Wartung und Erweiterung eines Legacy-Systems ist eine Herausforderung, weil der Code meistens schlecht strukturiert ist und Änderungen oft nicht durch Tests abgesichert sind. Dazu kann noch eine veraltete technologische Basis kommen.
Microservices helfen bei der Arbeit mit Legacy-Systemen, weil der Code nicht unbedingt geändert werden muss. Stattdessen können neben das alte System neue Microservices gestellt werden. Dazu ist eine Integration zwischen dem alten System und den Microservices notwendig – beispielsweise per Datenreplikation, per REST, Messaging oder auf der UI-Ebene. Außerdem müssen Probleme wie ein einheitliches Single Sign On über das alte System und die neuen Microservices gelöst werden.
Dafür sind die Microservices dann praktisch ein Greenfield: Es gibt keine vorhandene Codebasis, auf die aufgesetzt werden muss. Ebenso kann ein komplett anderer Technologiestack genutzt werden. Das erleichtert die Arbeit gegenüber einer Modifikation des Legacy-Systems erheblich.
Microservices versprechen, dass Systeme auch langfristig wartbar bleiben.
Ein wichtiger Grund dafür ist die Ersetzbarkeit der Microservices. Wenn ein einzelner Microservice nicht mehr wartbar ist, kann er neu geschrieben werden. Das ist im Vergleich zu einem Deployment-Monolithen mit weniger Aufwand verbunden, weil die Microservices kleiner sind als ein Deployment-Monolith.
Allerdings ist es schwierig, einen Microservice zu ersetzen, von dem viele andere Microservices abhängen, weil Änderungen die anderen Microservices beeinflussen können. Also müssen für die Ersetzbarkeit auch die Abhängigkeiten zwischen den Microservices gemanagt werden.
Die Ersetzbarkeit ist eine wesentliche Stärke von Microservices. Viele Entwickler arbeiten daran, Legacy-Systeme zu ersetzen. Aber beim Entwurf eines neues Systems wird viel zu selten die Frage gestellt, wie das System abgelöst werden kann, wenn es zu einem Legacy-System geworden ist. Die Ersetzbarkeit von Microservices ist eine mögliche Antwort.
Für die Wartbarkeit müssen die Abhängigkeiten zwischen den Microservices langfristig gemanagt werden. Auf dieser Ebene haben klassische Architekturen oft Schwierigkeiten: Ein Entwickler schreibt Code und führt dabei unabsichtlich eine neue Abhängigkeit zwischen zwei Modulen ein, die eigentlich in der Architektur verboten war. Das merkt der Entwickler üblicherweise noch nicht einmal, weil er nicht die Architektur-Ebene, sondern nur die Code-Ebene des Systems im Blick hat. Aus welchem Modul die Klasse stammt, zu der er gerade eine Abhängigkeit einführt, ist oft nicht sofort zu erkennen. So entstehen mit der Zeit immer mehr Abhängigkeiten. Gegen die ursprüngliche Architektur mit den geplanten Abhängigkeiten wird immer mehr verstoßen und am Ende steht ein völlig unstrukturiertes System.
Microservices haben klare Grenzen durch ihre Schnittstelle – egal ob die Schnittstelle als REST-Schnittstelle oder durch Messaging implementiert ist. Wenn ein Entwickler eine neue Abhängigkeit zu einer solchen Schnittstelle einführt, merkt er das, weil die Schnittstelle entsprechend bedient werden muss. Aus diesem Grund ist es unwahrscheinlich, dass auf der Ebene der Abhängigkeiten zwischen den Microservices Architektur-Verstöße geschehen. Die Schnittstellen der Microservices sind sozusagen Architektur-Firewalls, weil sie ArchitekturVerstöße aufhalten. Das Konzept einer Architektur-Firewall setzen auch Architektur-Managementwerkzeuge wie Sonargraph (https://www.hello2morrow.com/products/sonargraph), Structure101 (http://structure101.com/) oder jQAssistant (https://jqassistant.org/) um. Fortgeschrittene Modul-Konzepte können ebenfalls solche Firewalls erzeugen. In der Java-Welt beschränkt OSGi (https://www.osgi.org/) andere Module auf den Zugriff über die Schnittstelle. Der Zugriff kann sogar auf einzelne Packages oder Klassen eingeschränkt werden.
Also bleiben einzelne Microservices wartbar, weil sie ersetzt werden können, wenn sie nicht mehr wartbar sind. Die Architektur auf Ebene der Abhängigkeiten zwischen den Microservices bleibt ebenfalls wartbar, weil Entwickler Abhängigkeiten zwischen Microservices nicht mehr unbeabsichtigt einbauen können.
Daher können Microservices langfristig eine hohe Qualität der Architektur sicherstellen und damit eine nachhaltige Entwicklung, bei der die Änderungsgeschwindigkeit auch langfristig nicht abnimmt.
Continuous Delivery1 ist ein Ansatz, bei dem Software kontinuierlich in Produktion gebracht wird. Dazu wird eine Continuous-Delivery-Pipeline genutzt. Die Pipeline bringt die Software durch die verschiedenen Phasen in Produktion (siehe Abbildung 1–1).
Typischerweise wird die Software in der Commit-Phase kompiliert, die Unit Tests und eine statische Code-Analyse werden durchgeführt. In der Akzeptanztestphase überprüfen automatisierte Tests die fachlich korrekte Funktion der Software. Die Kapazitätstests überprüfen die Performance für die zu erwartende Last. Explorative Tests dienen dazu, bisher noch nicht bedachte Tests durchzuführen oder neue Funktionalitäten zu testen. Die explorativen Tests können so Aspekte untersuchen, die automatisierte Tests noch nicht abdecken. Am Ende wird die Software in Produktion gebracht.
Microservices stellen unabhängig deploybare Module dar. Also hat jeder Microservice eine eigene Continuous-Delivery-Pipeline. Das erleichtert Continuous Delivery:
Microservices helfen also bei Continuous Delivery. Die bessere Unterstützung von Continuous Delivery alleine kann schon ein Grund für eine Migration eines Deployment-Monolithen zu Microservices sein.
Microservices-Architekturen können aber nur dann funktionieren, wenn das Deployment automatisiert ist. Microservices erhöhen die Anzahl der deploybaren Einheiten gegenüber einem Deployment-Monolithen erheblich. Das ist nur machbar, wenn die Deployment-Prozesse automatisiert werden.
Tatsächlich unabhängiges Deployment bedeutet, dass die Continuous-Delivery-Pipelines vollständig unabhängig sind. Integrationstests widersprechen dieser Unabhängigkeit: Sie führen Abhängigkeiten zwischen den Continuous-Delivery-Pipelines verschiedener Microservices ein. Also müssen die Integrationstests auf ein Minimum reduziert werden. Abhängig von der Kommunikationsart gibt es dafür unterschiedliche Ansätze (siehe Abschnitt 13.1 und Abschnitt 10.3).
Microservices-Systeme sind robuster. Wenn in einem Microservice ein Speicherleck existiert, stürzt nur dieser Microservice ab. Die anderen Microservices laufen weiter. Natürlich müssen die anderen Microservices den Ausfall eines Microservice kompensieren. Man spricht von Resilience (etwa Widerstandsfähigkeit). Microservices können dazu beispielsweise Werte cachen und diese Werte bei einem Ausfall nutzen. Oder es gibt einen Fallback mit einem vereinfachten Algorithmus.
Ohne Resilience kann die Verfügbarkeit eines Microservices-Systems problematisch sein. Dass irgendein Microservice ausfällt, ist recht wahrscheinlich. Durch die Aufteilung in mehrere Prozesse sind viel mehr Server an dem System beteiligt. Jeder dieser Server kann ausfallen. Die Kommunikation zwischen den Microservices verläuft über das Netzwerk. Das Netzwerk kann ebenfalls ausfallen. Also müssen die Microservices Resilience umsetzten, um Robustheit zu erreichen.
Der Abschnitt 14.5 zeigt, wie Resilience in einem synchronen Microservice-System konkret umgesetzt werden kann.
Jeder Microservice kann unabhängig skaliert werden: Es ist möglich, mehr Instanzen eines Microservices zu starten und die Last für den Microservice auf die Instanzen zu verteilen. Das kann die Skalierbarkeit eines Systems erheblich verbessern. Dazu müssen die Microservices natürlich entsprechende Voraussetzungen schaffen. So dürfen die Microservices keinen State enthalten. Sonst können Clients nicht auf eine andere Instanz umschwenken, die ja den State dann nicht hätte.
Mehr Instanzen eines Deployment-Monolithen zu starten, kann aufgrund der benötigten Hardware schwierig sein. Außerdem kann der Aufbau einer Umgebung für einen Deployment-Monolithen komplex sein: So können zusätzliche Dienste notwendig sein oder eine komplexe Infrastruktur mit Datenbanken und weiteren Software-Komponenten. Bei einem Microservice kann die Skalierung feingranularer erfolgen, sodass üblicherweise weniger zusätzliche Dienste notwendig sind und Rahmenbedingungen weniger komplex sind.
Jeder Microservice kann mit einer eigenen Technologie umgesetzt werden. Das erleichtert die Migration auf eine neue Technologie, da man jeden Microservice einzeln migrieren kann. Ebenso ist es einfacher und risikoärmer, Erfahrungen mit neuen Technologien zu sammeln. Sie können zunächst nur für einen Microservice genutzt werden, bevor sie in mehreren Microservices zum Einsatz kommen.
Microservices können untereinander isoliert werden. So ist es möglich, zwischen den Microservices Firewalls in die Kommunikation einzuführen. Außerdem kann die Kommunikation zwischen den Microservices abgesichert werden, um zu garantieren, dass die Kommunikation tatsächlich von einem anderen Microservice kommt und authentisch ist. So kann verhindert werden, dass bei einer Übernahme eines Microservices auch andere Microservices kompromittiert sind.
Letztendlich lassen sich viele Vorteile der Microservices auf eine stärkere Isolation zurückführen.
Microservices können isoliert deployt werden, was Continuous Delivery vereinfacht. Sie sind bezüglich Ausfällen isoliert, was der Robustheit zugute kommt. Gleiches gilt für Skalierbarkeit: Jeder Microservice kann isoliert von anderen Microservices skaliert werden. Die eingesetzten Technologien können isoliert für einen Microservice bestimmt werden, was Technologiewahlfreiheit ermöglicht. Die Microservices sind so isoliert, dass sie nur über das Netzwerk miteinander kommunizieren. Die Kommunikation kann daher durch Firewalls abgesichert werden, was der Sicherheit zugute kommt.
Weil dank der starken Isolation die Modulgrenzen kaum noch aus Versehen überschritten werden können, wird die Architektur kaum noch verletzt. Das sichert die Architektur im Großen ab. Jeder Microservice kann isoliert durch einen neuen ersetzt werden. So kann risikoarm ein Microservice abgelöst werden und die Architektur der einzelnen Microservices sauber gehalten werden. Dadurch ermöglicht die Isolation eine langfristige Wartbarkeit der Software.
Mit der Isolation treiben Microservices die Entkopplung als wichtige Eigenschaft von Modulen auf die Spitze: Während Module normalerweise nur bezüglich Änderungen am Code und bezüglich der Architektur voneinander entkoppelt sind, geht die Entkopplung der Microservices darüber weit hinaus.
Welcher Grund für Microservices der wichtigste ist, hängt vom jeweiligen Szenario ab. Der Einsatz von Microservices in einem Greenfield-System ist eher die Ausnahme als die Regel. Oft gilt es, einen Deployment-Monolithen durch ein Microservices-System zu ersetzen (siehe auch Kapitel 4). Dabei spielen unterschiedliche Vorteile eine Rolle:
Die Skalierung der Entwicklung ist nicht das einzige Szenario für eine Migration. Wenn ein einziges Scrum-Team ein System mit Microservices umsetzen will, kann die Skalierung der Entwicklung kein sinnvoller Grund sein, weil die Entwicklungsorganisation dazu nicht ausreichend groß ist. Dennoch kann es andere Gründe geben: Continuous Delivery, technische Gründe wie Robustheit, unabhängige Skalierung, Technologiewahlfreiheit oder nachhaltige Entwicklung können in einem solchen Szenario eine Rolle spielen.
Abhängig von den Zielen kann ein Team bei der Umsetzung von Microservices Kompromisse eingehen. Wenn Robustheit ein Ziel der Microservices-Einführung ist, müssen die Microservices als getrennte Docker-Container umgesetzt werden. Jeder Docker-Container kann unabhängig abstürzen. Ist Robustheit kein Ziel, kommen Alternativen in Frage. Beispielsweise können mehrere Microservices als Java-Web-Anwendungen gemeinsam auf einem Java-Application-Server laufen. Sie laufen dann alle in einem Prozess und sind bezüglich der Robustheit nicht isoliert. Ein Speicherleck in einem Microservice bringt alle Microservices zum Absturz. Aber dafür ist die Lösung einfacher zu betreiben und kann der bessere Trade-Off sein.
Die technischen und organisatorischen Vorteile weisen auf zwei Ebenen hin, in denen ein System in Microservices unterteilt werden kann:
Abbildung 1–3 zeigt ein Beispiel für zwei Ebenen: Eine ECommerce-Anwendung ist fachlich in die Microservices Suche, Check Out, Inkasso und Lieferung aufgeteilt. Die Suche ist weiter aufgeteilt: Die Volltext-Suche ist von der Suche in Kategorien getrennt. Ein Grund dafür kann die getrennte Skalierung sein: Mit dieser Architektur kann die Volltext-Suche getrennt von der Suche in Kategorien skaliert werden, was ein Vorteil ist, wenn sie unterschiedlich hohe Last handhaben müssen. Ein weiterer Grund können unterschiedliche Technologien sein: Die Volltext-Suche kann mit einer Volltext-Suchmaschine umgesetzt sein, die für die Suche in Kategorien ungeeignet ist.
Es ist schwer, eine typische Anzahl von Microservices in einem System anzugeben. Wenn man der Aufteilung aus diesem Kapitel folgt, dann sollten sich 10–20 grobgranulare Fachlichkeiten ergeben und für jede von ihnen ein bis drei Microservices. Es gibt allerdings auch Systeme mit weitaus mehr Microservices.
Microservices haben nicht nur Vorteile, sondern halten auch Herausforderungen bereit: