Function as a Service
Einzelne Funktionen auf Abruf ausführen
Die Idee und ihr Nutzen
Kunden einer Versicherung laden zu ihren Schadenmeldungen Fotos hoch, an normalen Tagen etwa 500, nach einem Hagelsturm aber 40.000 in einer Stunde. Für jedes Foto soll ein kleines Vorschaubild entstehen. Statt dafür einen Server rund um die Uhr laufen zu lassen, lädt das Entwicklungsteam der Versicherung nur ein kleines Programm auf eine Funktionsplattform. Die Funktionsplattform startet dieses Programm bei jedem neuen Foto automatisch und lässt es bei vielen Fotos gleichzeitig mehrfach nebeneinander laufen. Dieses Betriebsmodell heißt Function as a Service, kurz FaaS, auf Deutsch Funktion als Dienstleistung. Der Name meint: Das Entwicklungsteam liefert nur die Funktion, also das kleine Programm, und das Ausführen übernimmt die Funktionsplattform als Dienstleistung.
Wie funktioniert es?
Nach dem Hagelsturm laden Kunden in einer Stunde 40.000 Fotos in den Bildspeicher der Versicherung hoch. Der Bildspeicher meldet jede neue Datei im Ordner für hochgeladene Fotos als Ereignis an die Funktionsplattform, also als kurze Meldung wie „neue Datei foto-1“. Für jedes Ereignis startet die Funktionsplattform eine Instanz der Funktion „Vorschaubild erzeugen“, also ein laufendes Exemplar der Funktion, und übergibt ihr den Dateinamen. Instanz 1 erhält foto-1, Instanz 2 erhält foto-2, Instanz 3 erhält foto-3 und so weiter. Bei vielen Ereignissen laufen dadurch viele Instanzen nebeneinander. Alle Instanzen führen denselben Code aus. Das geht, weil jede Instanz ihr Foto für sich bearbeitet und nichts von einer anderen Instanz braucht. Instanz 1 lädt foto-1 aus dem Bildspeicher, erzeugt ein kleines Vorschaubild, liest die Aufnahmezeit aus den Zusatzangaben des Fotos und legt das Ergebnis in einem eigenen Ordner des Bildspeichers ab. Neue Dateien in diesem Ordner meldet der Bildspeicher nicht, sonst würde jedes Vorschaubild die Funktion erneut starten. Jede andere Instanz macht dasselbe mit ihrem Foto. Das Entwicklungsteam hat die Obergrenze auf 100 gleichzeitige Instanzen gesetzt, weil der Bildspeicher mehr gleichzeitige Zugriffe nicht verkraftet. Dauert die Bearbeitung eines Fotos zwei Sekunden, sind die 40.000 Fotos bei 100 gleichzeitigen Instanzen rechnerisch nach etwa 13 Minuten verarbeitet. Nach getaner Arbeit beendet die Funktionsplattform die Instanz. Am nächsten Tag laufen nur noch gelegentlich einzelne Instanzen.
Bei Function as a Service startet die Funktionsplattform das Programm, sobald es gebraucht wird, und beendet es danach wieder. Das Entwicklungsteam liefert nur eine kleine Funktion, also ein Programmstück mit genau einer Aufgabe, hier „aus einem Foto ein Vorschaubild erzeugen und die Aufnahmezeit auslesen“. Zwischen zwei Fotos muss sich die Funktion nichts merken. Das Entwicklungsteam lädt die Funktion auf die Funktionsplattform und legt einen Auslöser fest, der die Funktion startet. Das kann eine eingehende Webanfrage sein, eine Nachricht in einer Warteschlange, eine Uhrzeit oder wie hier eine neue Datei im Bildspeicher. Statt eine Instanz nach getaner Arbeit sofort zu beenden, kann die Funktionsplattform sie auch kurz für den nächsten Aufruf bereithalten. Kommen keine Ereignisse, läuft auch keine Instanz. Wie viele Instanzen höchstens nebeneinander laufen, begrenzen eine einstellbare Obergrenze, die verfügbaren Rechner und die Zahl der Zugriffe, die andere beteiligte Dienste wie der Bildspeicher verkraften.
Bei den Plattformen großer Cloud-Anbieter richten sich die Kosten meist nach der Zahl der Aufrufe und der verbrauchten Rechenzeit. Betreibt ein Unternehmen die Funktionsplattform selbst, bezahlt es die dafür bereitgehaltenen Rechner und den Betrieb.
Eine Instanz kann jederzeit beendet werden. Was eine Funktion über einen Aufruf hinaus braucht, etwa eine Liste der schon bearbeiteten Fotos, speichert die Funktion deshalb in einem externen Speicher wie einer Datenbank, nicht im Arbeitsspeicher der Instanz. Nach einer längeren Pause kann der erste Aufruf spürbar länger dauern, weil die Funktionsplattform erst eine neue Instanz starten muss. Diese Verzögerung heißt Kaltstart. Wie groß sie ist, hängt von Plattform, Programmiersprache und Größe der Funktion ab. Scheitert eine Instanz an einem Foto, hängt es von der Funktionsplattform und ihrer Einstellung ab, ob der Aufruf wiederholt wird. Die Funktion muss deshalb damit umgehen können, dass sie dasselbe Foto zweimal bearbeitet. Bei den Vorschaubildern ist das harmlos, weil eine zweite Bearbeitung dasselbe Vorschaubild noch einmal ablegt. Die Funktionsplattform sagt auch nicht zu, dass alle Fotos innerhalb einer bestimmten Zeit verarbeitet sind. Das hängt von der Dauer je Foto, von der Obergrenze für gleichzeitige Instanzen und davon ab, wie schnell der Bildspeicher liefert.
Function as a Service: Vorschaubilder nach dem Hagelsturm
Wann ist es sinnvoll und wann nicht?
Function as a Service lohnt sich
- Kurze Aufgaben mit genau einem Ergebnis, etwa ein Vorschaubild je Foto, werden durch ein Ereignis ausgelöst, und ihre Menge schwankt stark. Betreibt das Entwicklungsteam für die Vorschaubilder einen eigenen Server, muss es den Server für die größte Spitze auslegen und bezahlt ihn auch in der übrigen Zeit, in der er kaum etwas zu tun hat. Legt das Entwicklungsteam den Server kleiner aus, stauen sich die Fotos nach einem Sturm stundenlang, und Kunden sehen lange keine Vorschau ihrer hochgeladenen Bilder.
- Die Last ist die meiste Zeit gering. Ein eigener Server liefe auch an ruhigen Tagen rund um die Uhr und müsste bezahlt werden. An ruhigen Tagen laufen nur gelegentlich einzelne Instanzen. Bei einem Cloud-Anbieter zahlt das Entwicklungsteam dann meist nur für diese Aufrufe und ihre Rechenzeit.
- Ein Team will für eine Nebenaufgabe wie die Vorschaubilder keinen eigenen Server betreiben, pflegen und mit Sicherheitsupdates versorgen. Betreibt das Team die Funktionsplattform selbst, bleibt dieser Aufwand allerdings für die Funktionsplattform bestehen.
- Kleine Programme, die Dienste miteinander verbinden, werden gebraucht. Beispiele sind eine Funktion, die eine eingegangene Schadenmeldung in das Format eines anderen Programms umwandelt, und eine Funktion, die dem Kunden danach eine Eingangsbestätigung per E-Mail schickt. Ohne Funktionsplattform bräuchte jedes dieser kleinen Programme einen eigenen, ständig laufenden Server, obwohl es nur bei jeder Schadenmeldung kurz arbeitet.
- Eine Aufgabe soll sich unabhängig von anderen Programmen ändern lassen. Die Funktion „Vorschaubild erzeugen“ ist klein und hat genau eine Aufgabe. Eine Änderung an ihr betrifft keine anderen Programme. Steckt die Erzeugung der Vorschaubilder dagegen in einem größeren Programm, muss für jede Änderung daran das ganze Programm neu getestet und ausgeliefert werden.
Function as a Service ist nicht das Richtige
- Eine Aufgabe läuft lange. Die Plattformen begrenzen, wie lange eine einzelne Funktion laufen darf, AWS Lambda zum Beispiel auf höchstens 15 Minuten je Aufruf. Ist die Funktion bis dahin nicht fertig, bricht die Plattform sie ab. Dann betreibt das Entwicklungsteam einen dauerhaft laufenden Dienst, der seine Aufträge aus einer Warteschlange abholt. Bei hoher Last kommen nach dem Ansatz Autoscaling automatisch weitere Instanzen desselben Dienstes hinzu. Eine Laufzeitgrenze gibt es dann nicht, und der Dienst ist nicht an eine Funktionsplattform gebunden.
- Die Last ist gleichmäßig hoch. Ein Server ist dann ohnehin ständig ausgelastet, und sein fester Preis kann unter dem liegen, was ein Cloud-Anbieter für die vielen Aufrufe und ihre Rechenzeit verlangt. Dann betreibt das Entwicklungsteam einen dauerhaft laufenden Dienst, wenn ein Kostenvergleich das bestätigt. In den Vergleich gehen die Preise der konkreten Plattform, die erwartete Zahl von Fotos und der eigene Betriebsaufwand ein.
- Sehr kurze Antwortzeiten sind zwingend, und Kaltstarts würden stören, etwa bei einer Beitragsberechnung, auf deren Ergebnis ein Kunde im Kundenportal wartet. Dann betreibt das Entwicklungsteam einen dauerhaft laufenden Dienst, dessen Instanzen ständig bereitstehen. Kaltstarts entfallen, weil keine Instanz erst gestartet werden muss.
Nachteile und typische Fehler
Viele kleine Funktionen sind schwer zu überblicken, zu testen und zu überwachen, weil sich ein Ablauf über viele Funktionen und Auslöser verteilt. Läuft eine Schadenmeldung zum Beispiel durch fünf Funktionen, die einander über Ereignisse auslösen, muss das Entwicklungsteam bei einem Fehler die Protokolle aller fünf Funktionen durchsuchen. Bei Cloud-Anbietern bindet sich das Entwicklungsteam an deren Auslöser, Dienste und Werkzeuge. Die Funktion „Vorschaubild erzeugen“ wird etwa von den Ereignissen des Bildspeichers eines bestimmten Anbieters gestartet, und ein Wechsel zu einem anderen Anbieter erfordert Umbauten. Wie schnell eine Funktion startet, wie lange sie laufen darf und was sie kostet, hängt von der gewählten Plattform ab. Das Entwicklungsteam muss die Obergrenze für gleichzeitige Instanzen so einstellen, dass der Bildspeicher die Zugriffe verkraftet und die Kosten in einer Spitze begrenzt bleiben. Der externe Speicher für alles, was über einen Aufruf hinaus gebraucht wird, muss ebenfalls betrieben und bezahlt werden.
Typische Fehler und woran man sie erkennt:
- Selbstauslösende Funktion: Das Entwicklungsteam legt die Vorschaubilder im selben Ordner ab wie die hochgeladenen Fotos. Der Bildspeicher meldet jedes abgelegte Vorschaubild als neue Datei, und die Funktionsplattform startet eine Instanz, die aus dem Vorschaubild ein weiteres Vorschaubild erzeugt und wieder dort ablegt. Aus jedem Foto wird so eine Kette von Aufrufen ohne Ende. Nach dem Hagelsturm arbeiten alle 100 Instanzen ununterbrochen weiter, statt nach etwa 13 Minuten fertig zu sein, und bei einem Cloud-Anbieter kostet jeder dieser Aufrufe Geld. Erkennbar ist das an einer Zahl von Aufrufen, die weit über der Zahl hochgeladener Fotos liegt.
- Zustand in der Instanz: Die Funktion merkt sich im Arbeitsspeicher ihrer Instanz, welche Fotos sie schon bearbeitet hat, damit sie ein wiederholt gemeldetes Foto nicht zweimal bearbeitet. Die Funktionsplattform beendet die Instanz nach getaner Arbeit, und diese Liste geht verloren. Wiederholt die Funktionsplattform später den Aufruf für ein Foto, bearbeitet eine neue Instanz das Foto ein zweites Mal, weil die neue Instanz die Liste nicht kennt. Erkennbar ist das an Fehlern, die nur nach einem Kaltstart oder bei vielen gleichzeitigen Instanzen auftreten.
- Überlasteter Bildspeicher: Das Entwicklungsteam setzt die Obergrenze auf 1.000 statt 100 gleichzeitige Instanzen. Nach dem Hagelsturm laden dann bis zu 1.000 Instanzen gleichzeitig Fotos aus dem Bildspeicher, zehnmal so viele Zugriffe, wie der Bildspeicher verkraftet. Der Bildspeicher antwortet langsam oder weist Ladevorgänge ab, auch beim Hochladen neuer Fotos durch die Kunden. Erkennbar ist das an langsamen oder abgewiesenen Ladevorgängen beim Bildspeicher während der Spitze.
- Zu niedrige Obergrenze: Das Entwicklungsteam setzt die Obergrenze auf 10 gleichzeitige Instanzen. Bei zwei Sekunden je Foto schaffen 10 Instanzen 300 Fotos in der Minute, also 18.000 in der Stunde. Nach dem Hagelsturm kommen in einer Stunde 40.000 Fotos an, die Ereignisse stauen sich, und die letzten Kunden sehen ihre Vorschau erst nach über zwei Stunden. Erkennbar ist das an Aufrufen, die die Funktionsplattform wegen der Obergrenze zurückhält, und an einer wachsenden Zahl von Fotos ohne Vorschaubild.
Weiterführend
Verwandte Ansätze
- Serverless Architecture: die umfassendere Bauweise, bei der ein Team neben Funktionen auch Datenbanken und Warteschlangen als Dienste nutzt, die der Cloud-Anbieter betreibt, ohne selbst Server zu betreiben.
- Event Driven Architecture: Bauweise, in der Programme auf Ereignisse reagieren, wie hier die Funktion auf ein neues Foto.
- Stateless Services: Programme, die zwischen zwei Anfragen nichts bei sich selbst speichern. Funktionen müssen so gebaut sein, weil ihre Instanzen jederzeit beendet werden können.
Werkzeuge und Umsetzungen
Neben den Diensten der großen Cloud-Anbieter gibt es Plattformen, die man selbst betreiben kann:
- Knative: führt Funktionen und kleine Dienste in Kubernetes, einer verbreiteten Plattform zum Betrieb von Anwendungen auf vielen Servern, bei Bedarf aus und kann sie bis auf null Instanzen herunterfahren.
- OpenFaaS: Plattform für Funktionen auf Kubernetes. Die frei verfügbare Community Edition ist laut Hersteller für Open-Source-Entwickler und zum Ausprobieren gedacht und für kommerzielle Nutzung nur für eine einmalige Testphase von 60 Tagen lizenziert. Für den Einsatz in Unternehmen verweist der Hersteller auf die kostenpflichtigen Pro-Ausgaben.
- Fission: Plattform für Funktionen auf Kubernetes, die Instanzen vorhält, um Kaltstarts zu verkürzen.
- Nuclio: Plattform für Funktionen mit hohem Durchsatz, etwa für Datenverarbeitung.
Quellen
- Mike Roberts: Serverless Architectures: erklärt Function as a Service mit automatischer Skalierung, Kaltstarts, Laufzeitgrenzen, Zustand außerhalb der Funktion und der Bindung an einen Anbieter.
- AWS: Understanding Lambda function scaling: beschreibt am Beispiel von AWS Lambda, wie die Plattform für jede gleichzeitige Anfrage eine eigene Ausführungsumgebung bereitstellt und wie sich die Zahl gleichzeitiger Instanzen einer Funktion begrenzen lässt.
- AWS: Use Lambda recursive loop detection to prevent infinite loops: beschreibt Funktionen, die sich über den Dienst, in den sie schreiben, immer wieder selbst auslösen, und die unerwarteten Kosten, die daraus entstehen.