Wie Sie Microservices mit AWS umsetzen

Microservices-Applikationen in der AWS Cloud

Sascha Möllering, Matthias Jung

(c) Shutterstock / jovan vitanovski

Microservices sind ein Softwarearchitektur- und Organisationsansatz, um das Deployment von neuen Software-Releases zu beschleunigen, Innovationen voranzutreiben und die Wartbarkeit und Skalierbarkeit von Software zu erhöhen. Eines der wichtigen Konzepte von Microservice-Architekturen ist es, die einzelnen Bestandteile einer Software in kleine unabhängige Services aufzuteilen, die miteinander über APIs kommunizieren. Dabei liegt die Verantwortung für die einzelnen Services bei relativ kleinen Entwicklungsteams.

In diesem Artikel werden wir zunächst kurz beschreiben, was die typischen Charakteristika von Microservices sind, die größten Herausforderungen beim Entwickeln von Microservices darstellen und besprechen, wie Produktteams AWS nutzen können, um diese Herausforderungen zu überwinden.

Microservices

Das Architekturmuster „Microservices“ hat in den letzten Jahren wachsende Aufmerksamkeit erhalten. Microservices sind kein vollständig neuer Ansatz für das Design verteilter Anwendungen, sondern eher eine Ansammlung und Kombination verschiedener bewährter Konzepte wie beispielsweise objektorientiertes Design, serviceorientierte Architektur, „API first“-Design oder Continuous Delivery.

Aufgrund der Tatsache, dass es sich bei „Microservices“ eher um einen Sammelbegriff handelt, existiert keine klar umrissene Definition. Dennoch teilen sich Microservices-Architekturen einige Gemeinsamkeiten:

  • Dezentralisiert: Microservices-Architekturen sind verteilte Systeme mit zentralisiertem Datenmanagement. Jeder Microservice hat die Hoheit über die Daten einer Domäne. Die einzelnen Services sind so dezentralisiert, dass sie unabhängig voneinander entwickelt, ausgerollt und betrieben werden können.
  • Unabhängig: Verschiedene Komponenten einer Microservices-Architektur können unabhängig voneinander geändert, aktualisiert und ausgetauscht werden, ohne dass dadurch die Funktionalität anderer Komponenten beeinflusst wird. Analog gilt für die Teams, die für die einzelnen Komponenten einer Microservices-Architektur verantwortlich sind, dass diese unabhängig voneinander arbeiten können.
  • „Do one thing well“: Jede Komponente fokussiert sich auf eine spezifische Domäne. Falls im Laufe der Zeit zusätzliche Komplexität hinzukommt, sind diese Funktionalitäten eventuell Kandidaten für einen eigenen Microservice.
  • Polyglott: Microservices-Architekturen folgen nicht einem „One size fits all“-Ansatz. Teams haben die Freiheit, die beste Plattform für die spezifischen Problemstellungen zu wählen. Als Konsequenz daraus folgt, dass Microservices-Architekturen für gewöhnlich heterogen sind in Hinblick auf genutztes Betriebssystem, Programmiersprachen, Datenbanken und Tooling.
  • Black Box: Die einzelnen Komponenten sind als sogenannte „Black Box“ entworfen. In diesem Kontext bedeutet das, dass die Komplexität der Implementierung anderen Komponenten verborgen bleibt. Jede Kommunikation erfolgt über diese APIs. Die Nutzung von geteilten Komponenten wie beispielsweise Bibliotheken oder gemeinsamen Datentöpfe würde die Unabhängigkeit der einzelnen Services beeinträchtigen.
  • „You build it, you run it“: Typischerweise ist das Team, das für die Implementierung eines Services verantwortlich ist, auch für dessen Betrieb zuständig. Dieses Prinzip ist auch unter dem Namen „DevOps“ bekannt. Neben dem Vorteil, dass Teams unabhängig voneinander entwickeln können, erlaubt dieses Vorgehen, dass Entwickler in engem Kontakt mit den eigentlichen Nutzern ihrer Software treten können – was zudem auch dabei hilft, Anforderungen von Kunden besser zu verstehen. Dieser organisatorische Aspekt bei Microservices sollte auf keinen Fall unterschätzt werden, denn Conway’s Law [1] besagt, dass ein System sehr stark durch die organisatorischen Strukturen der Teams, die das System bauen, beeinflusst wird.

Vorteile von Microservices

Aufgrund ihrer Verteiltheit begünstigen Microservices eine Organisationsform, in der kleine unabhängige Teams die Hoheit über ihre Services haben. Teams arbeiten unabhängig voneinander, was die Geschwindigkeit bei der Entwicklung der Software und der Deployments signifikant erhöht. Die Codebasis, an denen die einzelnen Teams arbeiten, ist deutlich kleiner, darüber hinaus gibt es deutlich weniger Abhängigkeiten im Quellcode. Beide Faktoren führen dazu, dass Codekonflikte reduziert, Testzyklen verkürzt und die Auslieferungszeit von neuen Features und Services minimiert werden. Das Feedback von Kundenseite kann somit deutlich schneller in kommende Releases integriert werden. Aufgrund der Tatsache, dass kleine Teams autonom agieren und Technologien, Frameworks und Tools auswählen können, die am besten für die entsprechende Problemdomäne geeignet sind, können Innovationen deutlich schneller vorangetrieben werden.

Dadurch, dass Teams die Kompetenzen sowohl zum Entwickeln als auch zum Betreiben einer Applikation haben, werden Reibungen und sich widersprechende Ziele vermieden. Die Automatisierung der Entwicklungs-Prozesse werden nicht länger unterbrochen, wenn es zum Deployment kommt, sondern auf den gesamten Lebenszyklus einer Software vom initialen Code-Commit bis zum Release ausgeweitet. Dadurch wird es einfach, neue Ideen als Features auszurollen und gegebenenfalls auch einfach wieder herauszunehmen. Die geringen Kosten „gescheiterter“ Experimente befördern eine Kultur des Wandels und der Innovation.

All diese Faktoren können dazu beitragen, die Qualität des Quellcodes zu erhöhen. Die Vorteile davon, Software in kleine und definierte Module aufzuspalten, erinnern an die Vorteile objektorientierter Softwareentwicklung: verbesserte Wiederverwendung, Zusammensetzbarkeit und Wartbarkeit von Quelltext.

Feingranulare Entkopplung ist eine der Best Practices, um hochskalierbare System zu bauen, und eine Voraussetzung für Performance-Optimierung, da sie erlaubt, Services individuell zu betrachten und die optimale Technologie für den jeweiligen Service zu nutzen. Jeder Service kann mit der für ihn sinnvollen Sammlung von Sprachen und Frameworks implementiert werden, um – in Kombination mit dem optimalen Datenspeicher – die beste Performance zu erreichen. Sinnvoll entkoppelte Services können horizontal und unabhängig voneinander skaliert werden. Vertikale Skalierung, also das Ausführen derselben Software auf leistungsfähigeren Servern, ist durch die Kapazität der einzelnen Server begrenzt und kann zudem zu Ausfallzeiten bei der Skalierung führen. Bei horizontaler Skalierung können auf einfache Weise weitere Server dem Pool an Ressourcen hinzugefügt werden. Dieser Prozess kann darüber hinaus auch noch automatisiert werden. Die Robustheit der Applikation steigt durch diese Maßnahme auch, denn Komponenten können einfach und automatisch ersetzt werden. Mit Microservices-Architekturen ist es auch erheblich einfacher, Ausfälle einzelner Komponenten zu isolieren: Design-Patterns wie beispielsweise Health-Checks, Caching, Bulkheads oder Circuit-Breaker erlauben es, Fehler lokal zu begrenzen, und verhindern Kaskadeneffekte.

Eine einfache Microservices-Architektur in AWS

Abbildung 1 zeigt eine Referenzarchitektur für einen typischen Microservice in AWS. Die Architektur besteht aus vier verschiedene Schichten ab: Auslieferungsschicht, API-Schicht, Anwendungsschicht und Datenhaltungsschicht.

Slide1-768x432

Abb. 1: Microservices Referenzarchitektur

Der Zweck der Auslieferungsschicht besteht in der beschleunigten Auslieferung von statischen und dynamischen Inhalten und der Entlastung der Backend-Systeme. Da der Zugriff auf die Microservices über die geographisch nächste Edge-Location stattfindet und Antworten entweder direkt aus einem Cache oder einem Proxy mit optimierter Verbindung zu den Ursprungs-Servern ausgeliefert werden, kann die Latenz signifikant reduziert werden. Microservices, die direkt miteinander kommunizieren, profitieren in der Regel nicht von einem CDN, können aber andere Caching-Mechanismen implementieren, um Latenz und Kommunikationsoverhead zu verringern.

Die API-Schicht ist der zentrale Einstiegspunkt für alle Anfragen der Clients und kapselt die Anwendungslogik hinter Schnittstellen, typischerweise HTTP REST APIs. Die API-Schicht ist dafür verantwortlich, die Aufrufe der Clients entgegenzunehmen und zu verarbeiten. Darüber hinaus kann diese Schicht auch Funktionalitäten wie beispielsweise Traffic Management, Filterung der Anfragen, Routing, Caching oder Authentifizierung und Autorisierung implementieren. Viele AWS-Kunden nutzen Amazon Elastic Load Balancing (ELB) in Kombination mit Amazon Elastic Compute Cloud (EC2) und Auto-Scaling, um eine API-Schicht zu implementieren.

Die Anwendungsschicht implementiert die eigentliche Applikationslogik. Analog zur API-Schicht kann auch diese mit Hilfe von ELBs, Auto Scaling und EC2 aufgesetzt werden.

Die Datenhaltungsschicht zentralisiert die für die Persistenz von Daten notwendige Funktionalität. Das Zusammenfassen dieser Funktionalität in eine separate Schicht hilft, den Zustand aus der Applikationsschicht auszulagern und horizontale Skalierung und Fehlertoleranz zu erreichen. Statische Inhalte sind typischerweise in Amazon S3 abgelegt und werden von Amazon CloudFront ausgeliefert. Beliebte Speicher für Session-Daten sind In-Memory-Caches wie Memcached oder Redis. AWS bietet beide Technologien als Teile des verwalteten Services Amazon ElastiCache an. Zusätzlich werden diese Caches auch gerne zwischen den Applikationsservern und der Datenbank verwendet, um Last von der Datenbank nehmen.

Caches können generell die Latenz einer Anwendung verbessern. Relationale Datenbanken sind immer noch sehr beliebte Speicher, um strukturierte Daten abzulegen. AWS unterstützt sechs verschiedene Datenbank-Engines (Microsoft SQL Server, Oracle, MySQL, MariaDB, PostgreSQL und Amazon Aurora) als verwaltete Dienste im Kontext von Amazon RDS.

Abbildung 2 zeigt die Architektur eines Microservices, bei dem alle Schichten mit Hilfe von verwalteten Diensten aufgebaut wurden. Bei diesem Ansatz müssen sich Entwickler nicht mehr selbst Gedanken um Skalierung und Hochverfügbarkeit machen, da diese bereits in der darunterliegenden Infrastruktur eingebaut sind.

Slide2-768x432

Abb. 2: Microservices-Architekturen mit verwalteten Services

Komplexitätsreduktion

Die in Abbildung 2 abgebildete Architektur ist bereits in hohem Maße automatisiert. Dessen ungeachtet ist durchaus noch Potential für weitere Vereinfachungen betrieblicher Aufwände gegeben. Entwicklung, Wartung, Deployment und Betrieb einer API-Schicht kann zeitintensiv sein. Manchmal ist es notwendig, unterschiedliche Versionen von APIs gleichzeitig zu betreiben, um Abwärtskompatibilität für die Clients zu garantieren. Die Unterstützung unterschiedlicher Umgebungen (wie beispielsweise Dev, Test und Prod) bedeutet weiteren signifikanten betrieblichen Aufwand.

Ein weiteres wichtiges und komplexes Feature aller APIs ist die Steuerung des Zugriffs: Wenn ein API veröffentlicht wird und erfolgreich wird, besteht die nächste Herausforderung darin, das API zu verwalten, zu überwachen und zu monetarisieren.

Andere wichtige Features und Herausforderungen beinhalten das Drosseln von Anfragen zum Schutz der Backend-Systeme, die Zwischenspeicherung von API-Antworten, die Transformation von Anfragen und Antworten oder der Generierung von API-Definitionen und Dokumentation mit Werkzeuge wie Swagger [2]. Das Amazon API Gateway adressiert diese Herausforderungen und verringert die betriebliche Komplexität der API-Schicht. Amazon API Gateway erlaubt es Kunden, APIs programmatisch (durch den Import einer Swagger-Definition) oder durch wenige Clicks in der AWS Management Konsole anzulegen. API Gateway agiert als „Eingangstür“ für Web-Applikationen, die auf Amazon EC2, Amazon ECS, AWS Lambda oder einer On-Premises-Umgebung laufen.

Zusammenfassend kann man sagen: Der Service erlaubt es, APIs ohne Server zu betreiben. Abbildung 2 visualisiert, wie Amazon API Gateway Anfragen bearbeitet und mit anderen Komponenten interagiert. Anfragen von Mobilgeräten, Webseiten oder anderen Services werden zum nächsten Amazon CloudFront PoP geroutet, um die Latenz zu minimieren. API Gateway überprüft zunächst, ob die Antwort auf die Anfrage bereits im Cache existiert (falls ein Cache angelegt wurde). Falls das nicht der Fall ist, wird die Anfrage weiter an die entsprechenden Backend-Systeme geleitet. Die Metriken des API-Aufrufs werden in Amazon CloudWatch abgelegt, und der Inhalt wird dem Client zurückgeliefert.

AWS bietet mehrere Alternativen zu ELB/Auto Scaling/EC2-basierten Architekturen, die helfen können, Deployment und Betrieb zu vereinfachen oder Ressourcen zu optimieren. Eine dieser Optionen ist die Nutzung von AWS Elastic Beanstalk. Die Idee hinter Elastic Beanstalk ist, dass Entwickler einfach ihren Quelltext beziehungsweise ihr Deployment-Artefakt hochladen und Elastic Beanstalk sich dann eigenständig um die Bereitstellung der Infrastruktur und um das Deployment kümmert. Wichtige Infrastruktur-Charakteristika wie automatische Skalierung, Lastverteilung und Überwachung sind integraler Bestandteil des Service. AWS Elastic Beanstalk unterstützt eine große Vielfalt an Plattformen wie Java, .NET, PHP, Node.js, Python, Ruby, Golang und Docker mit geläufigen Webservern wie Apache, Nginx, Phusion Passenger und IIS.

Ein anderer Ansatz, um betriebliche Aufwände für das Ausrollen zu reduzieren, besteht in Deployments auf Basis von Containern. Container-Technologien wie Docker haben in den letzten Jahren deutlich an Popularität gewonnen. Grund dafür sind folgende Vorteile:

  • Flexibilität: Die Verwendung von Containern fördert die Aufspaltung von Applikationen in unabhängige, feingranulare Komponenten.
  • Effizienz: Container erlauben die explizite Definition von Ressourcenanforderungen (CPU, RAM), die es einfach machen, Container zu orchestrieren und das Cluster an EC2-Instanzen effizienter zu nutzen. Container haben – im Vergleich zu Virtualisierung – nur einen geringen Overhead und teilen sich die Ressourcen des darunterliegenden Betriebssystems effizient.
  • Geschwindigkeit: Container sind wohldefinierte und wiederverwertbare Arbeitspakete, die unveränderlich sind, explizite Versionierung haben, einfach zurückzurollen und feingranular sind sowie Isolation unterstützen. All diese Eigenschaften können dabei helfen, die Produktivität der Entwickler und die betriebliche Effizienz zu erhöhen.

Amazon EC2 Container Service (ECS) ist ein skalierbarer Container-Management-Service, der Docker-Container unterstützt und es erlaubt, Anwendungen auf einem verwalteten Cluster von Amazon EC2-Instances zu betreiben und zu verwalten. Mit Amazon ECS entfällt das Erfordernis, eigene Cluster-Managementinfrastruktur zu installieren, zu betreiben oder zu skalieren. Mit einfachen API-Befehlen können Docker-basierte Anwendungen gestartet und beendet sowie der gesamte Zustand Ihres Clusters abgefragt werden. Mit Amazon ECS kann die Platzierung von Containern im Cluster entsprechend dem Ressourcenbedarf und den Verfügbarkeitserfordernissen geplant werden. Mit dem kürzlich veröffentlichten Open-Source-Project Blox [3] ist es möglich, eigene Scheduler zu implementieren und bestehende Scheduler auf ECS laufen zu lassen.

Serverlose Datenverarbeitung

Ein sinnvoller Weg, um betriebliche Komplexität beseitigen zu können, besteht darin, einfach keine Server mehr zu betreiben. AWS Lambda ist ein serverloser Datenverarbeitungsservice, der Code beim Eintreten bestimmter Ereignisse ausführt und automatisch die zugrundeliegenden Datenverarbeitungsressourcen verwaltet. Mit AWS Lambda können andere AWS-Services mit benutzerdefinierter Logik erweitert oder eigenen Backend-Services erstellen werden. AWS Lambda kann Code automatisch als Reaktion auf verschiedene Ereignisse ausführen, zum Beispiel Änderungen von Objekten in Amazon S3-Buckets oder Aktualisierungen von Tabellen in Amazon DynamoDB.

AWS Lambda führt den Code automatisch auf hochverfügbarer Datenverarbeitungsinfrastruktur aus und erledigt die gesamte Verwaltung der Datenverarbeitungsressourcen, einschließlich Server- und Betriebssystemwartung, Kapazitätsbereitstellung und automatischer Skalierung, Code- und Sicherheitspatch-Bereitstellung sowie Code-Überwachung und -Protokollierung. AWS Lambda ist mit dem Amazon API Gateway integriert: Durch die Möglichkeit der synchronen Aufrufe von Amazon API Gateway nach AWS Lambda können vollständige Applikationen ohne Server erstellt werden.

Obwohl relationale Datenbanken immer noch sehr populär sind, sind sie nicht für unlimitierte Skalierung konzipiert. NoSQL-Datenbanken hingegen wurden für eine hohe Skalierbarkeit konzipiert mit entsprechenden Einbußen bei der Datenkonsistenz. Ein sehr wichtiges Element bei NoSQL-Datenbanken ist, dass diese typischerweise kein striktes Schema vorschreiben. Die Daten werden über unterschiedliche Partitionen verteilt, die horizontal skaliert werden und mit Hilfe eines Partitions-Schlüssels gelesen werden können.

Da Microservices typischerweise dafür konzipiert sind, eine Sache sehr gut zu machen, haben sie in vielen Fällen ein simples Datenmodell, das sehr gut über NoSQL-Datenbanken abgebildet werden kann. Für diese Fälle kann Amazon DynamoDB eingesetzt werden: Mit Amazon DynamoDB können Datenbanktabellen angelegt werden, die jede beliebige Menge an Daten mit beliebiger Menge an Anfragen verarbeiten können. Amazon DynamoDB verteilt die Daten und die Anfragen an die Tabellen automatisch über eine ausreichend große Anzahl von Servern, um den spezifizierten Kapazitäten zu entsprechen.

Die Orchestrierung eines Deployments für serverlose Architekturen kann eine anspruchsvolle Aufgabe sein, denn manchmal müssen parallel einige verwaltete Services aktualisiert werden. Falls eine neue Funktionalität zu einem bereits bestehenden API hinzugefügt werden soll, muss das API in API Gateway modifiziert und eine neue Lambda-Funktion hinzugefügt werden. Frameworks wie Chalice [4] oder Serverless [5] vereinfachen diesen Prozess deutlich. Das folgende Beispiel zeigt, wie eine simple Applikation mit Hilfe des Serverless-Frameworks erzeugt und ausgerollt werden kann:

npm install serverless –g
serverless create --template aws-nodejs
serverless deploy

Diese drei Zeilen Quelltext installieren das Serverless-Framework mit NPM, erzeugen eine einfache Applikation auf Basis von Node.js und deployen das Projekt in AWS. Serverless nutzt Amazon CloudFormation [6] als Basis, um alle notwendigen Konfigurationen zu speichern. Jede Ressource wird durch dieses zentrale CloudFormation-Template erzeugt. Falls beispielsweise ein zusätzlicher S3-Bucket für Bilder erzeugt werden soll, kann dies in der zentralen serverless.yml-Datei konfiguriert werden:

service: lambda-images
provider: aws
functions:
  ...

resources:
  Resources:
    ImagesBucket:
      Type: AWS::S3::Bucket
      Properties:
         BucketName: images-lambda
         # Or you could reference an environment variable
         # BucketName: ${env:BUCKET_NAME}

Mit dem simplen Befehl „serverless deploy“ wird diese Ressource erzeugt.

Service Discovery

Eine der primären Herausforderungen bei Microservices-Architekturen ist es, Services zu erlauben, sich gegenseitig erkennen und miteinander interagieren zu können. Darüber hinaus existieren noch weitere interessante Herausforderungen wie beispielsweise die Überprüfung, ob diese Systeme noch korrekt funktionieren. Es muss entschieden werden, wie und vor allen Dingen auch wo Metainformationen wie Konfigurationsdaten abgelegt werden, die von den Anwendungen genutzt werden können.

Im Folgenden werden mehrere Optionen für Service Discovery in AWS für Microservice-Architekturen beleuchtet. Ein naheliegender Ansatz besteht in der Nutzung des Elastic Load Balancing (ELB) Service. Ein Vorteil dieses Ansatzes ist es, dass Health-Checks und automatische Registrierung/De-Registrierung von Services bereits gebrauchsfertig in den ELB integriert sind. Kombiniert mit DNS ist es möglich, eine einfache Service-Discovery-Lösung mit minimalem Aufwand und geringen Kosten zu bauen.

Für jeden Microservice kann ein eigener Domain-Name gewählt und dieser über den DNS-Namen des ELBs über den CNAME-Eintrag zugewiesen werden. Der DNS-Name des Service-Endpunkts ist dann von anderen Applikationen erreichbar. Der neue Application Load Balancers (ALB) unterstützt ein Feature namens „Path-based Routing“, das es möglich macht, Anfragen regelbasiert auf Basis der URL-Pfade zu verschiedenen Server-Gruppen weiterzuleiten. Beispielsweise können generelle Anfragen an eine allgemeine Zielgruppe weitergeleitet werden, Anfragen zur Erzeugung eines Bildes hingegen an eine andere Gruppe.

Amazon Route 53 könnte eine andere Quelle für Service-Discovery-Information sein. Route 53 bietet eine Funktionalität namens Private Hosted Zones, die es erlaubt, DNS-Zugriff auf bestimmte Virtual Private Clouds (VPCs) zu beschränken. In diesem Fall würden IP-Adressen, Hostnamen und Port-Informationen als SRV-Eintrag für einen spezifischen Microservice registriert werden und der Zugriff auf das VPC eingeschränkt werden, in dem die Microservices laufen, die darauf Zugriff bekommen sollen. Darüber hinaus können auch Health-Checks definiert werden, die regelmäßig den Status der Applikation überprüfen und im Fehlerfall einen Schwenk des Traffics initiieren.

Eine andere Möglichkeit besteht in der Nutzung eines Key/Value-Stores als Service Discovery für Microservices. Naturgemäß benötigt der Aufbau einer solchen Lösung mehr Zeit als der eben beschriebene DNS-Ansatz, bietet aber im Gegenzug eine deutlich höhere Flexibilität. Dieser Ansatz integriert sich zudem sehr gut mit Client-basierten Load-Balancing-Lösungen wie beispielsweise Netflix Ribbon.

Ein weiterer Ansatz besteht in der Nutzung von Service Discovery Software wie HashiCorp Consul oder Netflix Eureka. Consul macht es für Services einfach, sich selbst zu registrieren und andere Services mit DNS oder HTTP zu finden. Darüber hinaus bieten diese Komponenten Fehlererkennung mit Health-Checks, um zu verhindern, dass Anfragen an nicht funktionierende Knoten gesendet werden. Der verlinkte Blogpost [7] zeigt, wie eine Service Discovery für Amazon ECS mit Hilfe von Consul aufgesetzt wird.

Verteilte Überwachung

Eine Microservices-Architektur besteht aus vielen beweglichen Teilen, die überwacht werden müssen. Amazon CloudWatch kann verwendet werden, um Metriken zu erfassen und nachzuverfolgen, Log-Dateien zu aggregieren und zu überwachen, Alarme festzulegen und automatisch auf Änderungen von AWS-Ressourcen zu reagieren. Amazon CloudWatch kann AWS-Ressourcen, wie EC2-Instanzen, Amazon DynamoDB-Tabellen und Amazon RDS DB-Instanzen sowie von Anwendungen und Services generierte Metriken und Log-Dateien überwachen. Amazon CloudWatch bietet einen systemweiten Einblick in die Auslastung von Ressourcen, die Anwendungsleistung und die Integrität von Betriebsabläufe.

Mit Amazon CloudWatch ist es deshalb nicht mehr notwendig, eigene Infrastruktur für das Monitoring aufzusetzen, zu verwalten und zu skalieren. Bei einer Microservice-Architektur ist die Möglichkeit, eigene Metriken mit Hilfe von Amazon CloudWatch zu überwachen, eine zusätzliche Hilfe, denn Entwickler können entscheiden, welche Metriken für welchen Service ermittelt werden sollen. Darüber hinaus kann dynamische Skalierung von Infrastruktur auf Basis dieser Metriken implementiert werden.

Konsistentes Logging ist ein Schlüssel für effektive Fehlersuche und die Analyse von Problemen und Ursachen. Das Speichern von Log-Dateien in einem zentralen System ist essentiell, um eine aggregierte Sicht auf das verteilte System zu bekommen. In AWS kann Amazon CloudWatch Logs genutzt werden, um Log-Dateien von EC2-Instanzen, AWS CloudTrail oder anderen Quellen zu überwachen, zu speichern und auf diese zugreifen zu können.

Amazon ECS besitzt direkte Unterstützung für den awslogs Log-Treiber, der es erlaubt, die Log-Einträge der Docker-Container zentralisiert nach CloudWatch Logs strömen zu lassen. Es existieren unterschiedliche Optionen, um Log-Dateien zu zentralisieren. Die meisten AWS Services zentralisieren die Log-Dateien bereits direkt. Eine zentralisierte Logging-Lösung aggregiert alle Logs in einem zentralen Service, um die Möglichkeit zu haben, über die Log-Dateien zu suchen und diese zu analysieren. Dafür bieten sich Werkzeuge wie Amazon EMR, Amazon Redshift oder Amazon Athena an.

In vielen Fällen arbeiten mehrere Microservices an der Verarbeitung einer Anfrage zusammen. Wenn nun in der Verarbeitungskette dieser Microservices ein Fehler auftritt, kann es in bestimmten Fällen schwierig sein, das Problem zu finden, selbst wenn jeder Microservice ordnungsgemäß Log-Dateien schreibt und diese an einer zentralen Stelle abgelegt sind. Genau für dieses Problem bieten sich sogenannte „Correlation IDs“ an: Die zentrale Idee einer Correlation ID besteht darin, eine spezifische ID von einem Service zu erzeugen, der als zentraler Einstiegspunkt agiert. Diese ID kann an andere Services beispielsweise in Form eines HTTP-Headers weitergegeben und in den Services verwendet werden, um Log-Einträge mit Requests korrelieren zu können.

AWS bietet für dafür den Service AWS X-Ray [8]. AWS X-Ray macht es Entwicklern einfach, Zugriffs-Dynamiken in verteilten Anwendungen zu analysieren und Fehler zu beheben. Mit X-Ray können Entwickler verstehen, wie ihre Anwendung und die zugrundeliegenden Services arbeiten, um Ursachen von Leistungsproblemen und Fehlern zu identifizieren und zu beheben. X-Ray bietet eine End-to-End-Ansicht von Anforderungen, während sich diese durch ihre Anwendung bewegen, und zeigt eine Übersicht der Komponenten, die einer Anwendung zugrunde liegen. Der Application Load Balancer (ALB) fügt allen Anfragen einen HTTP-Header namens X-Amzn-Trace-Id hinzu. Gleiches gilt für Amazon API Gateway. Um X-Ray in eigenen Anwendungen einsetzen zu können, muss auf den EC2-Instanzen ein X-Ray-Daemon installiert werden. Die Anwendung kommuniziert mit dem Daemon über UDP und Port 2000. Bei Spring-Anwendungen muss dazu lediglich ein Filter der WebConfig hinzugefügt werden:

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import javax.servlet.Filter;
import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter

@Configuration
public class WebConfig {

  @Bean
  public Filter TracingFilter() {
    return new AWSXRayServletFilter();
  }
}

 

Chattiness

Durch das Aufteilen von monolithischen Applikationen in kleine Microservices erhöht sich zwangsläufig der Mehraufwand für die Kommunikation, denn die Microservices müssen untereinander Daten austauschen. In vielen Implementierungen wird REST über HTTP als Kommunikationsprotokoll verwendet, was ziemlich leichtgewichtig ist, aber dennoch bei großen Mengen an Anfragen Probleme bereiten kann.

In einigen Fällen kann es sinnvoll sein, einzelne Services zu konsolidieren, die viel miteinander kommunizieren. Falls man sich aber in einer Situation wiederfindet, in der mehr und mehr Services konsolidiert werden, um die Kommunikation unter den Services zu reduzieren, sollte die Problemdomäne und das Domänenmodell nochmals überprüft werden. Für Microservices ist es durchaus üblich, simple Protokolle wie HTTP zu nutzen. Die Nachrichten, die zwischen den Services ausgetauscht werden, können aber in unterschiedlichen Formaten vorliegen wie beispielsweise JSON, YAML oder einem effizienten Binärformat.

Fazit

Microservice-Architekturen sind ein verteilter Ansatz, der dabei hilft, die Limitierungen von traditionellen monolithischen Architekturen zu überwinden. Microservices helfen, Architekturen und Organisationen zu skalieren und die Release-Zyklen zu verkürzen. Herausforderungen bestehen in der gestiegenen Gesamtkomplexität der Architektur und den damit verbundenen betrieblichen Themen. AWS bietet ein großes Portfolio an verwalteten Services, die Produktteams dabei helfen, Microservices-Architekturen zu bauen und dabei die Komplexität in Bezug auf Architektur und Organisation zu minimieren.

Geschrieben von
Sascha Möllering
Sascha Möllering
Sascha Möllering arbeitet als Solutions Architect bei der Amazon Web Services Germany GmbH. Seine Interessen liegen in den Bereichen Automation, Infrastructure as Code, Distributed Computing, Container und JVM.
Matthias Jung
Matthias Jung
As Solutions Architect for Amazon Web Services, Matthias has helped many German startups to migrate their applications to the cloud and build highly reliable, scalable, and cost efficient backend architectures. Prior to AWS, Matthias worked as software engineer and architect and founded his own startup in the cloud security space. Matthias holds a PhD in Communication Systems and Network Protocols from the University of Nice (France) and a Master of Computer Science from the University of Mannheim.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.