Microservices und Docker

In der Softwaretechnik hat in den letzten Jahren kaum ein Architekturmuster für so viel Aufsehen gesorgt, wie Microservices. Vor allem Netflix setzt, auch in der Öffentlichkeit, sehr stark auf dieses Architekturmuster. Aber was versteht man unter Microservices? Anders als bei Client- Server-Architekturen oder ähnlichen klassischen Architekturmustern ist der Begriff (noch) nicht einheitlich definiert. Martin Fowler sieht als zentrales Merkmal von Microservices vor allem den Verzicht von Softwaremonolithen und die Bereitstellung eines Gesamtsystems aus kleinen, autarken Diensten, die zwar thematisch zusammenhängen, technisch jedoch unabhängig voneinander entwickelt, getestet und veröffentlicht werden können:

„The term ‘Microservice Architecture’ has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services. While there is no precise definition of this architectural style, there are certain common characteristics around organization around business capability, automated deployment, intelligence in the endpoints, and decentralized control of languages and data.“ (https://martinfowler.com/articles/microservices.html)

In der Definition von Martin Fowler wird deutlich, dass Microservices mehr sind als nur ein reines Architekturmuster. Fest verbunden mit der Thematik ist eine starke Unternehmensmentalität für automatisiertes Testen und automatisiertes Veröffentlichen von autarken Softwarekomponenten. Wer nach Microservices-Konzepten entwickelt, muss also auch eine DevOps-Kultur leben. Dabei sollen durch Microservices-Architekturen und DevOps-Ansätze vor allem die folgenden Indikatoren verbessert werden:

  •  Performance
    • Bereitstellungszyklen erhöhen
    • Schneller auf sich ändernde Geschäftsanforderungen reagieren
  • Verfügbarkeit
    • Fehlerrate nach Bereitstellungen reduzieren
    • Mean-Time-to-Detect & Repair (MTTD, MTTR) soll minimert werden.

Um dies zu erreichen, liegen Microservices eine immer gleichartige Philosophie zugrunde. Zunächst werden fachliche Services gemäß dem Prinzip der Single Responsibility definiert. Die Dienste weisen eine lose Kopplung auf und bilden eine in sich geschlossene Domäne ab. Ein Microservice wird typischerweise von einem Team von 5 bis 7 Entwicklern entwickelt. Dabei steht stets der fachliche Nutzen eines Microservices im Vordergrund. Ein Microservice zum Speichern und Laden von generischen Daten wäre daher kein geeigneter Microservice. Ein Microserice in einer E-Commerce-Anwendung, der die Bestellungen eines Kunden verwaltet, dagegen schon.

Da die Dienste autark sind, kann auch die Wahl der Technologie bedarfsgerecht erfolgen: von der konkreten Technologie wird mittels REST-Schnittstellen abstrahiert. Ein Beispiel für einen Microservice, in loser Anlehnung an https://goo.gl/MyYg4W, zeigt die folgende Abbildung.

Microservices spielen ihre Stärke unter anderen in der Skalierbarkeit aus. Abbildung 1 zeigt auf der linken Seite eine klassische, monolithische Anwendung. Wie können wir die Performance des Systems verbessern? Häufig stehen wir hier vor dem Problem, dass bestimmte Funktionalitäten des Monolithen eine höhere Systemlast erzeugen als andere. Trotzdem muss im Falle eines monolithischen Softwaresystem das gesamte System dupliziert werden. Im Gegensatz hierzu zeigt der rechte Teil der Abbildung ein Softwaresystem, das nach dem Microservice-Ansatz realisiert wurde. Hier können besonders rechenintensive Microservices (z.B. der Fotofilter) beliebig oft neu gestartet und auf andere Systeme verteilt werden. Hierdurch können die verfügbaren Serverkapazitäten erheblich besser ausgenutzt werden.

Vor- und Nachteile

Microservices besitzen eine Reihe von Vorteilen, jedoch auch einige Nachteile. Das Architekturmuster kann daher nicht als neuartige Wunderwaffe angepriesen werden. Der Einsatz von Microservices setzt eine Infrastruktur für dezentrale Softwarearchitekturen voraus. Hierzu gehört u.a. ein Konzept für ein dezentrales Logging (z.B. mittels ElasticSearchFluentd oder Kibana). Aber auch die Kommunikation der autarken Komponenten untereinander muss gewährleistet sein. Hierfür werden häufig MessageBus-Systeme wie RabbitMQ oder KeyValue-Datenbanken wie Redis oder Memcache zur Realisierung von Caches eingesetzt. Dadurch, dass Microservices auf beliebigen Server veröffentlicht werden können, benötigt man einen Loadbalancer bzw. eine Service-Discovery. Um die vielen kleinen Dienste überwachen zu können, muss eine spezielle, aggregierende Monitoring-Lösung wie IcingaAppMetric oder Grafana eingesetzt werden. Darüber hinaus wird in der Regel eine Infrastruktur für eine zentrale Authentifizierung benötigt.

All dies macht deutlich: Microservices verursachen einen erheblichen infrastrukturellen Mehraufwand. Gleichzeitig ist man mit völlig neuen Problemen konfrontiert, wie z.B. Netzwerklatenzen oder Resilienz. Die Art der Softwareentwicklung ändert sich daher erheblich – sowohl für die Betriebsabteilung, als auch für die Entwicklungsabteilung (womit wir wieder beim Thema DevOps wären).

Gleichzeitig bieten Microservices aber auch erhebliche Vorteile: Fachliche Anforderungen können sehr schnell umgesetzt werden. Die Entwicklerteams können autark arbeiten. Durch die Kleinteiligkeit der Dienste kann sich ein Entwickler schnell in neue Microservices einarbeiten. Gleichzeitig können neue Technologien schnell adaptiert werden. Langfristig bleibt die Architektur daher stabiler, als dies bei einem Softwaremonolithen der Fall ist: Wenn ein Dienst refaktorisiert werden muss, wirkt sich dies nicht auf die anderen Microservices aus.

Microsofts Referenzbeispiel

Es existieren mehrere interessante Beispiele für Microservices. Ein sehr gut dokumentiertes Beispiel, welches einen modernen Technologiestack verwendet und regelmäßig gepflegt wird, ist „eShopOnContainers“ von Microsoft. Das Beispiel kann unter https://goo.gl/MyYg4W heruntergeladen werden. Insgesamt 15 Microservices realisieren dabei einen Webshop. Einzelne Dienste repräsentieren dabei einen zusammenhängenden Themenkomplex:

Neben den im Beispiel selbst entwickelnden Diensten kommen viele weitere „Treibertechnologien“, wie SQL- und NoSQL-Datenbanken, ServicesBus-Systeme, ein Dienst für die Authentifzierung und vieles mehr hinzu.

Das Softwaresystem nutzt Docker als Laufzeitumgebung und kann daher, trotz der enormen Komplexität der vielen Teilkomponenten, sehr schnell gestartet werden.

Fazit

Microservices lösen viele Probleme. Sie ermöglichen eine bisher nie dagewesene Skalierungsfähigkeit sowohl zur Laufzeit als auch zur Entwicklungszeit. Besonders im Cloud-Kontext, wo neue Dienste wie Server- und Container-Instanzen automatisch gestartet und beendet werden können, spielt diese Technologie ihre Stärken aus. Gleichzeitig kann die Technologieunabhängigkeit erhebliche Vorteile mit sich bringen. Auch Wartung und Pflege sind einfacher, da die Dienste kleiner werden. Neue Funktionen können schnell bereitgestellt und auf Kundenanforderungen kann besser reagiert werden. Dem steht jedoch eine erheblich komplexere Infrastruktur gegenüber. Diese muss sowohl von der Betriebsabteilung, als auch von der Softwareabteilung zunächst einmal verstanden werden. Nicht jedes Unternehmen hat hierfür die notwendigen Kapazitäten. Nicht jedes Softwareprojekt rechtfertigt die aufwändige Infrastruktur. Daher lassen sich für Microservices-Architekturen kein pauschales Urteil fällen – es kommt, wie so so oft, darauf an.