GoSuda

Pokus o škálovateľné spustenie servera Go v prostredí .NET Aspire

By snowmerak
views ...

dotnet aspire?

dotnet aspire je nástroj vytvorený s cieľom pomôcť vývojárom s vývojom a konfiguráciou cloud-native aplikácií v prostredí, kde sa cloud-native prostredie neustále rozširuje. Tento nástroj umožňuje .NET vývojárom jednoducho nasadzovať .NET projekty a rôznu cloud-native infraštruktúru, ako aj služby alebo kontajnery v iných jazykoch.

Samozrejme, od dockeru po k8s, existuje rozsiahle nasadenie a prevádzka, a značný počet oblastí, priemyselných odvetví a vývojárov prechádza, alebo už prešlo, z existujúcich on-premise prostredí do cloud-native prostredí. Teraz je to už vyspelá oblasť. Preto si myslím, že nie je potrebné popisovať existujúce problémy s názvami hostiteľov, konfiguráciou portov, firewallmi, správou metrík atď.

Takže, ak vezmeme do úvahy vyššie uvedené vysvetlenia, pravdepodobne nebudete mať ani tušenie, čo je dotnet aspire. Je to preto, že ani Microsoft ho presne nedefinuje. Takže ani ja sa nepokúsim ho definovať. Avšak v tomto článku budem používať základné funkcie dotnet aspire, ako ich chápem ja, takže by ste si mali na základe toho určiť svoju vlastnú pozíciu.

Konfigurácia projektu

Vytvorenie dotnet aspire projektu

Ak nemáte šablónu dotnet aspire, musíte ju najprv nainštalovať. Šablónu nainštalujte pomocou nasledujúceho príkazu. Ak nemáte .net, musíte si ho nainštalovať sami.

1dotnet new install Aspire.ProjectTemplates

Potom vytvorte nové riešenie vo vhodnom priečinku.

1dotnet new sln

Následne v priečinku riešenia vytvorte projekt so šablónou aspire-apphost pomocou nasledujúceho príkazu.

1dotnet new aspire-apphost -o AppHost

Tým sa vytvorí projekt aspire-apphost, ktorý obsahuje iba jednoduchý kód pre nastavenie.

Pridanie Valkey

Teraz jednoducho pridáme Valkey.

Pred pridaním treba poznamenať, že dotnet aspire poskytuje rôzne riešenia tretích strán prostredníctvom community hosting. Samozrejme, valkey môže využívať podporu tohto community hostingu a je ľahko dostupný prostredníctvom nasledujúceho nuget balíka.

1dotnet add package Aspire.Hosting.Valkey

Okrem toho existuje mnoho integrovaných hostingov, ktoré si môžete pozrieť tu. Vráťme sa späť k valkey a otvorte súbor Program.cs v projekte AppHost a upravte ho nasledovne.

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 implementácia rozhrania IResourceBuilder, ktoré má informácie na zostavenie služby valkey.WithDataVolume vytvára zväzok na ukladanie údajov vyrovnávacej pamäte a WithPersistence umožňuje trvalé ukladanie údajov vyrovnávacej pamäte. Zatiaľ to vyzerá, že robí niečo podobné ako volumes v docker-compose. Samozrejme, aj vy si to môžete ľahko vytvoriť. Ale to presahuje rámec tohto článku, takže to teraz nebudem rozoberať.

Vytvorenie Go jazykového echo servera

Teraz pridáme jednoduchý Go jazykový server. Najprv vytvorte pracovný priestor pomocou go work init v priečinku riešenia. Pre .NET vývojárov je Go pracovný priestor podobný riešeniu.

Potom vytvorte priečinok s názvom EchoServer a po presune doňho spustite go mod init EchoServer. Tento príkaz vygeneruje Go modul. Modul by mali .NET vývojári vnímať ako niečo podobné projektu. Potom vytvorte súbor main.go a napíšte doň nasledovné.

 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 načíta premennú prostredia PORT, ktorú Aspire AppHost vloží pri spustení, a spustí server na tomto porte. Je to jednoduchý server, ktorý prijíma name query a vracia Hello, {name}.

Teraz pridáme tento server do dotnet aspire.

Pridanie echo servera do aspire

Prejdite späť do projektu Aspire AppHost, kde ste pridali Valkey, a pridajte community hosting pre Go jazyk.

1dotnet add package CommunityToolkit.Aspire.Hosting.Golang

Potom otvorte súbor Program.cs a pridajte nasledujúci 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();

V tomto prípade je echoServer implementácia rozhrania IResourceBuilder, ktoré obsahuje informácie na zostavenie Go jazykového servera. Práve pridaná metóda AddGolangApp je metóda rozšírenia vlastného hostiteľa na pridanie Go jazykového servera. Je možné vidieť, že používa pevný port 3000 a vkladá premennú prostredia PORT. Nakoniec, WithExternalHttpEndpoints umožňuje prístup zvonku.

Ak otestujete prístup na http://localhost:3000/?name=world, mali by ste vidieť výpis Hello, world.

V súčasnosti však existuje ťažká penalizácia pre non-dotnet projekty v dotnet aspire. A to je...

Rozšírenie projektu

Ako je to s horizontálnym škálovaním?

V súčasnosti dotnet aspire poskytuje možnosť WithReplica iba pre buildery .NET projektov pridaných metódou AddProject. Neposkytuje však túto možnosť pre Go jazykové hosty alebo externé projekty ako AddContainer.

Preto musíte použiť samostatný load balancer alebo reverzné proxy a implementovať ho sami. V tomto prípade sa však môže stať, že dané reverzné proxy bude SPOF, preto je dobré, ak reverzné proxy poskytuje možnosť WithReplica. Ak je to tak, tak reverzné proxy musí byť nevyhnutne .NET projekt.

Doteraz som používal metódy ako nginx, trafik alebo vlastnú implementáciu, ale keďže existuje obmedzenie .NET projektu, nemal som hneď po ruke žiadne riešenie. Preto som hľadal reverzné proxy implementované v .NET a našťastie bola k dispozícii možnosť YARP. YARP je reverzné proxy implementované v .NET, ktoré môže tiež fungovať ako load balancer a ponúka rôzne funkcie, takže som usúdil, že je to dobrá voľba.

Teraz pridáme YARP.

Konfigurácia reverzného proxy pomocou YARP

Najprv vytvorte projekt na použitie YARP.

1dotnet new web -n ReverseProxy

Potom prejdite do projektu a nainštalujte YARP.

1dotnet add package Yarp.ReverseProxy --version 2.2.0

Po dokončení inštalácie otvorte súbor Program.cs a napíšte doň nasledovné.

 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 na použitie YARP.routes obsahuje informácie o smerovaní, ktoré bude reverzné proxy používať, a clusters obsahuje informácie o klastroch, ktoré bude reverzné proxy používať. Tieto informácie sa načítajú do reverzného proxy pomocou metódy LoadFromMemory. Nakoniec sa reverzné proxy namapuje a spustí pomocou metódy MapReverseProxy.

Pre reálne použitie pridajte referenciu na projekt reverzného proxy v projekte aspire apphost a pridajte a upravte nasledujúci kód v súbore 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();

Teraz môže reverzné proxy odkazovať na echo server. Štruktúra sa mení tak, že požiadavky prichádzajúce zvonku sú prijímané reverzným proxy a presmerované na echo server.

Úprava reverzného proxy

Najprv musíme zmeniť adresu počúvania projektu priradeného k reverznému proxy. Odstráňte applicationUrl v súbore Properties/launchSettings.json. Potom otvorte súbor Program.cs a rozsiahlo ho upravte takto.

 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"}");

Najprv upravíme informácie pre routes a clusters. Pridajte echo-route a echo-cluster pre odosielanie požiadaviek na echo server. Adresu echo servera upravíme tak, aby ju čítala z premennej prostredia.

Pravidlo pre túto adresu je services__{service-name}__http__{index}. V prípade echo servera je názov služby echo-server a pretože je to jedna inštancia, pre index použijeme 0. Ak pridávate asp .net core server, môžete pomocou WithReplica vytvoriť viacero inštancií, takže ich môžete použiť zvýšením indexu. Výnimkou je http://localhost:8080, čo je bezvýznamná hodnota.

Teraz spustite projekt a ak sa pripojíte na http://localhost:3000/?name=world, mali by ste stále vidieť výpis Hello, world.

Rozširujúce nápady

Teraz sme potvrdili pridanie Go servera do dotnet aspire a odoslanie požiadaviek cez reverzné proxy. Potom môžeme rozšíriť túto implementáciu, aby bola programovateľná. Napríklad môžeme vygenerovať viacero inštancií pre echo server pridaním číslovania za názov služby a automaticky pridať nastavenia pre reverzné proxy.

Upravte kód v súbore Program.cs projektu aspire apphost tak, aby používal reverzné proxy a echo server nasledovne.

 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 súbor Program.cs projektu reverzného proxy nasledovne.

 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};

Pridajte nastavenia cieľa pre 8 inštancií echo servera. Teraz má reverzné proxy informácie o cieľoch pre rozšírené echo servery a môže doručovať požiadavky. Ak sa pripojíte k http://localhost:3000/?name=world, mali by ste stále vidieť výpis Hello, world.

Záver

V tomto článku som vysvetlil proces pridávania Go servera do dotnet aspire a doručovania požiadaviek cez reverzné proxy. Rozšírenia však nie sú napísané všetky a mám samostatné repo s príkladom programovateľnejšej implementácie pomocou premenných prostredia. Podrobnejšiu konfiguráciu projektu a kód nájdete na stránke snowmerak/AspireStartPack.

Osobne očakávam, že dotnet aspire bude schopný zohrávať svoju vlastnú úlohu ako alternatíva k docker compose a ako nástroj na cloudové nasadenie. Už existuje generátor, ktorý generuje docker compose alebo k8s manifesty, takže si myslím, že prístupnosť infraštruktúrnych nástrojov sa pre bežných vývojárov zlepšila.