Spustenie Go servera s rozšíriteľnosťou v dotnet aspire
Čo je dotnet Aspire?
dotnet Aspire je nástroj vytvorený na pomoc vývojárom pri vývoji a konfigurácii cloud-native aplikácií, keďže prostredí cloud-native pribúda. Tento nástroj umožňuje vývojárom .NET jednoducho nasadzovať projekty .NET, rôzne cloud-native infraštruktúry a služby alebo kontajnery napísané v iných jazykoch.
Je samozrejmé, že od uvedenia a prevádzky Dockeru až po k8s sa značný počet oblastí, priemyselných odvetví a vývojárov presúva z tradičných on-premise prostredí do cloud-native prostredia, alebo sa už presunuli. Je to už zrelá oblasť. Preto si myslím, že nie je potrebné vysvetľovať predchádzajúce nepríjemnosti týkajúce sa názvov hostiteľov, konfigurácie portov, firewallov alebo správy metrík.
Preto, aj keď sa pozriete na vyššie uvedené vysvetlenia, pravdepodobne nebudete mať jasnú predstavu o tom, čo je dotnet Aspire. Dôvodom je, že ani Microsoft preň neposkytuje presnú definíciu. Preto ani ja nebudem poskytovať žiadnu konkrétnu definíciu. Avšak v tomto článku použijem základné funkcie dotnet Aspire, ako ich chápem, takže si na základe toho môžete určiť vlastnú pozíciu.
Štruktúra projektu
Vytvorenie projektu dotnet Aspire
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 nainštalovaný .NET, nainštalujte si ho 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 spustite nasledujúci príkaz na vytvorenie projektu zo šablóny aspire-apphost.
1dotnet new aspire-apphost -o AppHost
Tým sa vytvorí projekt aspire-apphost, ktorý obsahuje jednoduchý kód pre nastavenie.
Pridanie Valkey
Teraz skúsime jednoducho pridať Valkey.
Predtým, ako ho pridáme, dotnet Aspire poskytuje rôzne riešenia tretích strán prostredníctvom community hosting. Samozrejme, Valkey môže tiež využívať podporu komunitného hostingu a možno ho ľahko použiť prostredníctvom nasledujúceho balíka NuGet.
1dotnet add package Aspire.Hosting.Valkey
Okrem toho poskytuje rôzne integrované hostingové služby, ktoré nájdete tu. Vráťme sa 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á obsahuje informácie na zostavenie služby Valkey.WithDataVolume vytvára zväzok na ukladanie dát do cache a WithPersistence umožňuje trvalé ukladanie dát do cache.
Zatiaľ sa zdá, že má podobnú funkciu ako volumes v docker-compose.
Samozrejme, môžete to ľahko vytvoriť aj sami.
To však presahuje rozsah tohto článku, takže o tom teraz nebudeme hovoriť.
Vytvorenie Go Echo Servera
Takže teraz pridáme jednoduchý server v jazyku Go.
Najprv vytvoríme workspace pomocou go work init v priečinku riešenia.
Pre vývojárov .NET je Go workspace podobný riešeniu.
Potom vytvorte priečinok s názvom EchoServer, prejdite doň a spustite go mod init EchoServer.
Tento príkaz vytvorí Go modul. Modul si pre vývojárov .NET môžete predstaviť ako projekt.
Potom vytvorte súbor main.go a napíšte doň nasledujúci 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 po spustení Aspire AppHost číta premennú prostredia PORT, ktorá mu je vstreknutá a na ktorej má počúvať, a spustí server.
Je to jednoduchý server, ktorý prijíma query parameter name a vracia Hello, {name}.
Teraz pridáme tento server do dotnet Aspire.
Pridanie Echo Servera do Aspire
Vráťte sa k projektu Aspire AppHost, kde ste pridali Valkey, a pridajte komunitný hosting pre jazyk Go.
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();
Tu echoServer je implementácia rozhrania IResourceBuilder, ktorá obsahuje informácie na zostavenie servera v jazyku Go.
Práve pridaná metóda AddGolangApp je metóda rozšírenia vlastného hostiteľa na pridanie servera v jazyku Go.
Môžete vidieť, že používa pevný port 3000 a vkladá premennú prostredia PORT.
Nakoniec WithExternalHttpEndpoints umožňuje externý prístup.
Pre účely testovania, ak navštívite http://localhost:3000/?name=world, mali by ste vidieť "Hello, world".
Avšak, v súčasnosti má dotnet Aspire značný "penaltu" pre non-dotnet projekty. A to je...
Rozšírenie projektu
Ako teda vykonať horizontálne škálovanie?
V súčasnosti dotnet Aspire poskytuje možnosť WithReplica iba pre buildery .NET projektov pridaných pomocou metódy AddProject.
Avšak pre hostiteľov v jazyku Go alebo externé projekty ako AddContainer túto možnosť neposkytuje.
Preto je potrebné implementovať ho priamo pomocou samostatného load balancera alebo reverse proxy.
Ak by však tento reverse proxy mohol byť SPOF, bolo by lepšie, keby reverse proxy poskytoval možnosť WithReplica.
To by nevyhnutne znamenalo, že reverse proxy musí byť .NET projekt.
Doteraz sa na riešenie tohto problému používali metódy ako Nginx, Traefik alebo vlastná implementácia, ale s obmedzením na .NET projekt som nemal k dispozícii žiadne okamžité riešenia. Preto som hľadal reverse proxy implementovaný v .NET a našťastie existovala možnosť YARP. YARP je reverse proxy implementovaný v .NET, ktorý môže fungovať aj ako load balancer a poskytuje rôzne funkcie, takže sa ukázal ako dobrá voľba.
Teraz pridáme YARP.
Konfigurácia reverse proxy s 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 nasledujúci 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 na použitie YARP.routes obsahuje informácie o smerovaní, ktoré bude používať reverse proxy, a clusters obsahuje informácie o klastroch, ktoré bude používať reverse proxy.
Tieto informácie sa načítajú do reverse proxy pomocou metódy LoadFromMemory.
Nakoniec sa reverse proxy mapuje a spúšťa pomocou metódy MapReverseProxy.
A pre praktické použitie pridajte odkaz na projekt reverse 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 reverse proxy odkazovať na echo server. Externé požiadavky sú teraz prijímané reverse proxy a preposielané na echo server.
Úprava reverzného proxy
Najprv je potrebné zmeniť počúvaciu adresu projektu priradeného k reverznému proxy.
Odstráňte applicationUrl zo súboru Properties/launchSettings.json.
Potom otvorte súbor Program.cs a rozsiahlo ho upravte nasledovne:
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 upravte informácie o routes a clusters.
Pridajte echo-route a echo-cluster na presmerovanie požiadaviek na echo server.
Potom upravte kód tak, aby adresa echo servera bola načítaná 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 keďže ide o jednu inštanciu, použije sa index 0.
Ak by ste pridali server asp .net core, pomocou WithReplica by sa mohlo vytvoriť viacero inštancií, takže by ste mohli index zvyšovať.
Výnimka http://localhost:8080 je len nejaká bezvýznamná hodnota.
Teraz spustite projekt a ak navštívite http://localhost:3000/?name=world, mali by ste stále vidieť "Hello, world".
Nápad na rozšírenie
Teraz sme videli, ako pridať Go server do dotnet Aspire a ako preposielať požiadavky cez reverse proxy. Teraz by sme tento proces mohli rozšíriť tak, aby sa dal implementovať programovo. Napríklad, pre echo server by sme mohli generovať viacero inštancií pridaním čísla za názov služby a automaticky pridávať konfiguráciu pre reverse proxy.
V súbore Program.cs projektu aspire apphost upravte kód používajúci reverse 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 súbor Program.cs projektu reverse proxy upravte 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 konfiguráciu cieľových adries pre 8 rozšírených inštancií echo servera.
Teraz má reverse proxy informácie o cieľových adresách pre rozšírené echo servery a môže preposielať požiadavky.
Ak navštívite pôvodnú adresu http://localhost:3000/?name=world, mali by ste stále vidieť "Hello, world".
Záver
V tomto článku sme opísali proces pridania Go servera do dotnet Aspire a preposielania požiadaviek cez reverse proxy. Rozšírenie však ešte nebolo úplne dokončené a príklad, ktorý sa dá programovo implementovať pomocou premenných prostredia, bol napísaný v samostatnom repozitári. Podrobnú štruktúru projektu a kód nájdete na snowmerak/AspireStartPack.
Osobne očakávam, že dotnet Aspire dokáže plniť svoju úlohu ako alternatíva k Docker Compose a ako nástroj na nasadzovanie do cloudu. Už existuje generátor, ktorý vytvára Docker Compose alebo k8s manifesty, čo podľa mňa zlepšilo prístupnosť infraštruktúrnych nástrojov pre bežných vývojárov.