Software wird seit vielen Jahren immer komplexer, wodurch es für Entwickler:innen immer schwieriger wird, Fehler zu beheben oder auch nur die Software am Entwicklungsrechner zu starten. In der Cloud gibt es mittlerweile eine Vielzahl von Tools, die, nachdem sie konfiguriert sind, den Betrieb vereinfachen. Für verteilte Systeme wird häufig Kubernetes verwendet, Deployments sind via CI/CD Pipelines automatisiert und alle Cloud-Anbieter stellen Tools zum Monitoring, Sammeln von Logs etc. zu Verfügung. Für die lokale Entwicklung hat es aber bis jetzt nur vereinzelt Lösungen gegeben, die allerdings alle mit ihren eigenen Nachteilen kommen.
Die wahrscheinlich am häufigsten eingesetzte Methode zum Testen von verteilter und komplexer Software am Entwicklungsrechner ist Docker Compose. Hierbei werden alle benötigten Komponenten in einer YAML-Datei konfiguriert und anschließend gemeinsam gestartet. Der Vorteil ist, dass diese YAML-Datei in Git eingecheckt wird und so einfach zwischen den Entwickler:innen geteilt werden kann. Diese Datei kann auch als Dokumentation dienen, da sie die Abhängigkeiten zwischen den Anwendungen beschreibt und festhält, welche Umgebungsvariable verwendet wird und welche Ports benötigt werden. Ein Beispiel dafür ist die Konfiguration des eigenen Frontends, eines API-Backends, einer Datenbank und eines Cache. Mit dem Befehl docker-compose up können alle Anwendungen gestartet und getestet werden. Der Nachteil dieser Lösung ist allerdings, dass die Anwendungen als Docker-Container laufen und nicht in einer Entwicklungsumgebung debuggt werden können.
ZUM NEWSLETTER
Regelmäßig News zur Konferenz und der .NET-Community
Aus diesem Grund hat Microsoft im Jahr 2020 Project Tye [1] vorgestellt, um die Entwicklung und die Fehlersuche in verteilten Systemen zu vereinfachen. Die Idee war gut, die ersten Versionen vielversprechend. Leider fehlte jedoch der Fokus, um die gemeldeten Probleme zu beheben. Daher verlief sich Project Tye mit der Zeit und wurde schließlich 2023 archiviert. Project Tye wirkt auf den ersten Blick wie eine gute Idee, die zu einem grandiosen Fehlschlag wurde. Allerdings hat Microsoft aus den Fehlern und Problemen gelernt und hat zeitgleich mit der Einstellung von Project Tye .NET Aspire vorgestellt.
.NET Aspire wurde erstmals im November 2023 auf der .NET Conf vorgestellt und hat seitdem erhebliches Interesse geweckt. Seit seiner offiziellen Veröffentlichung im Mai 2024 ist es zu einem zentralen Thema auf jeder Entwicklerkonferenz geworden. Microsoft zeigt kontinuierlich sein Engagement für .NET Aspire durch regelmäßige Erweiterungen und Updates, die die Plattform ständig verbessern und Fehler beheben. Diese kontinuierliche Weiterentwicklung stellt sicher, dass .NET Aspire immer auf dem neuesten Stand der Technik ist und Entwickler:innen immer bessere Werkzeuge zur Verfügung stellt.
Entwickler:innen können .NET Aspire einfach zu bestehenden Projekten hinzufügen und damit Abhängigkeiten zwischen verschiedenen Komponenten konfigurieren. Der Vorteil gegenüber anderen Lösungen, wie zum Beispiel Docker Compose, ist, dass .NET Aspire als Code konfiguriert wird und so die Verwendung von IDEs zum Debuggen erlaubt. Nachdem Aspire zu einem Projekt hinzugefügt wurde, konfiguriert es das Sammeln von Logs, Metriken und Traces und stellt zusätzlich ein Dashboard zu Verfügung. Der oder die Entwickler:in muss sich dabei um nichts kümmern, hat aber die Möglichkeit, die Konfiguration den eigenen Anforderungen anzupassen. Zusätzlich können weitere Tools, zum Beispiel PostgreSQL, RabbitMQ oder Azure Service Bus über NuGet installiert und mit wenigen Zeilen Code konfiguriert werden.
Listing 1 zeigt die Konfiguration eines Aspire-Projekts. Dieses Projekt hat Redis und PostreSQL via NuGet installiert und ein Frontend, das beide referenziert. Zusätzlich wird PostgreSQL mit der Methode WithPgAdmin() konfiguriert, wodurch Aspire einen Container mit pgAdmin startet und diesen automatisch für den Zugriff auf den PostgreSQL-Server konfiguriert.
// Create a distributed application builder given the command line arguments. var builder = DistributedApplication.CreateBuilder(args); // Add a Redis server to the application. var cache = builder.AddRedis("cache"); var postgres = builder.AddPostgres("database") .WithPgAdmin(); // Add the frontend project to the application and configure it to use the // Redis server, defined as a referenced dependency. builder.AddProject<Projects.MyFrontend>("frontend") .WithReference(cache) .WithReference(postgres) .WaitFor(cache);
Zusätzlich zur verbesserten lokalen Entwicklung kann Aspire verwendet werden, um das Projekt schnell und einfach auf Azure zu deployen. Wie bei der Entwicklung übernimmt Aspire dabei die gesamte Konfiguration und kann mit wenigen Befehlen deployt werden.
In den folgenden Abschnitten wird die Funktionsweise von Aspire anhand einiger praktischer Demos gezeigt.
Voraussetzungen für .NET Aspire
Um Aspire verwenden zu können, werden die folgenden Tools benötigt:
- .NET 8.0 oder .NET 9.0 SDK
- .NET Aspire SDK
- eine OCI-compliant Container-Runtime, z. B. Docker Desktop oder Podman
- eine Entwicklungsumgebung, z. B. Visual Studio ab Version 2022 17.9 oder Visual Studio Code
.NET-Aspire-Projekte sind für die Ausführung in Containern konzipiert. Sie können entweder Docker Desktop oder Podman als Containerlaufzeit verwenden. Docker Desktop ist die am häufigsten verwendete Laufzeit. Podman ist eine Open-Source-Alternative zu Docker, die ohne Daemon auskommt und OCI-Container (Open Container Initiative) erstellen und ausführen kann. Wenn sowohl Docker als auch Podman installiert sind, verwendet .NET Aspire standardmäßig Docker. .NET Aspire kann mit der Umgebungsvariable DOTNET_ASPIRE_CONTAINER_RUNTIME konfiguriert werden, um stattdessen Podman zu verwenden (Listing 2).
# Windows [System.Environment]::SetEnvironmentVariable("DOTNET_ASPIRE_CONTAINER_RUNTIME", "podman", "User") # Linux export DOTNET_ASPIRE_CONTAINER_RUNTIME=podman
.NET-Aspire-Demoprojekte von Microsoft
Nach der ganzen Theorie ist es jetzt Zeit für etwas Praktisches. Microsoft hat auf GitHub [2] ein Repository mit Demoprojekten für .NET Aspire veröffentlicht. Dieses Repository enthält unter anderem Beispiele für Aspire mit Azure Functions, Node.js oder Python. Diese Beispiele befinden sich im samples-Ordner des GitHub-Repositorys. Für diesen Artikel wird der Aspire-Shop verwendet.
Microsoft hat in der Vergangenheit schon öfter einen Onlineshop für Demos verwendet. Dieser konnte zwar via Docker Compose gestartet werden, war ansonsten aber aufgrund der Vielzahl an Services und Datenbanken recht kompliziert für neue Benutzer:innen. Aspire auf der anderen Seite bietet ein Dashboard mit allen gestarteten Containern und deren Status an. Dadurch müssen Entwickler:innen nicht mehr die Dokumentation oder Docker-Compose-Dateien durchsuchen, um die Ports und Pfade für die verschiedenen Container herauszufinden.
Nach dem Öffnen der Aspire-Shop-Solution sollte das AspireShop.AppHost-Projekt als Start-up-Projekt ausgewählt sein. Falls nicht, muss es als Start-up-Projekt konfiguriert werden; anschließend kann die Anwendung gestartet werden. Daraufhin öffnet sich das Aspire-Dashboard, welches eine Übersicht über alle Ressourcen im Projekt gibt und dessen Status bzw. Endpoints anzeigt. Abbildung 1 zeigt, dass einige Anwendungen schon gestartet sind, sowie zwei davon den Status „Unhealthy“ haben bzw. zwei noch nicht gestartet sind.
Abb. 1: Das Aspire-Dashboard zeigt den Status aller Anwendungen im Projekt
Je nach Internetgeschwindigkeit sollte es beim ersten Start wenige Minuten dauern, bis alle Container heruntergeladen und gestartet sind. Dann sollte der Status für alle Container „Running“ sein. Mit Hilfe der Endpoints kann jetzt ganz einfach durch die verschiedenen Anwendungen navigiert werden. So können zum Beispiel die Endpoints für das Swagger UI des Catalog Services, pgAdmin für Postgres oder das Frontend ganz einfach aufgerufen und ausprobiert werden.
Das Dashboard macht es neuen Entwickler:innen deutlich leichter, sich in einem Projekt zurecht zu finden. Zusätzlich bietet Aspire nützliche Informationen für das Monitoring und Debugging an. Auf der linken Seite im Dashboard kann so zwischen verschiedenen Ansichten für die Konsolenausgabe aller Container, Logs, Traces und Metriken gewechselt werden; zudem kann innerhalb dieser Ansichten gefiltert bzw. sortiert werden. Abbildung 2 zeigt, dass es mit Hilfe von OpenTelemetry eine Vielzahl von Metriken für den basketservice gibt, welche sowohl grafisch als auch als Tabelle angezeigt werden können.
Abb. 2: Das Aspire-Dashboard bietet eine Vielzahl von Metriken an
Vor allem verteilte Systeme sind dafür bekannt, unübersichtlich zu sein, wie auch dafür, dass die Fehlersuche für Entwickler:innen aufwendig und kompliziert sein kann. Hier ist Aspire sehr hilfreich, weil es OpenTelemetry, Health Checks und Service Discovery automatisch hinzufügt. Wie das genau funktioniert, sehen wir uns an einem bestehenden Blazor-Projekts an.
Hinzufügen von Aspire zu einem bestehenden Projekt
Aspire kann zu einem bestehenden Projekt mit Visual Studio, Visual Studio Code, JetBrains Rider oder via .NET CLI hinzugefügt werden. Für das .NET CLI kann der Befehl dotnet new aspire-servicedefaults -n ServiceDefaults verwendet werden. Allerdings müssen dann die Referenzen zu den bestehenden Projekten manuell hinzugefügt werden und das Aspire-Projekt muss per Hand konfiguriert werden.
Aus diesem Grund wird für diesen Abschnitt Visual Studio verwendet, um .NET Aspire zu einem bestehenden Blazor-Projekt mit dem Namen BlazorWithAspire hinzuzufügen. Grundsätzlich kann Aspire aber zu jedem .NET-Projekt ab .NET 8 hinzugefügt werden. Dazu klickt man mit der rechten Maustaste auf das Blazor-Projekt und wählt Add | .NET Aspire Orchestrator Support aus. Dies generiert zwei neue Projekte, BlazorWithAspire.AppHost und BlazorWithAspire.ServiceDefaults.
ZUM NEWSLETTER
Regelmäßig News zur Konferenz und der .NET-Community
Das AppHost-Projekt konfiguriert alle Projekte in der Programm.cs-Datei, die mit Aspire gestartet werden sollen. In diesem Fall ist es nur das BlazorWithAspire-Projekt:
var builder = DistributedApplication.CreateBuilder(args); builder.AddProject<Projects.BlazorWithAspire>("blazorwithaspire"); builder.Build().Run();
Wenn während der Entwicklung neue Projekte erstellt werden, können diese hier hinzugefügt und konfiguriert werden.
Das zweite Aspire-Projekt, ServiceDefaults, ist im Moment wahrscheinlich das interessantere Projekt, um die Funktionsweise von Aspire zu verstehen. In diesem Projekt befindet sich eine Datei, Extensions.cs, die alle Aspire-Features konfiguriert, damit diese out of the box verwendet werden können. So werden zum Beispiel Health Checks, Metriken und Logs konfiguriert. Listing 3 zeigt einen Ausschnitt aus dieser Datei, in dem diverse Features wie OpenTelemetry und Service Discovery hinzugefügt werden.
builder.ConfigureOpenTelemetry(); builder.AddDefaultHealthChecks(); builder.Services.AddServiceDiscovery(); builder.Services.ConfigureHttpClientDefaults(http => { // Turn on resilience by default http.AddStandardResilienceHandler(); // Turn on service discovery by default http.AddServiceDiscovery(); });
In dieser Datei wird die ganze „Magie“ von .NET Aspire konfiguriert. Wenn das AppHost-Projekt jetzt gestartet wird, sollte sich das Aspire-Dashboard mit einer Anwendung öffnen. Zusätzlich können die Logs, Metriken und Traces der Anwendung angezeigt werden. Abbildung 3 zeigt die Dauer der Requests der Blazor-App an, ohne dass die Blazor-App dafür konfiguriert werden musste.
Abb. 3: .NET Aspire zeigt die Metriken der Blazor-App
Die Demo hat bis jetzt gezeigt, dass mit .NET Aspire zusätzliche Dienste wie Logs und Metriken zur Anwendung hinzugefügt werden können, ohne diese zu verändern. Ein weiterer interessanter Aspekt der Demo ist, dass sie zeigt, wie Aspire die konfigurierten Anwendungen startet. Im Abschnitt über den Onlineshop wurde erwähnt, dass es ein paar Minuten dauern kann, bis alle Container heruntergeladen und gestartet sind. Das ist zwar korrekt, könnte aber den Eindruck erwecken, dass alle Anwendungen als Container gestartet werden – .NET Aspire startet .NET-Anwendungen allerdings nicht als Container. Das kann ganz einfach mit dem docker ps-Befehl überprüft werden. Nachdem Aspire die Demo-Blazor-Anwendungen gestartet hat, zeigt docker ps aber keinen laufenden Container an. Der Grund dafür ist, dass Microsoft das Debugging für Entwickler:innen so einfach wie möglich machen möchte und deshalb die Anwendungen ohne Container startet.
Anwendungen mit Aspire schnell auf Azure deployen
Die vorangegangenen Abschnitte haben eine Einführung in .NET Aspire gegeben und aufgezeigt, wie damit der Entwicklungsprozess vereinfacht werden kann. Aspire kann aber auch beim Deployment der gesamten Umgebung auf Azure helfen.
Dafür wird zusätzlich zu den bereits installierten Tools noch die Azure Developer CLI [3] benötigt. Mit dieser kann die Anwendung einfach in eine oder mehrere Azure-Container-Apps deployed werden. Das Developer CLI und Aspire sorgen dafür, dass alle benötigten Ressourcen erstellt und konfiguriert werden. So kann zum Beispiel, je nach Konfiguration des eigenen Projekts, ein Azure Key zum Speichern von sensitiven Daten wie Passwörtern oder Connection Strings gespeichert werden oder eine PostgresSQL-Datenbank erstellt werden. Die Entwickler:in muss sich dabei um nichts selbst kümmern.
Eine weitere Möglichkeit, .NET Aspire kennenzulernen, ist die .NET Aspire Starter App-Vorlage. Diese Vorlage erstellt ein Blazor-Frontend, ein API, optional ein Testprojekt mit .NET Aspire. Abbildung 4 zeigt, dass beim Erstellen des Projekts in Visual Studio Redis zum Projekt hinzugefügt werden kann. Aspire kümmert sich dabei um die Integration und Konfiguration von Redis.
Abb. 4: Aspire kann automatisch Redis zum Projekt hinzufügen und konfigurieren
Die Vorlage erstellt eine Blazor-Anwendung mit einem Frontend, API-Backend und Redis. Die Program.cs-Datei im AppHost-Projekt konfiguriert, dass das Frontend das API und Redis verwendet; zusätzlich wird das Frontend mit dem Parameter WithExternalHttpEndpoints() konfiguriert. Dieser Parameter ist für das Deployment nützlich, da nur das Frontend einen öffentlichen URL bekommt und das Backend und Redis nicht über das Internet erreichbar sein sollen. Listing 4 zeigt die gesamte Program.cs-Datei.
var builder = DistributedApplication.CreateBuilder(args); var cache = builder.AddRedis("cache"); var apiService = builder.AddProject<Projects.BlazorWithAspireForAzure_ApiService>("apiservice"); builder.AddProject<Projects.BlazorWithAspireForAzure_Web>("webfrontend") .WithExternalHttpEndpoints() .WithReference(cache) .WaitFor(cache) .WithReference(apiService) .WaitFor(apiService); builder.Build().Run();
Zum Testen der Anwendung kann sie gestartet und im Frontend die verschiedenen Menüpunkte aufgerufen werden. Diese Aufrufe sieht man auch im Aspire-Dashboard im Bereich Traces. Abbildung 5 zeigt zum Beispiel, dass der weather Endpoint vom Frontend aufgerufen wurde, dieser weatherforcast auf dem API aufgerufen und dann Daten in Redis geschrieben hat.
Abb. 5: Die Trace zeigt den Request vom Frontend an das Backend und Redis
Nachdem die Anwendung getestet ist, kann sie mit dem Azure Developer CLI deployt werden. Zunächst kann mit dem Befehl azd auth login in der Kommandozeile im zuvor angelegten Projekt das Log-in durchgeführt werden; anschließend wird mit azd init der Deployment Wizard gestartet. Das Deployment für diese Demo wurde mit den folgenden Angaben konfiguriert:
- Use code in the current directory: Dieser Befehl scannt den aktuellen Ordner nach Projekten und listet diese auf.
- Confirm and continue initializing my app: Wenn das CLI das richtige Projekt gefunden hat, kann mit diesem Projekt der Wizard fortgesetzt werden.
- Aspire-Demo: Als letzter Schritt muss ein Name für die Ressource-Gruppe angegeben werden, in die die Anwendung später deployt wird. Diese Ressource-Gruppe wird beim Deployment ebenfalls erstellt.
Nachdem der Name für die Ressource-Gruppe eingegeben ist, erstellt das Developer CLI zwei Dateien: azure.yaml und next-steps.md. Die Datei azure.yaml beinhaltet den Pfad zum zuvor konfigurierten Projekt; die next-steps.md-Datei gibt eine detaillierte Anleitung, wie die Anwendung deployt werden kann und bietet zusätzlich Links zur Dokumentation für weitere Informationen an. Abbildung 6 und Abbildung 7 zeigen das Konsolenfenster, in dem die Demoanwendung mit dem Azure Developer CLI konfiguriert wurde.
Abb. 6: Das Azure Developer CLI listet das Demoprojekt auf
Abb. 7: Das Azure Developer CLI erstellt die Deployment-Dateien
Der letzte Befehl für das Deployment ist azd up. Dieser Befehl erstellt alle benötigten Ressourcen in Azure, bevor die Demoanwendung deployt wird. Mit den Befehlen azd provision und azd deploy kann das Deployment auf zwei Schritte aufgeteilt werden. Nach der Eingabe des azd provision-Befehls (und auch nach der Eingabe von azd up) muss die gewünschte Azure Subscription und Region angegeben werden. Danach erstellt das CLI die benötigten Ressourcen, wie in Abbildung 8 gezeigt. Das Developer CLI verwendet Bicep-Skripte [4] für das Deployment. Dadurch können Änderungen ganz einfach mit denselben Befehlen deployt werden, und Bicep aktualisiert nur die angepassten Ressourcen.
Abb. 8: Alle benötigten Azure-Ressourcen wurden mit dem Azure Developer CLI automatisch erstellt
Folgende Ressourcen wurden erstellt:
- Ressource-Gruppe: Diese Gruppe beinhaltet alle Ressourcen.
- Container-Registry: In diese Registry werden die Container hochgeladen, die für die Azure-Container-Apps verwendet werden.
- Log Analytics Workspace: Alle Logs und Metriken werden im Workspace gespeichert und können dort durchsucht und angezeigt werden.
- Container-Apps-Environment: Vereinfacht gesagt handelt es sich hierbei um einen Behälter, in dem eine oder mehrere Container-Apps erstellt werden können. Alle Container in dieser Umgebung können untereinander kommunizieren.
- Managed Identity (wird nicht in der Konsole in Abbildung 8 angezeigt): Die Managed Identity wird von den Container-Apps verwendet und wurde berechtigt, um die Images aus der Container-Registry herunterzuladen.
Nachdem die Ressourcen angelegt wurden, kann die Anwendung mit azd deploy deployt werden. Dieser Befehl scannt das Aspire-Projekt, erstellt für alle Anwendungen einen Container und lädt diese Container in die Container-Registry hoch.
Abb. 9: Die Anwendungen und das Aspire-Dashboard wurden deployed
Abbildung 9 zeigt, dass Frontend, API-Backend, Redis und Aspire-Dashboard deployt wurden. Das Backend und Redis haben ein internal in der URL, das Frontend hat das allerdings nicht. Aspire weiß aufgrund der Konfiguration der Anwendungen, welche Container über das Internet erreichbar sein sollen und welche nicht. In der Demo-App wurde nur das Frontend mit dem zuvor beschriebenen WithExternalHttpEndpoints()-Parameter konfiguriert, wodurch nur dieser Container eine öffentliche URL bekommt. Aspire deployt auch das Aspire-Dashboard, welches fast genau wie in der lokalen Umgebung genutzt werden kann. Der einzige Unterschied zwischen der lokalen und der Azure-Instanz ist, dass für das Aufrufen des Azure-Dashboards ein Log-in benötigt wird. Aspire integriert automatisch das Log-in mit Entra in das Dashboard, wodurch nur User im Azure Tenant Zugriff auf die Anwendung haben.
Abb. 10: Das Aspire-Dashboard ist in Azure deployt
Abbildung 10 zeigt das Dashboard nach dem Log-in in Azure. Was dabei auffällt, ist, dass alle Endpoints via HTTPS aufgerufen werden können. Das ist ein nützliches Feature der Azure-Container-Apps. Alle Container-Apps werden mit einem automatisch erstellten TLS-Zertifikate erstellt und verwenden HTTPS, ohne dass die Entwickler:in etwas konfigurieren muss. Abbildung 11 zeigt die zuvor erstellten Ressourcen und die Container-Apps im Azure Portal.
Abb. 11: Alle erstellten Ressourcen im Azure Portal
Im Azure Portal sieht man auch die Konfiguration der einzelnen Container-Apps. Abbildung 12 zeigt die Umgebungsvariablen des Frontends.
Abb. 12: Die Umgebungsvariablen des Frontends wurden automatisch konfiguriert
Hier sind zum Beispiel die Endpoints der Redis- und Backend-Container konfiguriert, wie auch der Connection String für Redis. Hier sieht man zudem, dass sensitive Daten, zum Beispiel der Connection String, als Secret referenziert werden.
Das letzte interessante Feature des Deployments ist das Erstellen der Container für Frontend und Backend. Etwas weiter oben wurde die Azure-Container-Registry erwähnt, welche die Container-Images für die Container-Apps beinhaltet. Während der Entwicklung oder des Deployments wurde aber kein Dockerfile oder Ähnliches konfiguriert, um die Images zu erstellen. Um die Container-Images ohne Dockerfiles zu erstellen, nutzt Aspire ein .NET-Feature, das mit .NET 8 eingeführt wurde [5]. Seit .NET 8 kann das .NET SDK Docker Images erstellen, ohne dass dafür ein Dockerfile benötigt wird. Das erlaubt das einfache und schnelle Deployment des Aspire-Projekts.
Wenn die Ressourcen nicht mehr benötigt werden, können sie mit dem Befehl azd down gelöscht werden. Das Azure Developer CLI listet alle Ressourcen auf; nachdem das Löschen bestätigt ist, wird die gesamte Ressource-Gruppe gelöscht. Das dauert normalerweise nur wenige Minuten. Abbildung 13 zeigt aber, dass es manchmal auch etwas länger dauern kann.
Abb. 13: Die gesamte Umgebung wurde gelöscht
Migration von Docker Compose zu .NET Aspire
Viele Projekte verwenden für die Konfiguration der Abhängigkeiten während der Entwicklung Docker Compose. Diese Abhängigkeiten können eine Datenbank, Redis oder eine Queue wie etwa RabbitMQ sein, welche einfach mit einem Befehl gestartet werden können. Dadurch müssen sich vor allem neue Entwickler:innen nicht um die Konfiguration aller Komponenten kümmern und können so schnell produktiv im Projekt mitarbeiten.
Da .NET Aspire deutlich mehr Funktionalität als Docker Compose mitbringt, ist es empfehlenswert, bei neuen Projekten auf Aspire anstatt Docker Compose zu setzen. Für bestehende Projekte kann sich eine Migration von Docker Compose auszahlen, da diese oft recht schnell und einfach durchgeführt werden kann. Das größte Hindernis für eine Migration ist, dass Aspire externe Komponenten via NuGet-Paket einbindet. Sollte es noch kein NuGet-Paket für das gewünschte Tool geben, kann dieses derzeit nicht mit Aspire konfiguriert werden.
Um eine Migration von Docker Compose zu .NET Aspire zu demonstrieren, wird die zuvor erstellte Anwendung verwendet und ein fiktives Beispiel erstellt, in dem diese Anwendung neben Frontend und Backend auch PostgreSQL als Datenbank, Redis als Cache und RabbitMQ als Queue verwendet. Alle externen Komponenten werden in diesem fiktiven Beispiel in Docker Compose konfiguriert und an die Container der Blazor-Anwendung angehängt.
Wie bereits zuvor beschrieben kann einem bestehenden Projekt .NET Aspire (mit einem Rechtsklick auf das Webprojekt; dann mit Add | .NET Aspire Orchestrator Support) hinzugefügt werden. Das erstellt zwei neue Projekte: Projektname.AppHost und Projektname.ServiceDefaults. In der Program.cs-Datei des AppHost-Projekts wird Aspire konfiguriert. Diese Datei sollte in etwa wie in Listing 4 aussehen. Nun können die benötigten externen Komponenten über NuGet-Pakete installiert werden. In Visual Studio kann dazu mit der rechten Maustaste auf das AppHost-Projekt geklickt werden und unter Add | .NET Aspire package ausgewählt werden. Alternativ kann auch das NuGet-Menü geöffnet und dort nach Aspire gesucht werden. Abbildung 14 zeigt, dass es bereits eine Vielzahl NuGet-Pakete für .NET Aspire gibt und diese die unterschiedlichsten Technologien unterstützen. So gibt es zum Beispiel NuGet-Pakete für SQL Server, Azure Key Vault, MongoDB und auch für AWS.
Abb. 14: Aspire bietet eine Vielzahl von NuGet-Paketen an
Für diese Demo werden die Redis, PostgreSQL und RabbitMQ Pakete installiert. Anschließend müssen diese noch in der Program.cs-Datei im AppHost-Projekt konfiguriert werden.
var builder = DistributedApplication.CreateBuilder(args); var cache = builder.AddRedis("cache"); var postgres = builder.AddPostgres("database") .WithDataVolume() .WithPgAdmin(); var rabbitMq = builder.AddRabbitMQ("rabbitMq") .WithManagementPlugin(); var apiService = builder.AddProject<Projects.BlazorWithAspireForAzure_ApiService>("apiservice") .WithReference(postgres) .WithReference(rabbitMq); builder.AddProject<Projects.BlazorWithAspireForAzure_Web>("webfrontend") .WithExternalHttpEndpoints() .WithReference(cache) .WaitFor(cache) .WithReference(apiService) .WithReference(postgres) .WithReference(rabbitMq) .WaitFor(apiService); builder.Build().Run();
Listing 5 zeigt die vollständige Program.cs-Datei, in der Frontend und API konfiguriert sind, sowie Redis, PostgreSQL und RabbitMQ hinzugefügt wurden. Redis wird mit der Methode AddRedis hinzugefügt und in einer Variable gespeichert. Diese Variable wird im Frontend referenziert, damit das Frontend auf Redis zugreifen kann; zusätzlich wartet das Frontend, bis Redis verfügbar ist.
ZUM NEWSLETTER
Regelmäßig News zur Konferenz und der .NET-Community
Als nächstes wird PostgreSQL mit der Methode AddPostgres erstellt. Diese Methode verwendet die Erweiterungen WithDataVolume() und WithPgAdmin(). Mit dem Volume kann die Datenbank persistiert werden und bleibt dadurch beim Neustart der Container bestehen. Die PgAdmin-Methode konfiguriert das Management-Tool pgAdmin, damit die PostgreSQL-Datenbank mit einem UI verwaltet werden kann.
Zuletzt wird RabbitMQ mit der Methode AddRabbitMQ hinzugefügt und im Frontend sowie im API referenziert. Damit können beide Anwendungen auf RabbitMQ zugreifen und Nachrichten verschicken und empfangen. RabbitMQ bietet auch die Erweiterungsmethode, WithManagementPlugin(), wodurch automatisch das Management-UI von RabbitMQ konfiguriert wird und auch im Aspire-Dashboard angezeigt wird.
Wenn die AppHost-Anwendung jetzt gestartet wird, sollten nach dem Herunterladen der Container alle Anwendungen laufen. Abbildung 15 zeigt das Aspire-Dashboard mit dem Frontend und dem API sowie allen zuvor konfigurierten Tools inklusiver der Management-UIs.
Abb. 15: Das Aspire-Dashboard mit allen konfigurierten Tools
Das RabbitMQ-Management-UI fehlt in dieser Auflistung, weil es ein Teil von RabbitMQ ist und daher unter dem URL für RabbitMQ aufgerufen werden kann. Beim ersten Aufruf des Management-UIs werden Benutzername und Passwort benötigt. Diese können aus den Umgebungsvariablen des RabbitMQ-Containers ausgelesen werden. Es ist generell zu empfehlen, sich durch die Umgebungsvariablen der verschiedenen Anwendungen zu klicken, um einen besseren Einblick in die Konfiguration zu bekommen.
.NET Aspire kann aber nicht nur durch NuGet-Pakete erweitert werden, es kann auch durch die Implementierung der Aspire-Features erweitert werden. Die Konfiguration dazu befindet sich im ServiceDefaults-Projekt. Dort kann zum Beispiel das Logging oder das Tracing um weitere Datenquellen erweitert werden. In .NET wird mit RabbitMQ oft MassTransit [5] als Tracing-Tool verwendet. Dieses kann einfach als neue Quelle im Tracing hinzugefügt werden, wodurch alle MassTransit-Traces ebenfalls im Aspire-Dashboard angezeigt werden können. Die Implementierungsdetails dazu würden hier allerdings den Rahmen sprengen.
Fazit
.NET Aspire ist ein neues Tool von Microsoft, das Entwickler:innen bei der Entwicklung und Fehlersuche in komplexen Systemen unterstützen soll. Dazu bringt es viele nützliche Features wie Logging, Tracing und ein Dashboard mit, das ohne Konfiguration der Entwickler:innen funktionieren. Bei Bedarf kann das Verhalten dieser Tools aber auch an die eigenen Anforderungen angepasst werden. Zusätzlich kann gemeinsam mit dem Azure Developer CLI ein Aspire-Projekt mit wenigen Befehlen in die Cloud deployt werden.
Obwohl .NET Aspire erst vor einigen Monaten erschienen ist, ist es bereits so umfangreich, dass dieser Artikel nur einen kleinen Einblick in den vollen Funktionsumfang geben konnte. Die Zukunft sieht ebenfalls positiv aus, da laufend neue Features hinzukommen und Microsoft seinen Fokus auf das Projekt gelegt hat.
Links & Literatur
[1] https://devblogs.microsoft.com/dotnet/introducing-project-tye/
[2] https://github.com/dotnet/aspire-samples
[3] https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd
[4] https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep
[5] https://learn.microsoft.com/en-us/dotnet/core/docker/publish-as-container