Wir bringen eine Java-Anwendung in die Cloud - Teil 3

Migration nach AWS: Verwendung von Containern

Sascha Möllering

©Shutterstock / Dabarti CGI

In diesem Artikel werfen wir einen genaueren Blick darauf, wie wir die Java-basierte Anwendung mit Hilfe von Containern und einigen Restrukturierungen in Richtung Microservices deutlich wartbarer und dynamischer gestalten können.

Im zweiten Teil unserer Artikelserie haben wir gesehen, wie sich in der bestehenden Architektur mit Managed Services (Abb. 1) der Aufwand für die Bereitstellung und den Betrieb von Infrastruktur einer Java-basierten Webanwendung signifikant verringern lässt.

Abb. 1: Die Architektur unserer Anwendung aus dem zweiten Artikel

Abb. 1: Die Architektur unserer Anwendung aus dem zweiten Artikel

In Abbildung 1 sehen wir den aktuellen Stand unserer Architektur: Der Application Load Balancer empfängt alle Anfragen und verteilt sie auf die Apache-Webserver. Sie liefern statische Inhalte aus und leiten Anfragen nach dynamischen Inhalten an die Apache-Tomcat-Server weiter. Die Web- und Anwendungsserver laufen als EC2-Instanzen (Kasten: „AWS-Dienste für Containermanagement“). Die Anwendung nutzt Amazon Relational Database Service (RDS) MySQL als Datenbank. Häufige Datenbankabfragen speichert die Anwendung in Amazon ElastiCache für Redis.

AWS-Dienste für Containermanagement

Relevante AWS-Dienste für Containermanagement lassen sich in drei Kategorien unterteilen: Registry, Orchestrierung und Compute. Amazon Elastic Container Registry (ECR) [1] ist eine Docker-Container-Registry, um Docker-Container-Images sicher zu speichern, zu verwalten und bereitzustellen. Amazon Elastic Container Service (Amazon ECS) [2] ist ein Container-Orchestrierungsdienst, der Docker-Container unterstützt und es ermöglicht, containerisierte Anwendungen auf AWS auszuführen und zu skalieren. Der Amazon Elastic Kubernetes Service (Amazon EKS) [3] vereinfacht die Bereitstellung, Verwaltung und Skalierung von containerisierten Anwendungen mit Kubernetes auf AWS. Beide Orchestrierungsdienste werden von AWS Fargate [4] unterstützt, das die Verwaltung der Server und Cluster für die Container übernimmt. Somit ist der Kunde nicht für die Skalierung und das Patchmanagement der Clusterknoten verantwortlich. Amazon Elastic Compute Cloud (Amazon EC2) [5] ist ein Dienst, der sichere, skalierbare Rechenleistung in der Cloud bietet.

 

Artikelserie

Teil 1: Warum wechseln?

Teil 2: Verwendung von Managed Services

Teil 3: Verwendung von Containern

Teil 4: Serverless mit AWS Lambda

Teil 5: Optimierung der Architektur

Teil 6: Sicherheit und Verschlüsselung

Container Best Practices

Die Größe von Docker Images hat einen großen Einfluss auf die Ausführbarkeit von Containern. Starten wir einen Service in einem Amazon-ECS- oder Kubernetes-Cluster, müssen typischerweise mehrere Container auf verschiedene Containerinstanzen oder Worker Nodes verteilt werden. Der Docker Agent auf dem Host lädt dazu das Image aus ECR und startet die Container. Nehmen wir nun an, dass die Anwendung wegen eines temporären Anstiegs der Anfragen skaliert werden muss. Neue Container werden nun eventuell auf weiteren Hosts gestartet. Das bedeutet, dass der Agent sich das Image aus der Registry laden muss. Die Dauer zum Laden des Image aus der Registry wirkt sich somit direkt auf die Skalierung aus. Durch die Reduktion der Größe des Docker Image kann die Zeit reduziert werden, um neue Container zu starten. Es existieren unterschiedliche Möglichkeiten, die Größe eines Docker Image zu reduzieren. Eine sehr effektive Methode ist die Verwendung eines mehrstufigen Docker Builds: Da im Build-Prozess Dateien benutzt werden, die zur Laufzeit nicht mehr notwendig sind, werden in der ersten Stufe die Artefakte gebaut. In der zweiten Stufe werden die Artefakte in ein neues Image kopiert. Eine weitere, kombinierbare Methode zur Reduktion ist die Nutzung eines minimalistischen Betriebssystems wie beispielsweise Alpine Linux oder debian:<suite>-slim. Tabelle 1 zeigt die unterschiedlichen Größen und die Unterschiede zwischen populären Base Images ziemlich deutlich (die genauen Größenangaben unterscheiden sich von Version zu Version).

Repository Größe
node:latest 934 MB
java:latest 634 MB
node:slim 178 MB
debian:10-slim 69,2 MB
ubuntu:latest 64,2 MB
alpine:latest 5,55 MB
busybox 1,22 MB

Tabelle 1: Größe unterschiedlicher Base Images

Aufteilung in Microservices

Um die Vorteile von Containern nutzen zu können, sollte die bestehende Anwendung von einem monolithischen Ansatz in einzelne Microservices heruntergebrochen werden. Microservices sind ein architektonischer und organisatorischer Ansatz für die Softwareentwicklung. Sie verkürzen die Bereitstellungszyklen, fördern Innovation, Eigenverantwortung, Wartbarkeit und Skalierbarkeit. Üblicherweise arbeiten kleine Teams unabhängig voneinander an jeweils einem Microservice, was einen agilen Ansatz erlaubt. Microservices kommunizieren über klar definierte APIs, die unabhängig voneinander eingesetzt werden können.

Abb. 2: Eigenschaften von Microservices

Abb. 2: Eigenschaften von Microservices

Microservices haben dabei folgende Eigenschaften (Abb. 2):

  • Polyglot: Für die Implementierung von Microservices können unterschiedliche Sprachen und Plattformen gewählt werden.

  • Do one thing well: Microservices fokussieren sich auf eine spezifische Funktionalität.

  • You build it, you run it: Das Team, das für die Implementierung des Service zuständig ist, ist auch verantwortlich für den Betrieb in Produktion.

  • Black Box: Microservices bieten eine einfache Schnittstelle und verbergen die Implementierungsdetails.

  • Independent: Microservices können unabhängig voneinander ausgerollt werden.

  • Decentralised: Es existiert kein zentraler Service für die Orchestrierung, Services kommunizieren direkt untereinander.

In unserem konkreten Fall haben wir eine Java-Anwendung auf Basis der Servlet Engine Apache Tomcat, die auf eine Amazon-RDS-MySQL-Datenbank [6] und Amazon ElastiCache for Redis [7] zugreift. Für die Transformation des bestehenden Monolithen in Microservices bietet sich ein iterativer Prozess an. Nach und nach werden bestehende Teile der Anwendung in einzelne Services umgeschrieben. Diese Services laufen – bis zur vollständigen Transformation der Anwendung – mit dem Rest des Monolithen. Martin Fowler bezeichnet diese Strategie der Anwendungsmodernisierung als die „Strangler Fig Application“ [8]. Eine beliebte Strategie besteht darin, bestehende Module der monolithischen Anwendung, die eine vollständige Domäne abbilden, als eigenen Service auszugliedern. In Abhängigkeit davon, ob die einzelnen Anwendungsteile synchron oder asynchron miteinander kommunizieren sollen, können REST APIs oder Message Queues verwendet werden, um Nachrichten zwischen den einzelnen Services auszutauschen. Mehr zum Thema Microservices in AWS kann im Whitepaper „Implementing Microservices on AWS“ [9] gefunden werden.

Spring Boot als Basis-Framework

Ein beliebtes Framework zur Implementierung von Microservices ist Spring Boot [10]. Spring Boot macht es einfach, produktionsreife und Spring-basierte Anwendungen zu erstellen. Der Ansatz von Spring zum Aufbau von RESTful Web Services besteht darin, HTTP-Anfragen durch einen Controller zu verarbeiten. Die Annotation @RestController exponiert eine Spring Bean als REST-Schnittstelle.

Es wäre durchaus möglich, die Anwendung als traditionelle WAR-Datei für das Deployment auf einem Applikationsserver zu paketieren. Zur besseren Ausführbarkeit empfiehlt sich eine JAR-Datei, die alle notwendigen Abhängigkeiten enthält. Dafür benötigen wir natürlich eine main()-Methode. Spring Boot bietet die Möglichkeit, unterschiedliche eingebettete Servlet Engines zu nutzen. Populäre Implementierungen sind Apache Tomcat oder Jetty. Das Beispiel in Listing 1 nutzt die @SpringBootApplication-Annotation, um unter anderem den Autokonfigurationsmechanismus von Spring Boot zu verwenden.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class MainApplication {

  @RequestMapping("/")
  public String response() {
    return "Hello Docker";
  }

  public static void main(String[] args) {
    SpringApplication.run(MainApplication.class, args);
  }
}

In Abhängigkeit davon, ob wir auf weitere Services zugreifen möchten, benötigen wir ggf. noch zusätzliche Pakete wie das AWS SDK für Java [11], um auf AWS Services wie Amazon Simple Queue Service (SQS) [12] oder Amazon Kinesis Data Streams [13] zuzugreifen, die in unserer Implementierung für die asynchrone Entkopplung der einzelnen Services genutzt werden können. In unserem Beispiel nutzen wir SQS, für das im reichhaltigen Fundus der Spring-Cloud-Bibliotheken [14] auch eine Integration existiert. Leider unterstützt Spring Cloud noch nicht das aktuelle AWS SDK für Java in der Version 2, das eine umfassende Überarbeitung der Codebasis von Version 1.x darstellt. Es basiert auf Java 8+ und bietet einige häufig benötigte Funktionen [15]. Dazu gehören die Unterstützung von non-blocking I/O und die Möglichkeit, zur Laufzeit eine andere HTTP-Implementierung einzubinden. Für die Einbindung der Messaging-Bibliotheken von Spring Cloud benötigen wir dafür zusätzlich folgende Abhängigkeit in unserer pom.xml-Datei:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-aws-messaging</artifactId>
  <version>2.2.0.RELEASE</version>
</dependency>

Das Versenden von Nachrichten ist damit sehr einfach (Listing 2) und Gleiches gilt auch für den Empfang:

@SqsListener("myQueueName")
public void queueListener(Person person) {
  // ...
}
public class SqsSender {

  private final QueueMessagingTemplate queueMessagingTemplate;

  @Autowired
  public SqsSender(AmazonSQS amazonSqs) {
    this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSqs);
  }

  public void send(String message) {
    this.queueMessagingTemplate.send("myQueueName", MessageBuilder.withPayload(message).build());
  }
}

Die Spring-Cloud-Bibliothek unterstützt auch Amazon Simple Notification Service (SNS) [16], einen hochverfügbaren, sicheren und vollständig verwalteten Pub/Sub-Messaging-Dienst. Er ermöglicht es, Anwendungen in einer verteilten Architektur zu entkoppeln. Amazon SNS bietet Topics für Push-basiertes Many-to-Many-Messaging und ist optimiert für hohen Durchsatz. Mit Hilfe von Amazon SNS Topics können Publisher-Systeme Nachrichten an eine große Anzahl von Subscriber-Endpunkten zur parallelen Verarbeitung verteilen, einschließlich SQS-Queues, AWS-Lambda-Funktionen [17] und HTTP WebHooks. Typische Einsatzgebiete für die Kombination von SNS und SQS sind Fanout und Filtering. Typischerweise hat ein Event-getriebenes System eine Quelle für Nachrichten, die unterschiedliche Ziele hat, das heißt, mehrere Microservices reagieren auf eine bestimmte Nachricht. SNS ist dafür sehr gut geeignet, da es eine Vielzahl von Subscriptions pro Topic unterstützt. Ein besonders interessantes Feature von SNS besteht in der Filterung von Nachrichten. Ein Nutzer definiert eine Subscription Filter Policy mit Attributnamen und ordnet diesen Attributnamen eine Liste von Werten zu. Wenn Amazon SNS Nachrichtenattribute anhand der Subscription Filter Policy auswertet, ignoriert es Nachrichtenattribute, die nicht in der Richtlinie angegeben sind. Das Beispiel in Listing 3 zeigt, wie Nachrichten an ein SNS Topic gesendet werden.

public class SnsSender {
  private final NotificationMessagingTemplate notificationMessagingTemplate;

  @Autowired
  public SnsSender(AmazonSNS amazonSns) {
    this.notificationMessagingTemplate = new NotificationMessagingTemplate(amazonSns);
  }

  public void send(String subject, String message) {
    this.notificationMessagingTemplate.sendNotification("myTopicName", message, subject);
  }
}

Um unseren Redis-Cache im Amazon-ElastiCache-Cluster anzusprechen, existieren ebenfalls Integrationen in Spring Cloud, alternativ kann auch Jedis direkt verwendet werden. Für den Zugriff auf die MySQL-Datenbanken im Kontext von Amazon Relational Database Service (RDS) sind keine zusätzlichen AWS-spezifischen Bibliotheken notwendig: Die Datenbank kann wie gewohnt mit Hilfe von JPA oder dem JdbcTemplate angesprochen werden.

Clustermanagement mit AWS Fargate

Für die Implementierung unserer Microservices nutzen wir Amazon ECS mit AWS Fargate, da wir keine Server verwalten und betreiben wollen. Dazu erstellen wir zunächst einen Amazon-ECS-Cluster. Mit Fargate sind Cluster eine logische Infrastruktur und Berechtigungsgrenze, mit der Gruppen von Tasks isoliert und verwaltet werden können. Jeder Fargate-Task hat seine eigene Isolationsgrenze und teilt den zugrunde liegenden Kernel, CPU-Ressourcen, Speicherressourcen und die virtuelle Netzwerkkarte nicht mit einem anderen Task. Für unsere Anwendung müssen wir eine sogenannte TaskDefinition erstellen. Sie beschreibt die Container- und Volume-Definitionen eines Amazon-ECS-Tasks. Sie ist vergleichbar mit einem Pod Template bei Kubernetes. Diese Textdatei definiert, welche Docker Images verwendet werden sollen, ebenso die erforderlichen Ressourcen und andere Konfigurationen, die sich auf den Start der TaskDefinition über einen Amazon ECS Service oder einen Task beziehen. Die beispielhafte TaskDefinition in Listing 4 umfasst einen Spring-Boot-Container, bei dem die Ports 8080 und 8443 freigegeben werden. Alle Logs des Containers werden über den awslogs-Log-Driver nach Amazon CloudWatch Logs [18] in die Region eu-central-1 (Frankfurt) mit dem Präfix ecs transportiert. Bei dieser TaskDefinition ist der Teil unter portMappings besonders interessant: Wenn Container in einem Task mit dem awsvpc- oder host-network-Modus verwendet werden, kann der hostPort entweder leer gelassen oder auf den gleichen Wert wie der containerPort gesetzt werden. Bei leerem Wert für hostPort wird ein zufälliger Port zwischen 49153 und 65535 gewählt. Diese Funktionalität nennt sich Dynamic Port Mapping und kann dazu genutzt werden, mehrere Tasks mit demselben Containerport auf demselben Host laufen zu lassen.

---
memory: '1024'
networkMode: awsvpc
cpu: '512'
family: spring
requiresCompatibilities:
- FARGATE
containerDefinitions:
- portMappings:
  - hostPort: 8080
    containerPort: 8080
    protocol: tcp
  - hostPort: 8443
    containerPort: 8443
    protocol: tcp
  essential: true
  name: spring
  image: smoell/springboot-docker:0.2
  logConfiguration:
    logDriver: awslogs
    options:
      awslogs-group: "/ecs/spring"
      awslogs-region: eu-central-1
      awslogs-stream-prefix: ecs
  cpu: 512
  memoryReservation: 1024

Da wir neben den Containern, die dauerhaft laufen sollen, auch einen Load Balancer benötigen, der die HTTP Requests auf die einzelnen Container verteilt, müssen wir einen Service definieren. Eine Service-Definition spezifiziert, welche TaskDefinition mit einem Dienst verwendet werden soll, wie viele Instanzen dieses Tasks ausgeführt werden, welche Load Balancer (falls vorhanden) mit den Tasks verknüpft werden, sowie andere Dienstparameter. Wenn eine Task-Instanz aus irgendeinem Grund fehlschlägt oder stoppt, startet der Amazon ECS Service Scheduler eine weitere Instanz der TaskDefinition, um sie zu ersetzen und die gewünschte Anzahl von Tasks im Service abhängig von der verwendeten Scheduling-Strategie aufrechtzuerhalten. Wenn ein Task in einem Service stoppt, wird der Task beendet und ein neuer Task gestartet. Dieser Prozess wird fortgesetzt, bis der Service die Anzahl der gewünschten laufenden Tasks erreicht hat.

Der Service Scheduler drosselt den Start neuer Task-Instanzen, wenn sie wiederholt nicht starten. Wenn ein Task gestoppt wird, ohne einen RUNNING-Zustand erreicht zu haben, beginnt der Service Scheduler, die Startversuche schrittweise zu verlangsamen und sendet eine Service-Ereignismeldung. Dieses Verhalten verhindert, dass unnötige Ressourcen für fehlgeschlagene Tasks verwendet werden, bis das Problem gelöst wird. Nach der Aktualisierung des Service setzt der Scheduler das normale Verhalten fort.

Wie bereits erwähnt, werden die Logs des Tasks in CloudWatch Logs abgelegt und können dort analysiert werden. Oft reicht das intensive Lesen von Logdateien für die Fehlersuche aus. Doch hin und wieder tauchen Fehler auf, die ausschließlich durch die Kommunikation zwischen den einzelnen Services untereinander sichtbar werden. Wie können wir Fehler solcher Art finden? AWS X-Ray [19], ein System zur Analyse und zum Debugging verteilter Anwendungen, kann an dieser Stelle helfen. AWS X-Ray unterstützt Entwickler bei der Analyse und Fehlersuche im produktiven Betrieb und bei verteilten Anwendungen, wie sie beispielsweise mit einer Microservices-Architektur erstellt werden. X-Ray erfasst Daten zur Kommunikation in verteilten Anwendungen, um die Ursache von Leistungsproblemen und Fehlern zu identifizieren und zu beheben. Es bietet eine End-to-End-Ansicht der Anfragen und zeigt eine Karte der zugrunde liegenden Komponenten der Anwendung. Um X-Ray mit Fargate zu verbinden, ist es notwendig, den X-Ray Daemon als sogenannten Sidecar-Container laufen zu lassen. Im Prinzip bedeutet das, dass unser Fargate-Task nicht nur einen, sondern zwei Container hat. Beide Container werden zusammen gestartet und die Applikation sendet die Trace-Daten an den X-Ray-Container auf dem Port 2000. Wir müssen also unsere TaskDefinition noch um den Abschnitt aus Listing 5 erweitern.

---
name: xray-sidecar
mountPoints: []
image: amazon/aws-xray-daemon
cpu: 32
memoryReservation: 256
portMappings:
- protocol: udp
  containerPort: 2000
  hostPort: 2000
logConfiguration:
  logDriver: awslogs
  options:
    awslogs-region: eu-central-1
    awslogs-stream-prefix: ecs
    awslogs-group: "/ecs/spring"

Die Kommunikation mit anderen Diensten wird mit Aspekten (AOP) über den Sidecar-Container umgeleitet. In unserer Spring-Boot-Anwendung benötigen wir dazu eine weitere Abhängigkeit:

<dependency> 
  <groupId>com.amazonaws</groupId> 
  <artifactId>aws-xray-recorder-sdk-spring</artifactId> 
  <version>2.4.0</version> 
</dependency>

Die Klassen müssen entweder mit der @XRayEnabled-Annotation versehen sein oder die XRayTraced-Schnittstelle implementieren. Das veranlasst das AOP-System, die Funktionen der betroffenen Klasse für die X-Ray-Instrumentierung zu übernehmen. Der Code in Listing 6 verknüpft unsere Controller Beans mit dem Aspect, der in AbstractXRayInterceptor implementiert ist. In Listing 7 ist der Quelltext einer Klasse zu sehen, die von X-Ray instrumentiert wird.

@Aspect
@Component
public class XRayInspector extends AbstractXRayInterceptor {
  
  private static final Logger logger = Logger.getLogger(XRayInspector.class);
  
  @Override
  protected Map<String, Map<String, Object>>; generateMetadata(ProceedingJoinPoint proceedingJoinPoint, Subsegment subsegment) throws Exception {
    logger.log("your meta data");
    return super.generateMetadata(proceedingJoinPoint, subsegment);
  }

  @Override
  @Pointcut("@within(com.amazonaws.xray.spring.aop.XRayEnabled) && bean(*Controller)")
  public void xrayEnabledClasses() {}

}
@Service
@XRayEnabled
public class MyServiceImpl implements MyService {
  private final MyEntityRepository myEntityRepository;
  
  @Autowired    
  public MyServiceImpl(MyEntityRepository myEntityRepository) {
    this.myEntityRepository = myEntityRepository;
  }
  
  @Transactional(readOnly = true)
  public List<MyEntity> getMyEntities(){
    try(Stream<MyEntity> entityStream = this.myEntityRepository.streamAll()){
      return entityStream.sorted().collect(Collectors.toList());
    }
  }
}

Containerisierung der Anwendung

Wir haben nun die TaskDefinition, die ServiceDefinition und die JAR-Datei. Als letzten Teil für unser Puzzle benötigen wir noch ein Dockerfile, um aus der JAR-Datei ein Docker Image zu erstellen, aus dem zur Laufzeit Docker-Container erstellt werden. Unser Dockerfile ist relativ simpel gehalten. Für den produktiven Betrieb einer Anwendung sollte es sich an den Best Practices von Docker [20] orientieren. Die Sicherheit wird erhöht, wenn ein Container ohne Root-Privilegien läuft. Der Befehl USER bestimmt dazu den gewünschten User (Listing 8).

FROM openjdk:8-jdk-alpine
 
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} springboot-docker.jar
 
ENTRYPOINT ["java","-jar","/springboot-docker.jar"]
 
EXPOSE 8080

Mit dem Befehl docker build -t smoell/springboot-docker bauen wir das Docker Image, das die Basis für unsere Architektur ist. Falls noch zusätzliche Images benötigt werden, um die Anwendung in Microservices aufzuteilen, ist das Vorgehen analog. Die Images müssen wir in einer Registry speichern. In AWS bietet sich dafür ECR an. Amazon ECR ist in ECS integriert, was Ihre Entwicklung bis zum Produktionsablauf vereinfacht. Natürlich werden von ECS bzw. Fargate auch andere Registries – auch mit Authentifizierung – unterstützt. Damit ECS Images aus einem privaten Repository abrufen kann, benötigt der Service ein Secret im AWS Secrets Manager mit den entsprechenden Registrierungsdaten. Eine AWS-Identity-Access-Management-Rolle erlaubt ECS den Zugriff auf das Secret sowie die Ausführung von ECS-Tasks.

Abb. 3: Die Architektur unserer containerisierten Anwendung

Abb. 3: Die Architektur unserer containerisierten Anwendung

In Abbildung 3 ist die vollständig containerisierte Anwendung zu sehen. Diese liegt in der Amazon Elastic Container Registry als Image vor. Eine TaskDefinition referenziert die Images der Anwendung und die des Sidecar-Containers für die Analyse der Anwendung mit AWS X-Ray. Der ECS Service definiert die Anzahl der Tasks bei wechselnder Last. Die Tasks laufen in einem AWS-Fargate-Cluster mit vollständig verwalteten Servern. Jeder Task hat seine eigene Security Group, die auf die notwendigen Verbindungen beschränkt ist, etwa zum Cache in Amazon ElastiCache for Redis und der relationalen Datenbank RDS MySQL.

Fazit

Im dritten Teil unserer Artikelserie haben wir die bestehende monolithische Anwendung in verteilte, auf Spring Boot basierende Microservices zerlegt. Die einzelnen Services kommunizieren je nach Anwendungsfall entweder synchron über HTTP oder asynchron mit Amazon SQS als vollständig verwaltete Queue. Falls ein Fanout notwendig ist, kann zusätzlich noch Amazon SNS genutzt werden. Amazon ElastiCache und Amazon RDS bleiben im aktuellen Zustand unserer Architekturevolution unverändert bestehen. Die einzelnen Services laufen in Docker-Containern in Amazon ECS mit AWS Fargate, und AWS X-Ray wird für die Überwachung der Aufrufe in diesem verteilten System verwendet. Wir nutzen die umfangreiche Bibliothek von Spring Cloud, um den Implementierungsaufwand für die Nutzung der verwalteten AWS Services zu minimieren. In der nächsten Folge unserer Serie werden wir uns vollständig Serverless-Architekturen widmen und intensiv AWS Lambda nutzen.

 

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.
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: