Azure Functions – Eine kurze Einführung

20. März 2019

imageAzure Functions sind ein ideales Mittel, um asynchron ausgeführte Hintergrundverarbeitung, Batchverarbeitungen, klassische CRON-Jobs und ähnliches in Azure umzusetzen. Sie bieten flexible Trigger, ein einfaches Programmiermodell, sowie skalierbaren und kosteneffizienten Betrieb und Monitoring über AppInsight.

Oder wie es Microsoft formuliert:

Mit Functions von serverlosem Computing profitieren.

Erstellen Sie Ihre Apps ganz einfach mit simplen, serverlosen Funktionen, die sich je nach Anforderungen skalieren lassen. Verwenden Sie die Programmiersprache Ihrer Wahl, und machen Sie sich keine Gedanken um Server oder Infrastruktur.

Wir nutzen Azure Functions mittlerweile in verschiedenen Projekten, wobei sich die Einsatzgebiete durchaus voneinander unterscheiden. Hier einige Beispiele:

  • Ein Backend für die Datenübernahme und Weiterverarbeitung (X-Tag)
  • Ein einfaches Backend für Datensynchronisierung zwischen Mobile Apps und Desktop-Anwendungen
  • Durchführung von Datenimporten im Hintergrund, zeitgesteuert oder nach Benutzeranforderung
  • Steuerung einer Massendatenanalyse

Die Nutzung von Azure Functions hat sich in diesen Projekten bewährt. Nutzt man Azure Functions ausgiebiger, dann kommt man aber früher oder später auch mit den Einschränkungen und Grenzen in Berührung, so dass sich ein genauerer Blick lohnt.

In diesem Beitrag geht es um einen Überblick über die Grundlagen, sowie allgemeine Hinweise. In nachfolgenden Beiträgen werde ich einige Hinweise zu besagten Einschränkungen und Grenzen geben.

Überblick

Zunächst ein kurzer Überblick, um jeden abzuholen. Wer sich schon mit Azure Functions beschäftigt hat, kann diesen Abschnitt getrost überspringen und zum Abschnitt “Best Practices” gehen. 😉

Wer seine Azure Function App im Azure Portal erstellt, kann unterschiedliche Implementierungswege und Plattformen nutzen und seine Functions teilweise direkt dort umsetzen. Hier soll es aber um Azure Function Apps gehen, die als normale C#-Projekte in Visual Studio angelegt werden. Ein ausführliches Tutorial findet sich bei Microsoft, hier die kurze Zusammenfassung.

Die Function App erstellen

Nach der Installation der notwendigen Tools findet sich in Visual Studio eine entsprechende Vorlage für Azure Functions Projekte. Bei der Neuanlage muss man sich für die Plattform und den Trigger für die erste Function entscheiden:

image

Die aktuelle Version Azure Functions v2 setzt auf .NET Core 2; wer das volle .NET Framework benötigt kann aber immer noch Version 1.0 einsetzen.

Das erstellte Projekt besteht lediglich aus zwei Konfigurationsdateien und einer C#-Datei mit der ersten Function.

Die Anatomie einer einzelnen Function folgt dabei immer einem einfachen Schema: Eine Function besteht aus einer statischen Methode, die über ein Attribut gekennzeichnet ist. Der erste Parameter ist mit einem Trigger-Attribut gekennzeichnet, der Typ des Parameters ist abhängig von eben diesem Trigger:

image

Die wichtigsten Trigger sind – zumindest in unseren bisherigen Projekten:

  • HTTP-Trigger für Aufrufe von außen
  • Queue-Trigger für das asynchrone Abarbeiten einzelner Teilaufgaben
  • Timer-Trigger für regelmäßig durchzuführende Aufgaben

Ein Druck auf F5 und die Laufzeitumgebung für Azure Functions – die gleiche, die auch in Azure selbst zum Einsatz kommt – wird lokal gestartet und man kann seine Function beliebig testen und debuggen:

image

Die Möglichkeit, Azure Functions lokal zu entwickeln, wirkt sich deutlich auf die eigene Produktivität aus.

Die Function App deployen

Wenn man die Function App erstmalig in Azure anlegen will, kann man das direkt aus Visual Studio heraus tun. Natürlich stehen auch die üblichen Möglichkeiten über PowerShell, ARM-Templates etc. zur Verfügung.

Neben anderen in Azure üblichen Informationen muss man sich vor allem für den Hosting Plan (d.h. den zugrundeliegenden App Service Plan) entscheiden:

image

Als App Service Plan kommt ein Consumption Plan oder ein ganz normaler App Service Plan (wie er auch von anderen App Services genutzt wird) in Frage.

In der Regel wird man sich für einen Consumption Plan entscheiden, denn er bietet Abrechnung nach Ressourcennutzung und ein Freikontingent. Allerdings verbunden mit ein paar Einschränkungen – die wesentliche: Die Laufzeit einer Function ist auf 5 min begrenzt, was per Konfiguration auf max. 10 min heraufgesetzt werden kann.

Obacht: Man kann den App Service Plan zwar später wechseln, aber anlegen lässt sich ein Consumption Plan im Azure Portal derzeit nur indirekt über eine Azure Function App (oder über PowerShell, bzw. ARM Templates).

Noch ein Tipp: Man sollte das erstellte Publishing Profile nachbearbeiten und die gewünschte Projektkonfiguration sowie “Delete existing files” setzten. Letzteres stellt sicher, dass Functions, die gelöscht oder umbenannt werden, auch in Azure verschwinden.

Das erste Anlegen einer Function App kann schon mal etwas dauern, aber am Ende sollte man seine Function App im Azure Portal sehen:

image

Mit Postman oder einem anderen Tool kann man die Function aufrufen:

image

Der Function Key ist nötig, wenn man in der Function (wie oben geschehen) AuthorizationLevel.Function eingestellt hat; den Key findet man im Azure Portal.

Best Practices

Zum Abschluss noch ein paar Best Practices, die sich bei uns bewährt haben.

Das Function App Projekt schmal halten

Functions bestehen aus einfachen Funktionen, deren Parameter je nach Art der Function aus HttpRequests, Queues oder ähnlichem entnommen werden. Es hat sich bewährt, in den Functions lediglich diese protokollspezifischen Dinge zu tun, und dann eine Methode einer separaten Fachlogik aufzurufen. Die Fachlogik sollte in einem separaten Assembly liegen.

Dadurch setzt sich die Function App nur mit den technischen Aspekten der Laufzeitumgebung und der jeweiligen Protokolle auseinander, während die Fachlogik völlig unabhängig von diesen ist. Das wirkt sich deutlich auf die Testbarkeit und die Wiederverwendbarkeit aus. Teilweise machen sich auch die Build-Zeiten des Function App Projektes bemerkbar, aber das kann am Stand des SDKs liegen.

Entkoppeln von Aufgaben über Queues

Gerade bei HTTP-Triggern hat es sich bewährt, die eigentliche Verarbeitung nicht in der Function selbst vorzunehmen. Vielmehr sollten die eingehenden Daten unverarbeitet in Azure Storage abgelegt und anschließend eine Nachricht in eine Storage Queue gepostet werden. Die eigentliche Verarbeitung kann dann mit einer Function mit Queue-Trigger erfolgen.

Auf diese Weise muss der Aufrufer nicht auf die Verarbeitung warten und die Skalierbarkeit wird verbessert.

Arbeiten mit Azure Storage

Da Azure Functions zwingend einen Storage Account voraussetzen und die Nutzung über Trigger recht gut unterstützt wird, ist die Nutzung von Storage für die eigene Implementierung fast natürlich. Auch beim lokalen Arbeiten stellt dies kein Problem dar, weil der Azure Storage Emulator automatisch gestartet wird.

Dabei sollte man Storage Tables, Queues und Blob Container im eigenen Code nicht vorauszusetzen, sondern bei Bedarf anlegen. Das kostet nur einen Aufruf: CloudTable.CreateIfNotExists, bzw. analoge Methoden für Queues oder Blob Container.

Separate Setup-Routinen, wie man sie etwa bei einer Datenhaltung in SQL Server nutzen würde, sind hier nicht nötig und würden die Lauffähigkeit des eigenen Codes unnötig von diesem Setup abhängig machen.

Logging

Azure Functions bekommen als Parameter einen Logger übergeben (älterer Code nutzt TraceWriter, neuerer Code ILogger).

Es macht Sinn, diesen Logger an die nachgelagerte Business Logik weiterzugeben. Wer sich von den verwendeten nuget-Paketen unabhängig machen will, kann sich sehr einfach einen kleinen Wrapper schreiben.

Tipp: Ein Wrapper kann auch nützlich sein, um Log-Meldungen abzufangen und selbst in eine eigene Storage Table zu schreiben. Gerade während der Entwicklung hat man hier oft schneller und unkomplizierter Zugriff, als auf die Log-Files selbst.

Fazit

Diese kurze Einführung sollte unterschiedliche Vorteile von Azure Functions aufgezeigt haben. Vom einfachen Programmiermodell, über die Tool-Unterstützung, bis hin zu Kostenaspekten wirken sich diese Vorteile auf alle Phasen des Lebenszyklus einer Anwendung aus.

Azure Functions sind somit der ideale Ersatz für Dinge, die man früher mit Azure Scheduler oder WebJobs erledigt hätte. Insbesondere sind sie eine naheliegende Lösung für Dinge die man früher – um den damit verbundenen Aufwand zu vermeiden – nicht asynchron im Hintergrund erledigt hätte, sondern im Rahmen eines Requests in einer Web-Anwendung.

Das sollte ausreichend Motivation liefern, um Azure Functions im nächsten Cloud-Projekt zu nutzen. In nachfolgenden Beiträgen gehe ich auf konkrete Projektbeispiele ein und gebe Hinweise, wo man bei Azure Functions dann doch an Grenzen stößt und wie man damit umgehen kann.