Inzwischen gibt es bei den meisten Cloudanbietern die Möglichkeit kleine Codeteile als sogenannte Functions zu hinterlegen. Durch eingerichtete Trigger werden Parameter übergeben und die eigentliche Ausführung ausgelöst. Manchmal wird dieses Konzept auch als Serverless bezeichnet. Das ist etwas irreführend, denn zur Ausführung werden natürlich Server benötigt. Nur muss man sich als “Anwender” nicht im eigentlichen Sinne um eine Ablaufumgebung, also eine VM Instanz oder deren Skalierung, kümmern. Das übernimmt der Service selbst. Da kein expliziter Server existiert, fallen Kosten nur an, wenn die Function auch aufgerufen wird. Das spart Kosten. (siehe auch den Artikel meines Kollegen: Serverless ). Aber auch sonst bieten Functions eine sehr interessante Abstraktion, deren Vorteile man auch innerhalb eines Kubernetes Clusters sehr gerne nutzen würde (die Kosten eines Aufrufs spielen in diesem Fall natürlich keine Rolle).
Kubernetes
Wer FaaS auf Kubernetes realisieren will, kann derzeit zwischen einigen (mehr oder weniger erwachsenen) Projekten auswählen:
- Kubeless (erstes Release Jul 2017)
- OpenFaaS (erstes Release Aug 2017)
- Fission (erstes Release Sep 2017)
Alle drei Frameworks sind Kubernetes native. Das heißt, sie bedienen sich der Kubernetes-Ressourcen, um eine Ablaufumgebung für Functions (in Form von Containern) zur Verfügung zu stellen, sie zu skalieren und zu verwalten. Der Anwender erstellt und konfiguriert seine Functions ausschliesslich per Command Line Interface und/oder Webinterface. Im folgenden beziehen sich die Angaben im Wesentlichen auf Fission.
Functions
Functions bestehen aus Sicht des Anwenders nur aus dem Quellcode selbst. Unter Angabe der Quelldatei werden Functions sehr einfach erzeugt und damit deployed.
Charakteristik einer Function
In ihrer Charakteristik sind Functions grundsätzlich Zustandslos und existieren nur während der Ausführung ihres Codes. Dabei sind unterschiedliche Programmiersprachen möglich. Im Falle von Fission sind dies zum Beispiel: Bash, Binary Executables, Node.js, Go, Python, PHP, etc. Gestartet wird eine Function durch einen sogenannten Trigger. Davon stehen mehrere zur Verfügung: Messages, Timer, HTTP oder auch Kubernetes Events. Aber egal welcher Trigger auch verwendet wird, die Functions haben stets dasselbe Interface und müssen somit nicht abgepasst werden, wenn man einen anderen Trigger verwenden möchte.
Entwicklungsumgebung
Functions haben bereits bei der Entwicklungsumgebung einige Vorteile. Denn Functions sind nur Quellcode (der aber auch durch ZIP Files oder Packages zusammengefasst werden kann). Es muss weder ein gesonderter Build-Schritt implementiert werden, noch ein Build-System vorhanden sein. Ebenso müssen üblicherweise keine Container gebaut oder durch eine Registry verwaltet werden.
Laufzeitumgebung
Die Verwaltung der Functions und der Laufzeitumgebung im Cluster, erfolgt durch das Framework. Daraus ergibt sich:
- Es müssen keine zusätzliche Kubernetes-Ressource wie z.B. Service oder Ingress erzeugt werden (HTTP Routen können z.B. frei konfiguriert werden)
- Wichtige Teile der Infrastruktur, wie Logging oder die automatische Skalierung der Functions, werden durch das Framework zur Verfügung gestellt
- Auf andere Pods oder Services des Clusters kann dennoch wie gewohnt zugegriffen werden (auch auf Datenbanken, Message Queues etc.)
- Die Skalierung erfolgt automatisch. So darf man erwarten, dass Transaktionen mit hohem Volumen realisiert werden können (wie es z.B. für IoT notwendig ist)
- Die Lebenszeit der Container der Laufzeitumgebung ist länger als die der Functions (sie wird allerdings alleine durch das Framework bestimmt) und kann beispielsweise für das Aufrechterhalten von Datenbankverbindungen genutzt werden
Anwendungsfälle
Bei der Entwicklung von Anwendungen sind Functions orthogonal zu einem allgemeinen Architekturprinzip wie z.B. Microservices. Allerdings stellen sie eine weitere Option zur Modularisierung von Geschäftslogik oder zur Implementierung von kleinen Actions (als Reaktion auf einen Trigger) dar. Da sich mehrere Functions auch über Events miteinander verketten lassen, sind auch ereignisgetriebene Workflows möglich.
Für welche Anwendungsfälle setzt man Functions auf einem Cluster mit Kubernetes ein? Ein paar Beispiele:
- Die Größe einer Aufgabe/Function rechtfertigt kein Build-Tool, keine Registry, etc.
- Kleine CRUD Services
- Simple Web Hooks (z.B. für Slack, Github)
- Kleine WebServer zum Ausspielen einzelner HTML Seiten
- Endpunkte für IoT Devices
- Als einfacher Ambassador Service zu komplexeren Systemen
- Timer Actions, geplante Tasks oder Jobs
- Reaktion auf Kubernetes Events: Zu wenig speicher, Pods sterben etc. (weitere Automatisierung der Kubernetes Infrastruktur und dadurch eine Unterstützung für OPS)
- Gute Abbildung des Problems durch Function Composition: Verkettung von einfachen Aufgaben durch eine deklarative Beschreibung eines Workflows (wie z.B. die korrelation der Daten zweier Dienste. Daten, die erst parallel und durch unterschiedliche Functions beschafft werden)
Ausblick
Kubernetes ist noch jung, FaaS als Kubernetes Native Framework ist noch jünger. Dennoch scheinen sich die o.g. Projekte in Richtung Produktiveinsatz zu bewegen oder sie werden bereits eingesetzt. Sie sorgen dafür dass, neben z.B. einer Cloud Native Storage oder den Operators (für etcd, Prometheus, ElasticSearch etc.), weitere Teile der Infrastruktur der großen Cloudanbieter in den eigenen Cluster geholt werden können. Ein wichtiger Schritt in Richtung Unabhängigkeit und Flexibilität.
Wer in seinem Projekt die oben genannten Anwendungsfälle vorfindet, sollte sich also eins der FaaS Projekte unbedingt ansehen.