Engineering

Dotnet Aspire - Erste Konfiguration für ein Bestandsprojekt

Wir verfolgen das Ziel, ein Projekt auf einer Entwicklungsmaschine in 60 Sekunden zum Laufen zu bekommen. Dotnet Aspire ist unser Tool, um diesem Ziel näherzukommen. Hier machen wir die erste Konfiguration für ein Bestandsprodukt.

Ausgangslage

Das erste Produkt, in dem wir Aspire testen möchten, ist ein Produkt, das wir seit einiger Zeit immer wieder weiterentwickeln. Die Codebasis entstand mit .NET 5. Damit können wir auch testen, ob sich Aspire einfach in ältere Codebasen einbetten lässt.

Das Projekt basiert auf einer ASP.NET Core Webapplikation (Razor Pages), die auch ein paar wenige Typescript basierte Webkomponenten hat. Daten werden in einer Postgres-Datenbank gespeichert. E-Mails werden über ein SMTP-Service übermittelt und ein Open Telemetry Agent filtert unwichtige Telemetry aus, bevor sie an unseren zentrales Telemetry Store weitergeleitet werden.

Eine monolithische Applikation mit einer server-gerenderten Web Applikation, einer Postgres-Datenbank, einem Mail-Service und einem Open Telemetry Agent

Architektur der Anwendung

Bisher haben wir zur Verwaltung ein kleines Tool namens batect verwendet, das Abhängigkeiten in Form von Containern aus einer Container-Registry aktualisiert, in ein gemeinsames Netzwerk eingehängt und abhängig von Tasks gestartet hat. Es hatte gute Dienste geleistet, aber leider wurde die Entwicklung eingestellt.

Dotnet Aspire Projekt

Ein Aspire Projekt ist ein Dotnet-Projekt, welches einen DistributedApplicationBuilder verwendet, um die Konfiguration zu erstellen.

Als ersten Schritt kann dazu einfach eine C#-Console Application als Projekt in die Solution eingefügt werden. In einer neuen Solution legt das Template von Microsoft das Projekt als <SolutionName>.AppHost an. Diese Konvention habe ich daher übernommen.

Das neue Projekt muss das Package Aspire.Hosting.AppHost und deine Webapplikationen referenzieren.

<ItemGroup>
    <PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.2"/>
</ItemGroup>

<ItemGroup>
    <ProjectReference Include="..\Web\Web.csproj"/>
</ItemGroup>

Die Referenz auf das Webprojekt erzeugt über einen Source Generator in Aspire eine Klasse, die dem Namen des referenzierten Projektes entspricht.

Die erste Konfiguration kann dann für ein referenziertes Projekt Web so aussehen:

using Microsoft.Extensions.Configuration;
using Projects;

var builder = DistributedApplication.CreateBuilder(args);

builder.AddProject<Web>("Web");

builder.Build().Run();

Füge noch eine launchsettings.json in das Projekt hinzu.

Wenn das AppHost-Projekt gestartet wird, bekommst du Folgendes:

  • Aspire-Dashboard, welches alle deine gestarteten Services anzeigt
  • Für Web-Projekte, die in ihrem launchsettings.json richtig konfiguriert sind, ein Link zur laufenden Webapplikation
  • Logs der gestarteten Ressourcen
  • Traces (wenn Open Telemetry in eurem Projekt konfiguriert ist)

Fehlen noch unsere beiden Abhängigkeiten, die Postgres Datenbank und Mailhog, unser Test SMTP Server.

using Microsoft.Extensions.Configuration;
using Projects;

var builder = DistributedApplication.CreateBuilder(args);

var smtp = builder.AddContainer("smtp", "mailhog/mailhog", "latest")
        .WithEndpoint(name: "smtp-endpoint", port: 1025, targetPort: 1025) // Port unter dem der SMTP Server zu erreichen ist
        .WithHttpEndpoint(8025, 8025);                                     // Port für das Mailhog Web Interface

var databaseInstance = builder.AddPostgres("database")
        .WithOtlpExporter()
        .WithEnvironment("POSTGRES_DB", "Web");                            // Erzeugt die Datenbank mit dem Namen Web beim Startup des Containers
var database = databaseInstance.AddDatabase("DB", "Web");                  // Fügt die Datenbank mit dem Namen Web in die Aspire Konfiguration ein 

builder.AddProject<Web>("Web")
        .WithReference(database)
        .WithReference(smtp.GetEndpoint("smtp-endpoint"));

builder.Build().Run();

Im Beispiel ist zu sehen, dass Endpunkte als Referenz an das Webprojekt übergeben werden. Das ist Teil der Service-Discovery von Aspire, die wir uns im nächsten Teil ansehen werden.