3. Juni 2020 | 6 min lesezeit

VON „LESS SERVER“ ZU SERVERLESS: EINE REISE DURCH DIE CLOUD

Das #Softwarepicknick ist deine digitale Mittagspause mit den Experten der open knowledge GmbH. Bei diesem meet und eat bringen wir dir hilfreiche Codesnacks und aktuelle Thesen der modernen Softwareentwicklung direkt auf den Tisch und schaffen eine Wiese für deine konkreten Fragen aus dem Projektalltag. Die etwa 30-minütigen Aufzeichnungen der Talks und die wichtigsten Fragen haben wir dir in diesem Blogpost zusammengefasst.

Von „Less Server“ zu Serverless: Eine Reise durch die Cloud

Was genau ist Serverless und was sind sinnvolle Anwendungsszenarien für Serverless Applications? In welchem Zusammenhang stehen dabei die Akronyme IaaS, PaaS, Baas und FaaS? Wir sprechen über „Stolperfallen“ und „Wie man’s richtig macht“.

Wie lange dauert denn in der Regel ein „Kaltstart“ meiner Funktion?

Die Dauer eines „Kaltstarts“ hängt von vielen Faktoren ab und kann zwischen wenigen 100 Millisekunden und Werten im Sekundenbereich schwanken. Neben der Größe der Funktion spielen dabei u.a. der zur Verfügung gestellte Speicher, die genutzte Programmiersprache oder aber die Ablaufumgebung (VPC ja/nein) eine wichtige Rolle.

Entscheidend für die tatsächlich Dauer eines Cold-Starts ist das Zusammenspiel der verschiedenen Faktoren. Daher lässt sich auch keine generelle Regel für eine optimale Konfiguration angeben. Vielmehr muss man das Startup-Verhalten der eigenen Funktion beobachten und die Konfiguration entsprechend Schritt für Schritt optimieren. Es gilt zum Beispiel nicht automatisch, dass mehr Speicher auch einen kürzen Cold-Start impliziert. In der Regel gibt es einen Schwellwert, bei dem sich der Effekt wieder ins Negative umkehrt.

Und auch die Wahl einer interpretierten Programmiersprache, wie Node.js oder Python, führt nicht automatisch zu einer Verbesserung. Das Cold-Start Verhalten wird so zwar optimiert, gleichzeitig verlangsamt sich aber die Ausführungsgeschwindigkeit der Funktion, so dass die scheinbare Optimierung je nach Größe der Funktion unterm Strich evtl. zu einem schlechteren Laufzeitverhalten führt – und das für alle Aufrufe.

Der absolute Killer für eine Serverless Function bzw. deren Startzeit ist die Verwendung innerhalb einer VPC (Virtual Private Cloud), da in diesem Fall für einen Aufruf ein virtuelles Netzwerkinterface aufgebaut werden muss, was durchaus mehrere Sekunden dauern kann. Also NIEMALS eine Serverless Function innerhalb einer VPC platzieren, wenn es nicht wirklich einen zwingenden Grund dafür gibt!

Die beste Art der Optimierung des Startup-Verhalten aber ist, wann immer möglich einen Cold-Start zu vermeiden. Hierzu kann z.B. ein Cron-Job regelmäßig „keep-alive“ Events erzeugen, mit denen die Serverless-Functions angetriggert und die zugehörige Runtime am Leben (also im „warm“-Modus) gehalten wird. Genau diese Funktionalität bietet das WARM-Up Plugin des Serverless Framework (https://www.serverless.com/plugins/serverless-plugin-warmup/) out-of-the-Box. Ein Nachteil dieser Methode ist, dass innerhalb des Event-Handlers der Serverless-Function ein Dispatching zwischen den echten Events und den „keep-alive“ Events erfolgen muss, um so entweder die eigentliche (Business-)Logik der Funktion auszuführen oder aber sofort wieder aus der Funktion auszusteigen. Natürlich ist dabei auch die Abarbeitung der „keep-alive“ Events kostenpflichtig. Alternativ zu dem eben beschrieben manuellen Ansatz, bieten erste Cloud-Anbieter, wie zum Beispiel AWS, mittlerweile die Möglichkeit via Konfiguration die gewünschte Anzahl an „warmen“ Funktionen vorzuhalten (https://aws.amazon.com/de/blogs/compute/new-for-aws-lambda-predictable-start-up-times-with-provisioned-concurrency/). Dieser Ansatz hat den Vorteil, dass die eigentliche Funktion bzw. dessen Handler nicht angepasst werden muss. Natürlich ist auch dieser Dienst kostenpflichtig.

Laut Chris Munns, Principal Developer Advocate for Serverless bei AWS, laufen übrigens nur knapp 0,2% Funktionen in einen Cold-Start. Und selbst dieses stellen nur dann ein echtes Problem dar, wenn es sich um synchrone Aufrufe handelt.

Bei AWS Instanzen bin ich aber immer noch für OS Updates also Maintenance zuständig, oder?

Das Vorgehen zur Bereitstellung von virtuellen Recheninstanzen unterscheidet sich bei den Cloud-Providern ein wenig von dem der Rechenzentren. Bare Metal im eigentlichen Sinne gibt es in der Regel nicht. Stattdessen wählt man für seine virtuelle Recheninstanz ein Machine-Image aus einer Reihe vorgefertigter Images (Amazon Machine Image bei AWS) aus oder bringt alternativ sein eigenes Image mit, welches zuvor z.B. mit Hilfe des Tools Packer von HashiCorp (https://hashiCorp) erzeugt wurde. Ein klassisches Update des OS gibt es in dem Sinne somit nicht. Stattdessen erzeugt man einfach eine neue virtuelle Instanz mit einer entsprechend neueren Version des Images. Gleiches gilt für einen Downgrade.

Wie teste ich, also: Wie lässt sich eine kostengünstige Staging Umgebung realisieren?

Eine saubere und trotzdem kostengünstige Testabdeckung im Serverless-Umfeld, ist nicht ganz einfach zu realisieren. Für das Umsetzen einer entsprechenden Serverless-Teststrategie muss man sich zwei wesentliche Punkte bzw. Ziele vor Augen führen. Zum einen sollen die Tests dabei helfen, „Vertrauen“ in den eigenen Code zu gewinnen. D.h. man möchte mit Hilfe der Teststrategie sicherstellen, dass der Code so funktioniert, wie geplant – und das auf der angedachten Zielplattform, also in der Cloud. Zum anderen möchte man das Risiko unerwarteter Seiteneffekte innerhalb der produktiven Umgebung – also ebenfalls der Cloud – minimieren.

Anders als bei klassischen Enterprise Anwendungen, die zum Beispiel als Monolith innerhalb eines Application Servers laufen oder aber in Form einer Reihe von mehr oder minder autarken Microservices, liegt bei einer Serverless-Anwendung die Komplexität weniger in dem Code der einzelnen Funktionen an sich, als vielmehr in der Integration mit anderen Plattform-Komponenten, wie z.B. Stream-Engines, NoSQL / relationalen Datenbanken, Object Stores etc. Entsprechend sollte der Fokus des Testings auch auf die Integration und nicht (nur) auf die Function-Logik gelegt werden. Man spricht in diesem Fall auch gerne davon, dass sich die altbekannte Test-Pyramide in einen Test-Honeycomb verwandelt.

Die gute Nachricht: Trotz Serverless und Cloud lassen sich Teile der notwendigen Tests auch weiterhin lokal auf dem Entwicklungsrechner oder einem dafür dedizierten Server außerhalb der Cloud durchführen. Für diesen Zweck bieten nahezu alle Cloud-Provider die Möglichkeit, die Serverless-Runtime lokal zu simulieren (z.B. SAM local für AWS). Und auch für einen Großteil der Cloud-Komponenten, wie z.B. Datenbanken oder Object-Stores, existieren lokale Varianten oder Mock-Ups. Mit diesem Setup lässt sich der positive Pfad eines Use-Cases relativ gut und realitätsnah testen. Dies gilt zumindest für die umgesetzte Logik. Das Laufzeitverhalten (Speicherverbrauch, Antwortzeiten etc) weichen dagegen zum Teil stark von der realen Cloud-Umgebung ab.  Was aber, wenn ein Problemfall getestet werden soll. Z.B. eine falsche Payload innerhalb eines Trigger-Events, ein Timeout durch eine Verkettung von Serverless Function Calls oder ein Problem mit Berechtigungen? Spätestens an dieser Stelle stoßen die lokalen Umgebungen definitiv an ihre Grenzen.

Natürlich könnte man nun für die gewünschten Tests ein identisches Cloud-Setup, wie in Produktion aufziehen und dort alle integrativen Tests ablaufen lassen. Führt man die Tests dann allerdings mit der gebotenen Regelmäßigkeit aus, kann es sehr schnell sehr teuer werden. Daher sollte dieses Szenario vor allem für End-to-End Tests und zur Simulation von komplexeren Problemen in Produktion verwendet werden. Deutlich günstiger ist es, wenn man zum Testen des Zusammenspiels einzelner Serverless-Functions mit denen von ihnen angesprochenen Cloud-Komponenten eine temporäre Integration-Cloud aufbaut. Der Trick hierbei ist, dass die so entstehende partielle Cloud-Umgebung nur für den kurzen Zeitraum des Tests aufgezogen und im Anschluss sofort wieder abgebaut wird. Und das auch nur für die innerhalb des Tests benötigten Serverless-Functions und Cloud-Komponenten. Kosten entstehen somit nur für den kurzen Moment des Integration-Tests. Die meisten Cloud-Provider bieten dazu die Möglichkeit, das gewünschte Cloud-Setup deklarativ (Infrastructure as Code) anzugeben. Bei AWS zum Beispiel kann zu diesem Zweck das Serverless Application Model (kurz SAM) verwendet werden.

Weitere Anregungen zu dem Thema finden sich in meiner Präsentation „Surviving Serverless Testing: The utimate Guide“ (https://www.slideshare.net/_openknowledge/surviving-serverless-testing-the-ultimate-guide)

Wie erhalte ich denn Einblick, wenn etwas nicht funktioniert?

Wie schon beim Testing ist auch das Monitoring in einer Serverless Cloud-Umgebung alles andere als trivial. Dies gilt insbesondere für klassische OPs-Teams, die es gewohnt sind, via zentraler Monitorinstanz und Application-Server Runtime, einen direkten Einblick in das aktuelle Geschehen der ihnen anvertrauten Anwendungslandschaft zu erhalten. Bei einer Serverless Anwendung dagegen handelt es sich um eine durch Events getriggerte, stark verteilte Architektur. deren Function-Calls in der Regel asynchron ablaufen. Eine zentrale Instant an der das Monitoring ansetzen kann sucht man vergebens.

Natürlich bieten die einzelnen Cloud-Provider entsprechendes Tooling zum Monitoring an. Angefangen bei einfachen Logs, über verteiltes Tracing bis hin zu verschiedensten Metriken und Alarmen auf Basis eben dieser Metriken. Bei AWS können z.B. Probleme innerhalb bzw. beim Aufruf einer Serverless Function via CloudWatch in ein entsprechendes Log geschrieben und für dieses Alarme beim Auftreten bestimmter Muster (also z.B. bestimmter Fehlermeldungen) deklariert werden. Hierbei können auch Thresholds angegeben werden, so dass ein Alarm nicht direkt beim ersten Auftreten eines Fehlers angestoßen wird, sondern erst, wenn es zu mehreren Wiederholungen der Problemsituation kommt. Das Zusammenspiel der verschiedenen Cloud-Komponenten lässt sich bei AWS via X-Ray analysieren. Viele Cloud-Provider bieten zusätzlich Integrationen für etabliertes Tooling an vice versa. Logz.io, Dashbird oder der Elastic-Stack sind hier nur einige Beispiele von vielen.

Wie schon zuvor beim Testing bedarf es auch beim Monitoring einer guten Strategie. Insbesondere durch die gegebene Asynchronität müssen Automatismen geschaffen werden, die Probleme und Fehler nicht nur erkennen, sondern diese auch automatisiert korrigieren. Dabei sollte das System so intelligent aufgebaut sein, dass potentielle Fehler erkannt werden bevor sie überhaupt entstehen. Hier können die bereits angesprochenen Metriken extrem hilfreich sein. Daher gilt es neben den klassischen eher technischen Metriken, wie Durchsatz, Speicherverbrauch oder Antwortzeiten, auch Business-orientierte Metriken zu sammeln (Umsatz pro Stunde, Anmeldungen, Abbrüche im Kaufprozess). Ein Best-Practice besteht darin, spezielle Serverless-Functions auf diese Metriken anzusetzen – „Eat your own Dogfood“ – welche die Probleme nicht nur erkennen, sondern bei Bedarf auch automatisch korrigieren und so potentielle Fehler gar nicht erst entstehen lassen.

Weitere Einblicke zu dem Thema finden sich im meiner Präsentation „Serverless: The Missing Manuel“ (https://www.slideshare.net/_openknowledge/serverless-the-missing-manual)

Ist „Warmhalten“ eine ankerkannte Best Practice oder eher ein unerwünschter Workaround?

Siehe vorletzter Abschnitt in Frage „Wie lange dauert denn in der Regel ein „Kaltstart“ meiner Funktion?“.

Die Slides zum Talk gibt es hier auf Slideshare:

Die weiteren Talks aus der #Softwarepicknick Reihe findest du auf unserem Blog.


Keine Kommentare

Kontakt

OPEN KNOWLEDGE GmbH

Standort Oldenburg:
Poststraße 1, 26122 Oldenburg

Standort Essen:
II. Hagen 7, 45127 Essen