Wie man Serverless-Architekturen erfolgreich einsetzt
Serverless-Architekturen versprechen eine verbesserte Auslastung, verkürzte Time-to-Market-Zyklen, Skalierbarkeit und Ausfallsicherheit. Der Aufbau einer Serverless-Lösung birgt jedoch eigene Herausforderungen – insbesondere für Teams, die eher mit traditionellen Setups vertraut sind.
Function as a Service und die Grundsätze der Entwicklung guter Cloud Apps
Eine Serverless-Anwendung ist normalerweise ereignisbasiert. Ereignisse können bspw. die HTTP-Anforderung eines Clients, eine Statusänderung in einer Datenbank, ein Fenster aus einem Stream oder eine Nachricht von einem IoT-Gerät sein. Über eine FaaS (Function as a Service) wird das Ereignis aufgenommen. Die Cloud Service Provider (CSP) unterstützen i.d.R. eine Vielzahl von Programmiersprachen: AWS bietet den Datenverarbeitungsservice AWS Lambda, Microsoft die Azure Functions.
Entwickler von Serverless-Anwendungen können sich – anstatt sich an infrastrukturbezogenem Code abzuarbeiten – voll auf die Funktionalität ihrer Applikation konzentrieren, denn die CSP kümmern sich um die Bereitstellung, Skalierung und Sicherheit der Umgebung.
Funktionalität und Logik einer Applikation hängen natürlich in den meisten Fällen von den bereitstehenden und zu verarbeitenden Daten ab. Um eine horizontale Skalierung zu ermöglichen, sollten keine Status zu den Funktionen hinzugefügt werden (Zwölf-Faktor-App). Besser ist es, wenn die Applikation direkt mit anderen Diensten wie Datenbanken, Caches oder Suchmaschinen interagiert.
Serverless-Architektur am Beispiel einer API-basierten Webanwendung
Alle Anfragen von Clients (Browser, native Anwendungen usw.) richten sich an das API-Gateway. Das Gateway behandelt Probleme wie Autorisierung und Ratenbegrenzung. Die Lambda-Funktionen sind über eine RESTful-API verfügbar: Wenn eine Anfrage eingeht, wird sie in ein Ereignis übersetzt und ruft eine Lambda-Funktion auf. Dieses Setup ermöglicht auch den Aufbau einer Microservices-Architektur, da jedem REST-Endpunkt und jeder Methode verschiedene Funktionen (oder sogar unterschiedliche Systeme) zugeordnet werden können. Die Lambda-Funktionen führen die Geschäftslogik aus und interagieren mit einer Datenbank. In diesem Beispiel verwenden wir DynamoDB, einen hoch skalierbaren NoSQL-Datenbankdienst. Die Funktion skaliert automatisch und wird auf Basis der bereitgestellten Lese- und Schreibkapazitätseinheiten abgerechnet.
Obwohl DynamoDB für einen schnellen und dauerhaften Datenspeicher sorgt, fehlen uns noch erweiterte Abfragefunktionen. Für die Volltextsuche und Aggregation von Abfragen setzen wir daher auf ElasticSearch, eine „Suchmaschine-as-a-Service“.
Um die Daten aus der Datenbank mit dem Suchindex zu synchronisieren, greifen wir wieder auf Lambda zurück. Durch die Registrierung einer Lambda-Funktion im DynamoDB-Ereignisdatenstrom erhält unsere Funktion für alle Datenbankaktualisierungen (Erstellen, Aktualisieren, Löschen) ein Ereignis und aktualisiert den Suchindex entsprechend. Besonders komfortabel dabei ist, dass sowohl die Datenbank als auch unsere Lambda-Funktion automatisch nach Bedarf skalieren und wir uns keine Gedanken über Updates o.ä. machen müssen.
Es gibt jedoch eine Einschränkung: Anders als der Name vermuten lässt, ist der derzeit von AWS angebotene ElasticSearch-Dienst weniger elastisch als Lambda und DynamoDB. Die ElasticSearch-Server werden zwar für uns verwaltet. Auch sind die Instanztypen und die Anzahl der Instanzen konfigurierbar. Aber sie skalieren nicht automatisch. Das kann dazu führen, dass DynamoDB und Lambda bei einem Traffic Peak nahtlos skaliert werden – die ElasticSearch aber herunterfahren muss, da sie nicht mit genügend Ressourcen ausgestattet wurde.
Natürlich kann man zur dynamischen Anpassung der Konfiguration eigene Skripte schreiben oder eine Warteschlange zum Drosseln der Anfragen aufsetzen. Nur widerspricht das den Serverless-Prinzipien, da für uns Aufwand und Komplexität entstehen. Deshalb hoffe ich, dass AWS sein Angebot für Serverless mit ElasticSearch erweitert – bspw. durch die kürzlich angekündigte Aurora Serverless-Funktion. (Apropos diese Funktion bietet eine gute Alternative zu dem hier beschriebenen Setup, wenn Sie SQL-basierte Datenbanken bevorzugen.)
Serverless und die Herausforderung für traditionelle Organisationen
Um das Serverless-Konzept optimal zu nutzen, sollten so viele Aufwände wie möglich automatisiert werden. Besonders beim Einsatz von FaaS stoßen traditionelle Teams oft an ihre Grenzen: Die Anzahl der Funktionen wächst schnell über den Punkt hinaus, an dem ein manuelles Erstellen, Aktualisieren oder Löschen noch möglich ist.
Je mehr Cloud-Dienste Sie verwenden, desto schwieriger wird es außerdem, Anwendungen lokal auszuführen. So führen beim Einsatz von Mocks kleine Verhaltensunterschiede oft zu unterschiedlichen Ergebnissen zwischen der lokalen und der Cloud-Umgebung. Das Bereitstellen von Cloud-Umgebungen für Tests kann hier Abhilfe schaffen: Bspw. betreiben wir ein Setup, bei dem automatisch ein neueres Stack in der Cloud erstellt wird, wenn ein neuer Feature-Zweig in unserem Git-Repository erzeugt wird. Dadurch wird eine isolierte Entwicklung und Prüfung möglich. Nach der Integration des Feature in den Hauptzweig wird die temporäre Umgebung automatisch gelöscht.
Serverless-Komponenten bringen bereits alle nötigen APIs und Vorlagen mit und sind damit bestens für eine Automatisierung konzipiert. Um Serverless-Architekturen erfolgreich einzusetzen, müssen die Anwenderteams sich mit diesen intensiv auseinandersetzen. Sie müssen stärker in infrastrukturbezogenes Scripting bzw. Templating involviert werden – was im Rahmen der Zusammenarbeit als DevOps-Team üblich ist, für traditionellere Teams jedoch erst einmal eine Herausforderung darstellt.