GoSuda

Spuštění Go serveru rozšiřitelně v dotnet aspire

By snowmerak
views ...

Co je to dotnet aspire?

dotnet aspire je nástroj vytvořený k podpoře vývojářů při vývoji a konfiguraci cloud-native prostředí, jelikož se tato prostředí stále více rozšiřují. Tento nástroj umožňuje vývojářům .NET snadno nasazovat projekty .NET a různé cloud-native infrastruktury, stejně jako služby nebo kontejnery napsané v jiných jazycích.

Je zřejmé, že s uvedením a provozem dockeru a k8s mnoho oblastí, odvětví a vývojářů přechází z on-premise prostředí do cloud-native prostředí, nebo již přešlo. Je to nyní vyspělá oblast. Proto se domnívám, že není nutné vysvětlovat stávající nepříjemnosti spojené s názvy hostitelů, konfigurací portů, firewally a správou metrik.

Proto ani z výše uvedených vysvětlení pravděpodobně nebudete mít tušení, co dotnet aspire je. Důvodem je, že ani Microsoft nedefinuje jeho přesnou definici. Proto ani já nebudu poskytovat žádnou konkrétní definici. Nicméně, v tomto článku budu používat základní funkce dotnet aspire tak, jak jsem je pochopil, takže to můžete brát jako referenci a stanovit si vlastní postoj.

Konfigurace projektu

Vytvoření projektu dotnet aspire

Pokud nemáte šablonu dotnet aspire, musíte ji nejprve nainstalovat. Nainstalujte šablonu pomocí následujícího příkazu. Pokud nemáte .NET, nainstalujte si ho sami.

1dotnet new install Aspire.ProjectTemplates

Poté v příslušné složce vytvořte nové řešení.

1dotnet new sln

Následně ve složce řešení spusťte následující příkaz k vytvoření projektu ze šablony aspire-apphost.

1dotnet new aspire-apphost -o AppHost

Tím se vytvoří projekt aspire-apphost, který obsahuje pouze jednoduchý kód pro nastavení.

Přidání Valkey

Nyní zkusme přidat Valkey.

Než jej přidáme, dotnet aspire nabízí různá řešení třetích stran prostřednictvím community hosting. Valkey samozřejmě může využívat této komunitní podpory a lze jej snadno použít prostřednictvím následujícího balíčku nuget.

1dotnet add package Aspire.Hosting.Valkey

Kromě toho nabízí různé integrované hostování, které najdete zde. Vraťme se k Valkey a otevřete soubor Program.cs v projektu AppHost a upravte jej následovně.

1var builder = DistributedApplication.CreateBuilder(args);
2
3var cache = builder.AddValkey("cache")
4    .WithDataVolume(isReadOnly: false)
5    .WithPersistence(interval: TimeSpan.FromMinutes(5),
6        keysChangedThreshold: 100);
7
8builder.Build().Run();

cache je implementace rozhraní IResourceBuilder, která obsahuje informace pro sestavení služby valkey.WithDataVolume vytvoří svazek pro ukládání dat do mezipaměti a WithPersistence umožňuje trvalé ukládání dat do mezipaměti. Dosud se zdá, že to hraje podobnou roli jako volumes v docker-compose. Samozřejmě, že to můžete snadno vytvořit i vy. Ale to přesahuje rozsah tohoto článku, takže o tom teď nebudu mluvit.

Vytvoření Go jazykového Echo serveru

Nyní zkusme přidat jednoduchý server v jazyce Go. Nejprve vytvořte pracovní prostor pomocí go work init ve složce řešení. Pro vývojáře .NET je pracovní prostor Go podobný řešení.

Poté vytvořte složku s názvem EchoServer, přesuňte se do ní a spusťte go mod init EchoServer. Tento příkaz vytvoří Go modul. Modul si můžete představit jako něco podobného projektu pro vývojáře .NET. Poté vytvořte soubor main.go a napište následující kód.

 1package main
 2
 3import (
 4	"log"
 5	"net/http"
 6	"os"
 7)
 8
 9func main() {
10	addr := os.Getenv("PORT")
11	log.Printf("Server started on %s", addr)
12
13	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
14		name := request.URL.Query().Get("name")
15		writer.Write([]byte("Hello, " + name))
16	})
17
18	http.ListenAndServe(":"+addr, nil)
19}

Tento server se spustí, když Aspire AppHost injektuje proměnnou prostředí PORT, na které má naslouchat, a přečte tento port. Jedná se o jednoduchý server, který přijímá dotaz name a vrací Hello, {name}.

Nyní přidáme tento server do dotnet aspire.

Přidání Echo serveru do Aspire

Vraťte se k projektu Aspire AppHost, kam jste přidali Valkey, a přidejte komunitní hosting pro jazyk Go.

1dotnet add package CommunityToolkit.Aspire.Hosting.Golang

Poté otevřete soubor Program.cs a přidejte následující kód.

 1var builder = DistributedApplication.CreateBuilder(args);
 2
 3var cache = builder.AddValkey("cache")
 4    .WithDataVolume(isReadOnly: false)
 5    .WithPersistence(interval: TimeSpan.FromMinutes(5),
 6        keysChangedThreshold: 100);
 7
 8var echoServer = builder.AddGolangApp("echo-server", "../EchoServer")
 9    .WithHttpEndpoint(port: 3000, env: "PORT")
10    .WithExternalHttpEndpoints();
11
12builder.Build().Run();

Zde echoServer je implementace rozhraní IResourceBuilder, která obsahuje informace pro sestavení serveru v jazyce Go. Metoda AddGolangApp, kterou jsme právě přidali, je rozšiřující metoda vlastního hostitele pro přidání serveru v jazyce Go. Je vidět, že používá pevný port 3000 a injektuje proměnnou prostředí PORT. Nakonec WithExternalHttpEndpoints umožňuje externí přístup.

Pro testování můžete přistoupit na http://localhost:3000/?name=world a ověřit, že se zobrazí Hello, world.

Nicméně, v současné době dotnet aspire ukládá značné sankce non-dotnet projektům. A to je...

Rozšíření projektu

Jak tedy horizontálně škálovat?

V současné době dotnet aspire poskytuje možnost WithReplica pouze pro buildery .NET projektů přidaných metodou AddProject. Nicméně, tuto možnost nenabízí pro hostitele jazyka Go ani pro externí projekty, jako je AddContainer.

Proto je nutné implementovat to přímo pomocí samostatného load balanceru nebo reverse proxy. Nicméně, v takovém případě by se tato reverse proxy mohla stát SPOF, proto je vhodné, aby reverse proxy poskytovala možnost WithReplica. To pak nutně znamená, že reverse proxy musí být .NET projekt.

Doposud se k řešení tohoto problému používaly metody jako nginx, trafik nebo vlastní implementace, ale s omezením na .NET projekt jsem neměl po ruce žádné řešení. Proto jsem hledal reverse proxy implementovanou v .NET a naštěstí existovala možnost YARP. YARP je reverse proxy implementovaná v .NET, která může fungovat jako load balancer a nabízí různé funkce, proto jsem ji považoval za dobrou volbu.

Nyní přidáme YARP.

Konfigurace reverse proxy s YARP

Nejprve vytvořte projekt pro použití YARP.

1dotnet new web -n ReverseProxy

Poté přejděte do projektu a nainstalujte YARP.

1dotnet add package Yarp.ReverseProxy --version 2.2.0

Po dokončení instalace otevřete soubor Program.cs a napište následující kód.

 1using Yarp.ReverseProxy.Configuration;
 2
 3var builder = WebApplication.CreateBuilder(args);
 4
 5var routes = new List<RouteConfig>();
 6var clusters = new List<ClusterConfig>();
 7
 8builder.Services.AddReverseProxy()
 9    .LoadFromMemory(routes, clusters);
10
11var app = builder.Build();
12
13app.MapReverseProxy();
14app.Run(url: $"http://0.0.0.0:{Environment.GetEnvironmentVariable("PORT") ?? "5000"}");

Tento kód je základní kód pro použití YARP.routes obsahuje informace o trasách, které bude reverse proxy používat, a clusters obsahuje informace o clusterech, které bude reverse proxy používat. Tyto informace jsou načteny do reverse proxy pomocí metody LoadFromMemory. Nakonec se reverse proxy mapuje a spouští pomocí metody MapReverseProxy.

Pro praktické použití přidejte odkaz na projekt reverse proxy z projektu aspire apphost a přidejte a upravte následující kód do souboru Program.cs.

1dotnet add reference ../ReverseProxy
1var echoServer = builder.AddGolangApp("echo-server", "../EchoServer")
2    .WithHttpEndpoint(env: "PORT");
3
4var reverseProxy = builder.AddProject<Projects.ReverseProxy>("gateway")
5    .WithReference(echoServer)
6    .WithHttpEndpoint(port: 3000, env: "PORT", isProxied: true)
7    .WithExternalHttpEndpoints();

Nyní může reverse proxy odkazovat na echo server. Požadavky přicházející zvenčí jsou nyní přijímány reverse proxy a předávány echo serveru.

Úprava Reverse proxy

Nejprve je nutné změnit adresu naslouchání projektu přiděleného reverse proxy. Odstraňte applicationUrl uvnitř souboru Properties/launchSettings.json. Poté otevřete soubor Program.cs a proveďte následující rozsáhlé úpravy.

 1using Yarp.ReverseProxy.Configuration;
 2
 3var builder = WebApplication.CreateBuilder(args);
 4
 5var routes = new List<RouteConfig>
 6{
 7    new RouteConfig
 8    {
 9        ClusterId = "cluster-echo",
10        RouteId = "route-echo",
11        Match = new RouteMatch
12        {
13            Path = "/"
14        }
15    }
16};
17
18var echoServerAddr = Environment.GetEnvironmentVariable("services__echo-server__http__0") ?? "http://localhost:8080";
19
20var clusters = new List<ClusterConfig>
21{
22    new ClusterConfig
23    {
24        ClusterId = "cluster-echo",
25        Destinations = new Dictionary<string, DestinationConfig>
26        {
27            { "destination-echo", new DestinationConfig { Address = echoServerAddr } }
28        }
29    }
30};
31
32builder.Services.AddReverseProxy()
33    .LoadFromMemory(routes, clusters);
34
35var app = builder.Build();
36
37app.MapReverseProxy();
38app.Run(url: $"http://0.0.0.0:{Environment.GetEnvironmentVariable("PORT") ?? "5000"}");

Nejprve upravte informace o routes a clusters. Přidejte echo-route a echo-cluster pro každou z nich a nakonfigurujte je tak, aby odesílaly požadavky na echo server. Dále upravte adresu echo serveru tak, aby se četla z proměnné prostředí.

Pravidlo pro tuto adresu je services__{service-name}__http__{index}. V případě echo serveru je název služby echo-server a jelikož se jedná o jedinou instanci, používá se index 0. Pokud přidáte server asp .net core, lze pomocí WithReplica vytvořit více instancí, takže můžete index navyšovat. Zpracovaná výjimka http://localhost:8080 je bezvýznamná hodnota.

Nyní spusťte projekt a přistupte na http://localhost:3000/?name=world a měli byste vidět, že se stále zobrazuje Hello, world.

Myšlenky na rozšíření

Nyní jsme potvrdili, že můžeme přidat Go server do dotnet aspire a předávat požadavky přes reverse proxy. To znamená, že bychom mohli tento proces rozšířit tak, aby byl implementovatelný programově. Například, pro echo server bychom mohli přidat číslování za název služby pro vytvoření více instancí a automaticky přidat nastavení pro reverse proxy.

Upravte kód, který používá reverse proxy a echo server, v souboru Program.cs projektu aspire apphost následovně:

 1var reverseProxy = builder.AddProject<Projects.ReverseProxy>("gateway")
 2    .WithHttpEndpoint(port: 3000, env: "PORT", isProxied: true)
 3    .WithExternalHttpEndpoints();
 4
 5for (var i = 0; i < 8; i++)
 6{
 7    var echoServer = builder.AddGolangApp($"echo-server-{i}", "../EchoServer")
 8        .WithHttpEndpoint(env: "PORT");
 9    reverseProxy.WithReference(echoServer);
10}

A upravte soubor Program.cs projektu reverse proxy následovně:

 1var echoServerDestinations = new Dictionary<string, DestinationConfig>();
 2for (var i = 0; i < 8; i++)
 3{
 4    echoServerDestinations[$"destination-{i}"] = new DestinationConfig
 5    {
 6        Address = Environment.GetEnvironmentVariable($"services__echo-server-{i}__http__0") ?? "http://localhost:8080"
 7    };
 8}
 9
10var clusters = new List<ClusterConfig>
11{
12    new ClusterConfig
13    {
14        ClusterId = "cluster-echo",
15        Destinations = echoServerDestinations
16    }
17};

Přidávají se nastavení cíle pro 8 rozšířených instancí echo serveru. Nyní má reverse proxy informace o cílech pro rozšířené echo servery a může předávat požadavky. Pokud přistoupíte na původní http://localhost:3000/?name=world, měli byste stále vidět Hello, world.

Závěr

V tomto článku jsme popsali proces přidání Go serveru do dotnet aspire a předávání požadavků přes reverse proxy. Nicméně, co se týče rozšíření, není všechno ještě dokončeno a příklad, jak to implementovat programově pomocí proměnných prostředí, jsem připravil v samostatném repozitáři. Podrobnou konfiguraci projektu a kód naleznete v snowmerak/AspireStartPack.

Osobně očekávám, že dotnet aspire může hrát svou roli jako alternativa k docker compose a jako nástroj pro nasazení do cloudu. Již existuje generátor, který vytváří docker compose nebo k8s manifesty, a myslím, že to zlepšilo přístup běžných vývojářů k nástrojům infrastruktury.