Stefan Zörner
28.03.2024
Ein großes Cloud-Versprechen ist seit jeher „Pay per Use“ – Bezahlung nach Verbrauch. Bereits frühe Angebote wie die virtuellen Maschinen von Amazon Web Services (AWS) halten dies ein. Allerdings erfolgt die Abrechnung bei AWS EC2 (Elastic Compute Cloud) vergleichsweise grob: pro Maschine, stundenweise. Das Function-as-a-Service-Modell kann das Ganze nicht nur auf eine neue Stufe heben: Abrechnung pro Aufruf, CPU- und Speicherverbrauch. Es führt auch zu einer neuen Art, Anwendungen zu denken. Und zu völlig neuen Skalierungsmöglichkeiten und Entwicklungsgeschwindigkeiten. Vorreiter dieses Ansatzes war 2014 wiederum Amazon. Dieses Porträt führt uns in die Serverless-Welt, mit AWS Lambda als Protagonisten.
Steckbrief: Compute-Service AWS Lambda
Name: AWS Lambda
Software-Gattung: Serverless Compute-Service
Veröffentlicht: 2014 (General Availability: April 2015)
Herkunft/Ursprung: Proprietär, Amazon Web Service
Zielplattform: Public Cloud (Linux, vor dem Nutzer versteckt)
Programmiersprache(n): Rust (unter anderem)
Homepage: aws.amazon.com/lambda/
Was genau ist Cloud Computing? Gängige Definitionen fordern die Präsenz charakteristischer Eigenschaften ein (siehe z.B. [NIST2011]). Hierzu zählt die Möglichkeit für Entwicklungsteams, Ressourcen selbst zu buchen und ohne manuelle Aktivität Dritter rasch zu erhalten („Self Service“). Weiterhin gehört der möglichst elegante Umgang mit wachsender und schrumpfender Last dazu (Elastizität). Schließlich ist ein Bezahlmodell gefragt, das mit sehr geringen (im Idealfall keinen) Fixkosten und ohne lange Anbieterbindung auskommt, und nur tatsächlich genutzte Ressourcen in Rechnung stellt („Pay per Use“).
Je nach Liefermodell (Public vs. Private Cloud) und den genutzten Services lassen sich die zweite und dritte Eigenschaft (Elastizität und Pay per Use) mitunter nur eingeschränkt darstellen. Das liegt vor allem daran, dass sich im klassischen IaaS (Infrastructure-as-a-Service) Ressourcen wie virtuelle Maschinen nicht feingranular buchen lassen.
Ein weniger einheitlich verwendetes und verstandenes Schlagwort als Cloud Computing ist „Serverless“. Manche setzen es mit Function-as-a-Service gleich, die Fachliteratur ist hier differenzierter. P. Sbarski et al. [Sba+2021] beispielsweise definieren Serverless als Attribut, das sich jedem Software- oder Serviceangebot voranstellen lässt, das als Dienst oder Ressource genutzt wird und nur bei Nutzung Kosten verursacht. Damit gehen auch Persistenzlösungen wie Google Firebase oder Authentifizierungsdienste wie AWS Cognito als Serverless durch. Denn abgerechnet wird dort nach Größe der gespeicherten Daten und Anzahl der Lese- und Schreiboperationen, bzw. nach Anzahl monatlich aktiven Nutzern, also solchen mit Identitätsvorgängen (Registrierung, Anmeldung, Passwortänderung …).
M. Roberts et al. [Rob+2017] erklären den Begriff Serverless, indem sie ihn für die Anwendungsentwicklung in zwei Modelle aufdröseln: BaaS und FaaS. BaaS steht dabei für Backend-as-a-Service und Softwarekomponenten von der Stange. Sie übernehmen Domänen-unspezifische Aufgaben innerhalb der Anwendung. Persistenz- und Authentifizierungslösungen (z.B. Google Firebase, AWS Cognito, siehe oben) sind auch hier gute Beispiele. FaaS steht für Function-as-a-Service und eine Plattform, in der die eigene Geschäftslogik in Form von Funktionen bereitgestellt wird. BaaS und FaaS ist gemein, dass sich die Entwicklungsteams nicht mit Servern (egal ob Hardware oder virtualisiert) oder Betriebssystem(-prozessen) beschäftigen. Zitat aus [Rob+2017]: „Serverless bedeutet nicht, dass die Server verschwunden sind, sondern dass man sich nicht mehr um sie kümmern muss“. In Summe führen BaaS und FaaS zu Anwendungen, die völlig anders gebaut werden als konventionell (also vor „der Cloud“). Manche nennen das „Cloud Native“.
Der Function-as-a-Service-Ansatz erlaubt eigene Geschäftslogik in Form von Funktionen auszuführen und abstrahiert alles unterhalb der Geschäftslogik weg, also Laufzeitumgebungen wie Applikationsserver, Betriebssystem und Infrastruktur. Ein Cloud-Provider, dem es gelingt, eine entsprechende Umgebung zuverlässig und wirtschaftlich zu betreiben, kann seinen Kunden ein „echtes“ Cloud-Erlebnis im Sinne der charakteristischen Eigenschaften oben bieten. Amazon Web Services (AWS) tut dies seit 2014 mit AWS Lambda, dem Vorreiter des FaaS-Trends. Die beiden anderen großen Public Cloud-Anbieter haben mit ihren Angeboten schnell nachgezogen.
Abbildung 1 zeigt einen Zeitstrahl mit wichtigen Meilensteilen bei AWS Lambda, ergänzt um die beiden Marktbegleiter Microsoft (Azure Functions) und Google (Cloud Functions). Tabelle 1 zeigt zentrale Architekturziele von AWS Lambda; die Reihenfolge gibt Orientierung bezüglich deren Wichtigkeit. Die Möglichkeit, leicht Lösungen bauen zu können, steht dabei ganz oben. Wie lassen sich Anwendungen mit AWS Lambda entwickeln?
Ziel | Beschreibung (und zugehörige(s) Software-Qualitätsmerkmal(e)) |
Lösungen einfach entwickeln | Softwarelösungen auf Basis von AWS Lambda lassen sich einfach bauen, testen und auch ändern und erweitern. Teams erleben eine verkürzte Time-to-Market. (Wartbarkeit) |
Leichte Integration in andere Systeme | In AWS Lambda realisierte Funktionalität lässt sich leicht aktivieren und ermöglicht umgekehrt einen einfachen Zugriff auf andere Komponenten wie zum Beispiel Persistenzlösungen. (Kompatibilität) |
Zuverlässig, auch bei Lastspitzen | Geschäftslogik lässt sich unabhängig von der Last einfach und zuverlässig in AWS Lambda betreiben. Etwaige Fehler sind schnell gefunden und behoben. (Verfügbarkeit, Zuverlässigkeit) |
Schutz vor unberechtigtem Zugriff und anderen Angriffen | Funktionalität in Lambda ist vor unberechtigten Zugriffen geschützt. Insbesondere stellen die Funktionen kein leichtes Einfallstor für Angriffe auf nachgelagerte Ressourcen dar. (Sicherheit) |
Kosteneffizienter Betrieb | Das Bereitstellen und Ausführen von Geschäftslogik in AWS Lambda ist für Entwicklungsvorhaben preislich attraktiv. Teams profitieren von Skalierungseffekten und können sich effektiv vor Überraschungen durch Kostenexplosionen schützen. (Kosteneffizienz) |
Die eigene Geschäftslogik schreiben Entwicklerinnen und Entwickler in AWS Lambda als Funktionen, sogenannte Handler. Diese entsprechen einer bestimmten Signatur, die je nach Programmiersprache leicht variiert. Handler lassen sich etwa bei JavaScript (Node.js) und Python direkt in der AWS-Konsole schreiben und ändern (siehe Abbildung 2) – eine schöne Einladung zum Ausprobieren der Technologie.
Alternativ oder auch für Programmiersprachen wie Go oder Java nutzt man die gewohnte IDE und stellt AWS Lambda den Code (bzw. das Kompilat) z.B. über einen S3-Bucket bereit (S3 steht für Simple Storage Service). Innerhalb der Konsole lassen sich die Funktionen interaktiv aufrufen und direkt in der echten Umgebung testen. Das ist insbesondere spannend, wenn die Funktionen primär Glue-Code zwischen AWS-Diensten wie DynamoDB oder S3 bilden, was lokal deutlich schwieriger zu testen ist. Abgesehen davon, dass der Handler einer bestimmten Signatur genügen muss, um als Funktion in Lambda aktivierbar zu sein (für statisch getypte Sprachen stellt AWS hierzu APIs bereit), kann man in einer Funktion praktisch alles machen. Insbesondere eigene Bibliotheken einbinden, was die Verwendung bestehenden Codes erleichtert.
Zu beachten ist, dass die Funktionen selbst zustandlos sind. Um sich etwas dauerhaft zu merken legt man es außerhalb von Lambda ab. Weiterhin gibt es Vorgaben bezüglich der maximalen Laufzeit einer Funktion und des maximalen Speicherbedarfs (sowohl RAM als auch flüchtiger Speicher unter /tmp). Diese Grenzen haben sich mit der Zeit verschoben, aktuell (Stand Januar 2024) sind es 15 Minuten und 10 GB flüchtiger Speicher. Das klingt viel, sollte aber nicht zur Verschwendung einladen. Im Gegenteil: Da AWS als Cloud-Anbieter direkt nach Laufzeit und Speicherbedarf abrechnet, freuen sich alle über eine effiziente Implementierung – die Endanwender:innen ebenso wie Budget-Verantwortliche.
AWS stellt für die unterstützten Programmiersprachen SDKs bereit, um aus Handlern auf andere AWS-Dienste wie z.B. S3 oder DynamoDB zuzugreifen. Umgekehrt lassen sich Handler auf Basis unterschiedlichster Ereignisse aktivieren. Dies können Zustandsänderungen in anderen AWS-Services sein (eine Datei in S3 wurde geändert, ein neuer Benutzer in Cognito angelegt …), direkte Aufrufe z.B. per REST über ein API Gateway oder auch zeitgesteuert, z.B. 1x pro Stunde.
Wie führt AWS Lambda die Handler-Funktionen aus? Auch wenn sich Entwicklungsteams nicht im Detail darum kümmern müssen, ist es kein völliges Betriebsgeheimnis. Amazon stellt in Form von Whitepapers und Konferenzbeiträgen einige Informationen dazu bereit, die teilweise auch für eine effiziente Implementierung der Funktionen relevant sind.
Zunächst basiert bzw. läuft AWS Lambda auf etablierten und ausgereiften AWS-Services, allen voran EC2 (Elastic Compute Cloud) und SQS (Simple Queue Service), beide bereits verfügbar seit 2006. Einige Systemteile wurden speziell für Lambda entwickelt, vorrangig in Rust. Abbildung 3 zeigt den Aufbau des Services mit all seinen Komponenten, für Details siehe z.B. [Woo+2022]. Auf oberster Ebene folgt die Zerlegung einem AWS-typischen Muster für Services mit hohen Verfügbarkeitsanforderungen (siehe [Wei+2019]): der Zweiteilung in Control und Data Plane. Erstere ist dabei für Änderungen am System zuständig und beherbergt insbesondere die Tools zur Entwicklung von Lambda-Anwendungen. Die Data Plane stellt den reibungslosen Betrieb der Funktionen sicher.
Innerhalb der Data Plane zeigt Abbildung 3 zwei Ausführungspfade je nach Art des auslösenden Ereignisses für den Handler. Ein Pfad behandelt synchrone Funktionsaufrufe, etwa REST-Calls via HTTP über ein API Gateway. Der andere Pfad ist für die asynchrone Verarbeitung von Ereignissen zuständig, wie sie etwa bei eingehenden Nachrichten in Message Queues auftreten, oder in Datenströmen wie Apache Kafka oder Amazon Kinesis. Tatsächlich erfolgt die Abarbeitung eines Ereignisses immer über den synchronen Pfad (vgl. Woo+2022]), die asynchrone Seite routet sie quasi „rüber“.
Die eigentliche Ausführung des Handlers erfolgt auf einem Worker Host, der eine entsprechende Runtime bereithält. In diese wird der Code der Funktion geladen und dann in ihr ausgeführt. Der Placement Service stellt beim ersten Aufruf eine solche bereit („Kaltstart“). Spätere Aufrufe übernimmt diese schneller, allerdings sichert Lambda zu, dass ein Handler nicht mehrere Anfragen parallel abarbeitet. Dafür starten weitere Umgebungen, und damit das flink und effizient vonstatten geht, setzt AWS Lambda seit 2018 auf die eigens hierzu entwickelte Virtualisierungstechnologie Firecracker [Firecracker] – dazu später mehr.
Durch Quotas lässt sich regeln, dass eine Funktion nur allokierte Ressourcen benutzt (Anzahl parallele Aufrufe, Speicherbedarf …). Ansonsten wirken in der Amazon-Cloud etablierte Mechanismen auch bei Lambda positiv auf den zuverlässigen Betrieb – etwa das Monitoring über CloudWatch, oder dass der Service „Multi AZ“ ist, AWS-Sprech für den parallelen Betrieb in mehreren Rechenzentren (AZ = Availability Zone).
Ein Grundpfeiler der Sicherheitsarchitektur von AWS ist das Shared Responsibility Model. Dabei teilen sich Cloud-Provider (also Amazon) und Kunde die Verantwortung. Amazon ist für die Sicherheit der Cloud selbst verantwortlich (wörtlich „Security of the Cloud“), AWS-Kunden hingegen für die Sicherheit in der Cloud. Dieses Model greift auch bei AWS Lambda, wobei AWS dem Kunden hier besonders viel abnimmt – aber nicht alles. Abbildung 4 zeigt den Aufbau eines Worker Hosts und der damit verbundenen Virtualisierung auf Basis von EC2 und Firecracker. Das Bild mit seinen zwei MicroVMs ist dabei schematisch zu verstehen. Denn tatsächlich können auf einer größeren EC2-Instanz dank Firecracker Tausende MicroVMs unterschiedlichster Kunden laufen, isoliert durch Virtualisierung.
Ein Entwicklungsteam, das Software auf Basis von AWS Lambda baut und betreibt, sieht das meiste davon nicht. Es stellt lediglich den Anwendungscode bereit und kann die Ausführungsumgebung konfigurieren – aus Sicherheitssicht verantwortet es diese Teile. Hierunter fällt insbesondere die Zuweisung von Rollen und Berechtigungen. AWS Lambda setzt auch hier auf etablierte Services wie AWS IAM (Identity and Access Management). Die Verschlüsselung von Daten, sowohl gespeichert als auch im Fluss, folgt den üblichen Sicherheitsstandards in AWS. Weiteren Schutz bietet die Möglichkeit, nur signierten Quelltext ausführen zu lassen.
Eine Besonderheit in der Aufteilung der Verantwortlichkeiten in AWS Lambda stellt die Runtime dar. Je nach Programmiersprache fällt sie entweder dem Entwicklungsteam oder dem Provider (Amazon) zu. Und tatsächlich ist es möglich, beliebige Binaries in Lambda ausführen zu lassen, solange Betriebssystem und Prozessorarchitektur passen. Die Verantwortung liegt dann beim Team. Schutzmechanismen des Betriebssystems greifen trotzdem, darüber gibt es Lambda-spezifische Quotas, z.B. für ausgehende Zugriffe. Details zu allen Sicherheitsthemen in AWS Lambda sind in einem speziellen Whitepaper beschrieben [WPSec2023].
Die Abrechnung des Lambda-Services erfolgt nach Verbrauch von CPU-Zeit und Speicher. Kunden haben daher aus Kostensicht ein Interesse, dass die eigentliche Ausführung einer Funktion effizient erfolgt, es also insbesondere keinen großes „Abrieb“ durch Virtualisierung gibt. Hier leistet die Firecracker-Technologie einen großen Beitrag (für Messungen siehe [Aga+2020]), ebenso wie bei der Möglichkeit für Amazon, extrem viele Runtimes auf einer einzelnen EC2-Instanz zu betreiben. Von so einem kosteneffizienten Betrieb durch hohe Packdichte hat der AWS-Kunde wenig, außer die Hoffnung, dass Amazon Ersparnisse mittelfristig weitergibt.
Kosten bei einer Lambda-Anwendung fallen insbesondere auch durch Aufrufe anderer kostenpflichtiger AWS-Services an. Zur Überwachung greifen die AWS-üblichen Mechanismen (Stichwort „Billing Alerts“), sowohl für Lambda als auch für andere Dienste.
Wie Firecracker zeigt, entwickelt Amazon seine Lösungen und insbesondere die Interna konsequent weiter. So unterstützt AWS Lambda mittlerweile Container-Images als Träger für Handler. Kunden profitieren von verbesserten Reuse-Strategien für vorhandene Umgebungen ihrer Funktionen. Nicht nur, dass dadurch Kaltstart-Zeiten wegfallen und keine zusätzliche Latenz verursachen – die Init-Phase einer Funktion wird bei den meisten Runtimes in Rechnung gestellt – sie entfällt, wenn die Funktion für einen Aufruf bereits parat steht. Neben der Auswahl eines x86-basierten Prozessors erlaubt AWS Lambda seit 2021 auch ARM. Das bedeutet (laut [Poc2021]) eine Effizienzsteigerung von 34%. Bei Runtimes wie für Python oder Node.js sogar ohne Änderung an der bestehenden Lambda-Funktion (ein Go-Handler z.B. muss für ARM kompiliert sein).
AWS Lambda stellte vor einem Jahrzehnt einen Meilenstein im Serverless-Trend dar. Aber auch die Marktbegleiter schlafen nicht. Google Cloud Functions beispielsweise hat 2022 ein größeres Update erfahren („2nd Gen“, [Ram+2022]). Anders als mitunter erwartet blieb der ganz große Hype, verglichen etwa mit Docker und Kubernetes, bisher aus. Woran liegt das? M. Roberts et al. [Rob+2017] diskutieren Serverless (im Sinne von BaaS + FaaS) als eine andere Art Anwendungen zu bauen. Sie erfordere neue Entwurfskonzepte, Werkzeuge und Betriebsmodelle. Bestehende Anwendungen lassen sich nur schwierig sinnvoll dahin befördern und anpassen.
Gleichwohl legt Amazon beeindruckende Zahlen vor, was die Akzeptanz von AWS Lambda angeht (Stand 2022: mehr als eine Millionen Kunden, mehr als 10 Billionen Anrufe pro Monat [Woo+2022]). Erfolgsgeschichten betonen einerseits die kurzen Entwicklungszeiten, erzielt durch eine geringe Fertigungstiefe (viel BaaS, vergleichsweise wenig FaaS). Andererseits den zuverlässigen Umgang mit schwer vorhersehbarer Last, wie ihn beispielsweise die BBC mit ihrem Online-Auftritt erlebt [Ish2021].
In Zukunft werden wohl noch mehr Anwendungen in der Cloud von Serverless profitieren, vor allem neue und solche, bei denen Time-to-Market und/oder starke Lastschwankungen eine große Rolle spielen. Erforderlich ist die Akzeptanz von Hersteller-gefertigten Bestandteilen (nicht nur Middleware) und im Grunde auch vom Betrieb in einer Public Cloud. Denn auch wenn es Serverless-Lösungen für den On Premises-Bereich gibt: eigene Rechenzentren sind bezogen auf Elastizität stets ein limitierender Faktor und können bei der kontinuierlichen Weiterentwicklung der Technologien nur unter größten Anstrengungen mithalten. Aufwände, die in domänenspezifischen Themen besser aufgehoben sind, um am Markt zu bestehen.
[Aga+2020] Alexandru Agache, Marc Brooker et al: “Firecracker: Lightweight virtualization for serverless applications”
[Firecracker] Homepage Firecracker, „Secure and fast microVMs for serverless computing“, https://firecracker-microvm.github.io
[Ish2021] Johnathan Ishmael: “Delivering BBC Online using Serverless”
[NIST2011] National Institute of Standards and Technology: „The NIST Definition of Cloud Computing“, NIST Special Publication 800-145, September 2011
[Poc2021] Danilo Poccia: “AWS Lambda Functions Powered by AWS Graviton2 Processor”
[Rob+2017] Michael Roberts, John Chapin: “What is Serverless? Understanding the Latest Advances in Cloud and Service-Based Architecture”, O’Reilly Media 2017
[Ram+2022] Vidya Nagarajan Raman, Jaisen Mathai: “Cloud Functions 2nd gen is GA, delivering more events, compute and control”
[Sba+2021] Peter Sbarski, Yan Cui, Ajay Nair: “Serverless Architectures on AWS”, 2. Auflage, Manning 2021
[Wei+2019] Becky Weiss, Mike Furr: “Static stability using Availability Zones”, in Amazon Builders’ Library
[Woo+2022] Julian Wood, Chris Greenwood: “A closer look at AWS Lambda”, Vortrag auf der AWS re:invent 2022
[WPSec] Amazon Web Services: „Security Overview of AWS Lambda“, AWS Whitepaper, 2023
Dieses Porträt ist ursprünglich in der IT Spektrum, Ausgabe 2 | 2024 als zehnter Teil einer Reihe über Architektur-Ikonen der Softwareentwicklung erschienen. Rückmeldungen aller Art gerne an mich per E-Mail. Insbesondere auch Wünsche für weitere Porträts.
Architektur-Porträt: Das Build-Management-Tool Maven
Architektur-Porträt: Die Monitoring-Lösung Prometheus
Architekturikonen in Software Überblick über alle Porträts