Azure Functions und noch mehr Serverless

Serverless Microservices am Beispiel Azure
29
Okt

Azure Functions und noch mehr Serverless

Warum muss es „Serverless oder Microservices“ sein? Es sollte vielmehr „Microservices mit Serverless“ heißen! Basierend auf einigen der allgemein anerkannten Prinzipien von Microservices können wir serverlose Architekturen und Technologien verwenden, um hochfokussierte Microservices zu bauen. Schauen wir uns gemeinsam überblicksmäßig einen pragmatischen Ansatz zum Erstellen von Microservices mit Azure Functions, Azure Service Bus, Azure Storage und weiteren Diensten und Tools an. Und das gilt für fast alle Softwareentwickler: .NET, Java, Node.js und sogar Python.

Microservices sind seit einigen Jahren in aller Munde und es wird heftig über deren Vorzüge
aber auch über die damit verbundenen Nachteile diskutiert. Wenn man sich für einen Microservices-
Ansatz entscheidet, stellt sich relativ schnell die Frage nach dem technologischen Lösungsstapel. Mit
Serverless und Functions as a Service existiert seit relativ kurzer Zeit ein alternativer Weg für die
Realisierung.

Microservices – aber pragmatisch

Ohne hier eine komplette Abhandlung über Microservices zelebrieren zu wollen –einige der zentralen Prinzipien für erfolgreiche Microservices-Architekturen möchte ich gerne thematisieren, um dann die Überleitung in die Serverless-Welt daran festmachen zu können. Die vier mir wichtigsten aus Architektursicht sind:

  • Single Responsibility: Ein Microservice macht eine (Business-)Sache, und die macht er richtig. Es ist in der Tat eine hohe Kunst, Microservices richtig zu schneiden und das Gleichgewicht aus Zuständigkiet und Komplexität zu finden.
  • Isolation: Ein Microservice ist isoliert von anderen Services. Das bedingt eine physikalische Trennung und die Möglichkeit, mit dem Microservice über wohl definierte und technologieunabhängige Schnittstellen kommunizieren zu können. Zudem ist es manchmal sinnvoll, eine spezielle Technologie im Innenleben eines Microservices zu nutzen, um Use Cases optimal umsetzen zu können – die Wahl der Technologien pro Microservice sollte über Isolation gewährleistet sein.
  • Autonomie: Die Königsdisziplin bei der Gestaltung von Microservices-Architekturen – jeder Service hat seine eigene Datenhaltung und teilt sich keine Datenbank o. Ä. mit anderen Diensten. Dieses Prinzip konsequent umzusetzen, bedeutet eine weitgehende Entkopplung der Services – die allerdings einen Preis mit sich bringt.
  • Entkopplung: Um Services auf der Prozess- und Kommunikationsebene von einander zu entkoppeln und die Gesamtarchitektur dafür weniger fehlerfällig gestalten zu können, bietet sich der Einsatz asynchroner Kommunikationsmuster z. B. über Message-Queues an.

Vermutlich ist es für Sie als Leser am einfachsten, die Microservices-Prinzipien in einer Serverless-Welt anhand eines kleinen Beispiels vor Augen geführt zu bekommen. Dann schauen wir uns doch mal eins an. Die Beispielanwendung ist auch mit komplettem Sourcecode in einem GitHub Repository zugänglich.

End-to-End-Betrachtungen

Wichtig für die Betrachtungen von Microservices – egal ob Serverless oder nicht – ist eine End-to-End-Sicht auf ein System. Es hilft meist nicht wirklich, sich nur einzelne Services oder gar einzelne Implementierungsdetails von Services anzuschauen: Eine Gesamtbetrachtung umschließt die Clientanwendungen ebenso wie auch nichtfachliche Services wie z. B. ein Authentifizierungs- oder Notifizierungsdienst.

Verschaffen Sie sich den Zugang zur .NET- und Microsoft-Welt mit unserem kostenlosen Newsletter!

Unser exemplarisches Order-Monitoringsystem (Abb. 1) – das natürlich für den Zweck des Artikels stark vereinfacht dargestellt und implementiert wurde – ist vom Grundgedanken her recht simpel: Wann immer ein angemeldeter Benutzer über die Shoppingwebsite einkauft und eine Bestellung aufgibt, wollen wir mit einer mobilen App – als Cross-Plattform-SPA umgesetzt – als ebenfalls angemeldeter Benutzer benachrichtigt werden. Wir können zudem über diese SPA immer die aktuelle Liste von Bestellungen sowie deren Auslieferungszustand einsehen.

Abb. 1: Serverless-Microservices-Beispiel-Architektur (Order-Monitoring)

Wie in Abbildung 1 zu sehen ist, sind hier fünf Microservices involviert. Die Interaktionen sind über nummerierte Schritte nachvollziehbar. Die Client-SPA ist in diesem Fall nicht als Sammelsurium von Microfrontends umgesetzt, das würde den Rahmen des Beispiels bei weitem sprengen. Vielmehr bedient sich die SPA der unterschiedlichen APIs der Microservices. Für ein vereinfachtes URL-Management und eine bessere Entkopplung der physikalischen Dienste wird in der Gesamtarchitektur ein API-Gateway eingesetzt. Selbstverständlich ist ein API-Gateway kein Muss in einer solchen Architektur, wird aber oft gerne zur weiteren Abstraktion angewendet.

Die erste Frage, die sich stellt, wenn man aus Sicht des Endanwenders auf die Anwendung schaut: Wo kommt die SPA her und wie kommt Sie in meinen Browser?

Zuhause für SPAs: Azure Blob Storage

Eine SPA ist am Ende des Tages ein Paket bestehend aus HTML, CSS, Bildern und anderen Assets. Dieses Paket muss man nicht umständlich und teuer auf einen Webserver oder gar Application-Server deployen. Bei jeder Cloudplattform bietet sich die sehr schlanke und zudem kostengünstige Option, die SPA einfach als statisches Gesamtgebilde über einen Storage-Service bereitzustellen. Im Fall von Microsofts Cloud ist das Azure Blob Storage.

Praxistipp: Azure Portal, CLI und REST API

Für alle Azure Services kann man die Erstellung und die Konfiguration entweder über das Azure Portal, über ein REST API oder aber über das sehr mächtige Azure CLI (Command Line Interface) konfigurieren und automatisieren.

 

Ist der Storage-Account angelegt, kann man das Statische-Website-Feature aktivieren. Danach steht ein Container namens $web zur Verfügung. Dieser dient gewissermaßen als Root-Verzeichnis für einen statischen Webserver, nur eben über Blog Storage bereitgestellt. In Abbildung 2 kann man die Client-SPA aus dem Beispiel sehen, hier über das Werkzeug Azure Storage Explorer manuell in die Cloud deployt.

Abb. 2: Angular SPA über Azure Blob Storage bereitgestellt

Selbstverständlich werden die Endpunkte für den Blob-Storage-basierten Webserver über HTTPS veröffentlicht. Mit zusätzlichen Azure-Diensten wie Azure DNS und Azure CDN lassen sich zudem eigene Domains, SSL-Zertifikate und letztendlich auch superschnelle CDN-Cacheknoten etablieren. Alles, ohne einen einzigen Server anfassen oder gar kennen zu müssen.

Wenn die SPA nun also per HTTPS in unserem Browser läuft und Daten abrufen möchte, dann braucht es freilich APIs und Schnittstellen in unseren Diensten. Die werden wir nun Serverless-like implementieren und bereitstellen.

Microservice-Code und Serverless – Azure Functions

Serverless-Code für Ihre Businesslogik oder Geschäftsprozesse kann man über Azure Functions ausführen. Und Azure Functions können sogar noch mehr, als dort vorgestellt wird. Eine wichtige Eigenschaft ist etwa, dass man nicht nur .NET-Core- und C#-Code schreiben, hochladen und ausführen kann – nein, ebenso lässt sich F#, Java, JavaScript, TypeScript, PowerShell oder aber Python nutzen. Details zu den zum Zeitpunkt des Verfassens dieses Beitrags unterstützten Plattformen und Sprachen finden Sie in Tabelle 1.

Sprache Release Version
C# Final .NET Core 2.2
F# Final .NET Core 2.2
JavaScript Final Node 8 und 10
TypeScript Final (Transpilierung nach JS)
Java Final Java 8
Python Preview Python 3.6
PowerShell Preview PowerShell Core 6

Tabelle 1: In Azure Functions unterstützte Plattformen und Sprachen

Freie Wahl der Technologie

Die Plattform- und Sprachvielfalt ist in einer Microservices-Welt nicht zu unterschätzen. Denn wie wir gelernt haben, ist die freie Wahl der zur Problemstellung passenden Technologien ein mögliche Erfolgsfaktor. Wenn Sie z. B. eine Menge Java-Code haben und den mit in die Serverless Cloud nehmen wollen, dann tun sie das einfach mit Azure Functions. Oder wenn Sie Algorithmen im Umfeld von Data Science Serverless umsetzen möchten, dann können Sie das mit dem Azure-Functions-Python-Support umsetzen. Oder Sie wollen das schier unendliche Universum an Node-Modulen nutzen, dann schreiben Sie JavaScript- oder TypeScript-Code für Azure Functions.

Physikalische Isolation

Damit jeder einzelne Microservice auch wirklich von den anderen isoliert läuft, werden Functions in sogenannte Azure-Functions-Apps gekapselt. Abbildung 3 illustriert eine Azure Ressourcengruppe für die gesamte Serverless-Beispielanwendung. Hier sind alle beteiligten Azure-Ressourcen aufgeführt. In der Summe sind das wesentlich mehr, als wir hier in diesem Artikel behandeln können. Einen wichtigen Part spielen dabei die Azure-Functions-Apps (gekennzeichnet als App Service) für unsere Microservices wie Identity, Notifications, Orders, Products und Shipping (jeweils mit dem Namensschema „cw-serverless-microservices-SERVICENAME“).

Abb. 3: Azure-Ressourcengruppe mit u. a. einzelnen Azure-Functions-Apps für die jeweiligen Microservices

Praxistipp: Azure-Functions-Apps und Functions

Eine Azure-Functions-App basiert auf einem sogenannten Azure App Service, einem der klassischen PaaS-Angebote in Azure. Eine Functions-App ist somit ein komplett eigenständiges physikalisches Deployment. Darin befindet sich die Azure Functions Runtime, immer in einer ganz bestimmten Version und immer nur für eine Plattform (also .NET Core oder Java oder eine der anderen unterstützten Technologien). Ein klassischer Microservice besteht sicherlich aus mehr als einer Function. Daher ist eine Functions-App als Sammelbehälter für beliebige Functions ein geeigneter Container für Serverless Microservices.

 

Wir stellen unsere Microservices also als Verbund von Functions in Functions-Apps bereit. Doch wie kann die Client-SPA mit unseren Serverless Microservices reden?

HTTPS APIs

Wie im erwähnten Artikel von Boris Wilhelms gezeigt wird, kann man mit Azure Functions sehr einfach HTTPS APIs implementieren. Genau das tun wir auch hier für unser Order-Monitoringsystem. Der Orders Microservice hat eine in C# geschriebene Function namens SubmitNewOrder, über die einen Client mittels eines HTTPS POST eine neue Bestellung abschicken kann. Listing 1 zeigt den kompletten Code hierfür. Die Connection-Strings, die im Beispiel verwendet werden, sind nicht im Code, sondern über die Application-Settings abgebildet und im Artikel nicht zu sehen. Vertraut dürfte dem Leser vor allem der HttpTrigger für die POST-Operation in den Zeilen 5-7 vorkommen – wenn man den genannten Artikel gelesen hat.

Listing 1

1    public class SubmitNewOrderFunctions
2    {
3      [FunctionName("SubmitNewOrder")]
4      public async Task<IActionResult> Run(
5        [HttpTrigger(AuthorizationLevel.Anonymous,
6          "POST", Route = "api/orders")]
7        HttpRequest req,
8
9        [ServiceBus("ordersforshipping", Connection="ServiceBus")]
10       IAsyncCollector<Messages.NewOrderMessage> messages,
11
12       ILogger log)
13     {
14       if (!await req.CheckAuthorization("api"))
15       {
16         return new UnauthorizedResult();
17       }
18
19       var newOrder = req.Deserialize<DTOs.Order>();
20       newOrder.Id = Guid.NewGuid();
21       newOrder.Created = DateTime.UtcNow;
22
23       var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
24       var userId = identity.Name;
25
26       var newOrderMessage = new Messages.NewOrderMessage
27       {
28         Order = Mapper.Map<Entities.Order>(newOrder),
29         UserId = userId
30       };
31
32       try
33       {
34         await messages.AddAsync(newOrderMessage);
35         await messages.FlushAsync();
36       }
37       catch (ServiceBusException sbx)
38       {
39         // TODO: retry policy & proper error handling
40         log.LogError(sbx, "Service Bus Error");
41         throw;
42       }
43
44       return new OkResult();
45     }
46   }

Doch was genau passiert da zu Beginn des Funktionskörpers in Zeile 14-17? Die Methode CheckAuthorizationauf dem req-Parameter zieht hier unsere volle Aufmerksamkeit auf sich.

Securitytokens für alle: IdentityServer

Damit wir unser Microservices-System an den API-Grenzen und auch über die entkoppelten Kommunikationswege auf Anwendungsebene absichern können, verwenden wir JSON Web Tokens aus den OAuth-2.0- und OIDC-Standards. Zeile 14 in Listing 1 schaut auf dem HTTP Request in der Azure Functions Pipeline, ob es ein gültiges JWT gibt. Den exakten Code können Sie wie den gesamten restlichen Code für das Beispiel im zugehörigen GitHub Repository einsehen. Doch wo kommt dieses Token ursprünglich her?

Eine Option in Azure für Authentifizierung von Benutzern und Anwendungen ist das Microsoft-eigene Azure Active Directory (AAD) für Organisationsinterne Benutzer und deren Daten, oder aber auch alternativ AAD B2B für die Integration externer Benutzer. Wenn man jedoch mehr Flexibilität und vollen Sourcecodezugriff in Form eines Open-Source-Projekts genießen möchte, kann man zum beliebten IdentityServer-Projekt greifen. IdentityServer ist ein Framework, kein vollwertiger Server oder gar Service. Man passt die Basisfunktionalität an sein jeweiliges Projekt an und hostet es dann beispielsweise in der Cloud, etwa über Azure App Service.

Mit ein paar Kniffen lässt sich IdentityServer – da es ein ganz normales ASP.NET-Core-MVC-Projekt ist – auch in Azure Functions bereitstellen. Abbildung 4 zeigt in recht unspektakulärer Art eine exemplarische IdentityServer-Instanz, jedoch vollständig Serverless über Azure Functions zur Verfügung gestellt. Anders ausgedrückt: ein Wolf im Schafspelz.

Abb. 4: IdentityServer als vollwertiges OAuth 2.0 und OIDC Framework in Azure Functions gehostet – Serverless-Tokens, sozusagen

Über das vom IdentityServer ausgestellte Token können konsumierende Client oder Services Anfragen z. B. an unseren Products oder Orders Service stellen. Diese überprüfen das Token, ob es von einem bekannten Aussteller und noch nicht abgelaufen ist. Zusätzlich kann jeder fachliche Service für sich entscheiden, ob weiter sogenannte Claims überprüft werden müssen, um dem Aufrufer Zugang zur Businesslogik zu gewähren. Dieses Token verwendet nun aber nicht nur die Frontend Services, die direkt von der SPA angesprochen werden, nein – Vielmehr werden Informationen aus dem Token auch bei den entkoppelten und asynchron arbeitenden Microservices wie dem Shipping Service genutzt. Doch wie funktioniert diese viel besagte Entkopplung?

Asynchrone entkoppelte Kommunikation: Azure Service Bus

Für die Entkopplung auf der Zeitachse, also für eine asynchrone Kommunikation und damit die Möglichkeit der asynchronen Ausführung von Code nutzen wir ein Message-Queue-System. Im Fall von Serverless Azure ist das der Azure Service Bus. Service Bus ist ein sehr robuster, weil auch schon seit vielen Jahren existierender Dienst in der Microsoft-Cloud. Er nutzt zum Zwecke der Interoperabilität das allseits anerkannte Advanced Message Queuing Protocol (AMQP) in der Version 1.0. Neben simplen Nachrichtenschlangen (Queues) kann man mit dem Service Bus auch weiterführende Patterns implementieren, etwa Topics und Subscriptions.

Damit wir nun also in unserem Beispiel den Orders Service vom Shipping Service trennen können, legt der Orders Service eine Nachricht in eine Message-Queue namens ordersforshipping. Dies sieht man gut im C#-Code in Listing 1 (Zeilen 19-35). Mit dem API-Aufruf messages.FlushAsync()wird die Service-Bus-Client-Library angewiesen, alle bislang im Code angesammelten Nachrichten in die konfigurierte Queue zu senden. In bester Azure-Functions-Manier ist messagesan ein Functions-Output-Binding gebunden. Dieses Binding für den Service Bus wird im Beispiel asynchron genutzt: Wir nutzen also ein asynchrones API, um asynchron Daten in Nachrichten auszutauschen. Ergibt Sinn.

Ein Blick auf Abbildung 1 verrät uns, dass der Shipping Service nur über diese Message-Queue zu erreichen ist und ansonsten keinerlei API bietet. Das bedeutet, dass er in Ruhe seine Businesslogik für die Abarbeitung von Bestellungen und die Abwicklung von Auslieferungen ausführen kann. Ebenso bedeutet es, dass etwaige Resultate oder fachliche Antworten auf Bestellvorgänge ebenfalls als Nachrichten formuliert werden und in eine neue Queue geschrieben werden müssen.

Während der Orders Service in C# mit .NET Core umgesetzt wurde, handelt es sich beim Shipping Service um eine Java-basierte Lösung. Somit sehen wir auch, dass Azure Functions wirklich über mehrere Plattformen hinweg funktioniert. Listing 2 zeigt die ganze Pracht des simplen Java-Codes in einer Function mit Service-Bus-Trigger und Service-Bus-Output-Binding. Über den Trigger in Zeile 11 lauscht der Java-Code auf der Message-Queue ordersforshipping. Nach Durchlaufen des überaus komplexen Codes mit all seiner aufwendigen Businesslogik wird eine Ergebnisnachricht erzeugt, befüllt und schließlich als Rückgabewert der Function in die Queue shippingsinitiated gesendet (Zeile 19 bis 31).

Listing 2

1    public class CreateShipment {
2      /**
3       * @throws InterruptedException
4       * @throws IOException
5       */
6      @FunctionName("CreateShipment")
7      @ServiceBusQueueOutput(name = "$return",
8        queueName = "shippingsinitiated",
9        connection = "ServiceBus")
10     public String run(
11       @ServiceBusQueueTrigger(name = "message", 
12         queueName = "ordersforshipping", 
13         connection = "ServiceBus")
14       NewOrderMessage msg,
15       final ExecutionContext context
16     ) throws InterruptedException, IOException {
17       // NOTE: Look at our complex business logic 😉
18       // Yes - do the REAL STUFF here!
19       Thread.sleep(5000);
20
21       ShippingCreatedMessage shippingCreated =
21           new ShippingCreatedMessage();
22       shippingCreated.Id = UUID.randomUUID();
23       shippingCreated.Created = new Date();
24       shippingCreated.OrderId = msg.Order.Id;
25       shippingCreated.UserId = msg.UserId;
26
27       Gson gson = new GsonBuilder().setDateFormat(
28         "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'").create();
29       String shippingCreatedMessage = gson.toJson(shippingCreated);
30
31       return shippingCreatedMessage;
32     }
33   }

Sie sehen und spüren regelrecht, wie sehr man sich mit Azure Functions, dessen Triggers und Bindings sehr stark auf die Implementierung der eigentlichen Logik konzentrieren kann und sich recht wenig mit dem infrastrukturellen Drumherum auseinandersetzen muss. So soll es sein, denn wir wollen ja Softwarelösungen bauen, keine Infrastrukturmonster.

Der Shipping Service hat mittlerweile seine Schuldigkeit getan und seine ihm wichtigen Informationen in einer Message-Queue abgelegt. Dem Orders Service ist das ziemlich egal – aber es gibt jemanden im Gesamtsystem, dem diese Info durchaus wichtig ist – denn der Endanwender hat sich registriert, um über Neuigkeiten bezüglich der Auslieferungen (Shippings) benachrichtigt zu werden.

Webbasierte Realtimebenachrichtigungen: Azure SignalR Service

Durch die Pushnachrichten aus dem mobilen Umfeld unserer Lieblingsapps entfacht, werden Realtimebenachrichtigungen auch im Businesskontext immer wichtiger. Im Order-Monitoringsystem möchte der User der SPA gerne wissen, wenn es einerseits eine neue, erfolgreich erstellte Bestellung gibt, und vor allem andererseits, wenn diese Bestellung zur Auslieferung kommt.

Für Realtimebenachrichtigungen im Webumfeld nimmt man auf Anwendungsebene gerne WebSockets als unterliegende Technologie. WebSockets funktionieren jedoch nicht immer und überall (Browserversionen, Netzwerk-Proxies etc.). Zudem müssen Verbindungsabbrüche selbst behandelt werden und das eigentliche anwendungsspezifische Protokoll zum Datenaustausch muss auch jedes Mal definiert und implementiert werden. Um all diese Anforderungen ein bisschen besser handhabbar zu gestalten, gibt es SignalR vom ASP.NET-Core-Team und den Signal Service vom Azure-Team. Für SignalR gibt es Clientbibliotheken für alle möglichen Plattformen und Programmiersprachen, allen voran natürlich JavaScript. Diese nutzen wir hier in unserer SPA.

Für unsere Azure Functions gibt es zudem Bindings für den Azure SignalR Service, wer hätte das gedacht? Der Notifications Service (vgl. Abb. 1) bedient sich eines Service-Bus-Triggers, um Nachrichten aus der shippingsinitiated Queue zu holen (siehe Listing 3). Diese werden dann in der Function aufbereitet, um anschließend, wie in Zeile 16 zu sehen ist, über das SignalR-Service-Output-Binding über den SignalR Service an die vorher registrierten Clients gesendet zu werden. Fertig.

Die Clients erhalten dann per SignalR-Verbindung (in den meisten Fällen wird das physikalisch eine WebSocket Connection sein) die Information, dass es neue Daten/Argumente für das Ziel shippingInitiated gibt. Der Client kann dann entscheiden, wie der damit umgeht. In der SPA wird einfach ein Event lokal ausgelöst, um danach die aktuelle Liste an Orders mit allen Produktinformationen von den jeweiligen Services zu laden.

Listing 3

1    public class NotifyClientsAboutOrderShipmentFunctions
2    {
3      [FunctionName("NotifyClientsAboutOrderShipment")]
4      public async Task Run(
5        [ServiceBusTrigger("shippingsinitiated", Connection = "ServiceBus")]
6        ShippingCreatedMessage msg,
7
8        [SignalR(HubName="shippingsHub", ConnectionStringSetting="SignalR")]
9        IAsyncCollector<SignalRMessage> notificationMessages,
10
11       ILogger log)
12     {
13       var messageToNotify = new { 
14         userId = message.UserId, orderId = message.OrderId };
15
16       await notificationMessages.AddAsync(new SignalRMessage
17       {
18         Target = "shippingInitiated",
19         Arguments = new[] { messageToNotify }
20       });
21     }
22   }

Wir haben bis hierhin schon einiges umgesetzt bekommen mit unserer Idee von Serverless Microservices. Und das Schöne an Serverless ist eben immer wieder, dass man komplexe verteilte Architekturen relativ schnell und unkompliziert umgesetzt bekommt. Hier ein Service, da ein Service. Hier ein Client, da eine Queue, dort ein Notification-Dienst. Doch wer behält da den Überblick? Was passiert wo und wie?

„Ich sehe was, was du nicht siehst …“ – Azure Application Insights

Sicherlich hat eine derart verteilte und lose gekoppelte Architektur nicht nur Vorteile. Die Sicht auf ein solches System zur Laufzeit ist enorm wichtig, um beispielsweise Fehler mitzubekommen und darauf reagieren zu können. Für Azure können wir uns in diesem Fall der Application Insights bedienen (diese werden gerade in den sogenannten Azure Monitor integriert).

Mit Application Insights können wir vor allem das Laufzeitverhalten unserer Systeme monitoren. Für Fehler oder auftretenden Schwellwerten bei spezifischen Metriken können wir auch Regeln festlegen, was darauffolgend passieren soll –beispielsweise das Versenden von Emails o. Ä. Im Rahmen unserer Serverless-Beispielanwendung für diesen Artikel werfen wir mal einen Blick auf das werte Befinden unseres Identity Services. Über den Live Metrics Stream können wir tatsächlich in Echtzeit in das Herz unseres Identity Services blicken.

Wie man in Abbildung 5 sehen kann, bekommen wir Einsicht in die Telemetriedaten, Requests, Speicher- und CPU-Auslastung und auch auf die automatisch verwaltete Anzahl von Serverinstanzen in unserer Serverless-Azure-Functions-Anwendung. Eine Info, die uns ja eigentlich egal sein sollte, den wir wollen ja explizit nichts mehr mit Servern zu tun haben. Aber es ist durchaus interessant zu beobachten, wie sich Azure Functions unter Last verhält. Im Screenshot sieht man, dass wir ca. 800-900 Request pro Sekunde verarbeiten und die Azure Functions Runtime bzw. der Scale-Controller mittlerweile auf bis zu zwölf Serverinstanzen skaliert hat – ohne jegliches Zutun unsererseits. Und wenn die Last abnimmt, dann wird der Scale Controller auch wieder für das Scale-In sorgen. Wichtig ist, nochmals festzuhalten, dass wir nur dann Geld bezahlen, wenn unser Code auch wirklich ausgeführt wird, egal, wie viele Server in der Zwischenzeit Däumchen drehen.

Abb. 5: Echtzeiteinblicke in Serverless Microservices mit Application Insights Live Metrics

End-to-End Microservices mit Serverless

Wenn man nun alle der in Abbildung 1 aufgemalten Artefakte nimmt und sie auf Azure Services abbildet, landet man bei Abbildung 6: Die Umsetzung des Serverless-Order-Monitoringsystems mit Serverless Azure – voilà.

Im Verlauf des Artikels haben wir uns gemeinsam angeschaut, wie man auf pragmatische Art und Weise End-to-End Microservices mit Serverless und der Azure-Cloud umsetzen kann. Mit Azure Functions und den dazu passenden anderem Azure-Diensten ist das mit relativ wenig Aufwand in kurzer Zeit möglich. Natürlich steckt der Teufel im Detail, keine Frage. Und selbstverständlich kann jedwede Darstellung im Rahmen einess Beitrags nur ein Kratzen an der Oberfläche sein. Daher zum Schluss nochmals der Aufruf, einen Blick in den Sourcecode des Beispiels zu werfen.

Abb. 6: Serverless-Microservices-Beispiel – umgesetzt mit Serverless-Azure-Diensten

 

Azure-DevOps-Spickzettel


Der Azure-DevOps-Spickzettel von Dr. Holger Schwichtenberg zeigt Ihnen kurz und knapp, wie Sie mit wenigen Befehlen das Kommando über Azure DevOps übernehmen können.

Download for free

Ihr aktueller Zugang zur .NET- und Microsoft-Welt.
Der BASTA! Newsletter:

Behind the Tracks

.NET Framework & C#
Visual Studio, TFS, C# & mehr

Agile & DevOps
Best Practices & mehr

Web Development
Alle Wege führen ins Web

Data Access & Storage
Alles rund um´s Thema Data

HTML5 & JavaScript
Leichtegewichtig entwickeln

User Interface
Alles rund um UI- und UX-Aspekte

Microservices & APIs
Services, die sich über APIs via REST und JavaScript nutzen lassen

Security
Tools und Methoden für sicherere Applikationen

Cloud, Azure, Serverless
Cloud-basierte & Native Apps