Futtassunk Go szervereket skálázhatóan a .NET Aspire keretrendszerben
Mi az a dotnet aspire?
A dotnet aspire egy olyan eszköz, amelyet a felhőalapú natív környezetek növekedésével párhuzamosan fejlesztettek ki, hogy segítsék a fejlesztőket a felhőalapú natív fejlesztésben és konfigurációban. Ez az eszköz lehetővé teszi a .NET fejlesztők számára, hogy könnyedén telepítsék a .NET projekteket, különböző felhőalapú natív infrastruktúrákat, valamint más nyelveken írt szolgáltatásokat vagy konténereket.
Nyilvánvaló, hogy a docker-től a k8s-ig terjedő kibocsátások és működések révén a korábbi on-premise környezetekből számos területen, iparágban és fejlesztőnél történt, illetve történik meg a felhőalapú natív környezetekre való áttérés. Ez mára egy kiforrott terület. Ezért feleslegesnek tartom magyarázni a korábbi kellemetlenségeket a hosztnevek, portkonfigurációk, tűzfalak, metrikakezelés stb. terén.
Ezért az előző magyarázatok alapján valószínűleg fogalma sincs arról, hogy mi a dotnet aspire. Ennek oka, hogy még a Microsoft sem ad pontos definíciót. Én sem fogok külön definíciót adni. Azonban ebben a cikkben a dotnet aspire általam értelmezett alapvető funkcióit fogom használni, így referenciaként szolgálhat a saját álláspontjának meghatározásához.
Projekt konfiguráció
dotnet aspire projekt létrehozása
Ha nincs dotnet aspire sablonja, először telepítenie kell a sablont. Telepítse a sablont a következő paranccsal. Ha nincs .net telepítve, azt Önnek kell telepítenie.
1dotnet new install Aspire.ProjectTemplates
Ezután hozzon létre egy új megoldást egy megfelelő mappában.
1dotnet new sln
Majd a megoldás mappájában futtassa a következő parancsot az aspire-apphost sablon projektjének létrehozásához.
1dotnet new aspire-apphost -o AppHost
Ekkor létrejön egy aspire-apphost projekt, amely csak a beállításhoz szükséges egyszerű kódot tartalmazza.
Valkey hozzáadása
Most egyszerűen hozzáadunk egy Valkey-t.
Mielőtt azonnal hozzáadnánk, a dotnet aspire a community hostingon keresztül számos harmadik féltől származó megoldást kínál. Természetesen a Valkey is élvezheti ezen közösségi hoszting támogatását, és könnyen használható a következő nuget csomaggal.
1dotnet add package Aspire.Hosting.Valkey
Számos más integrált hosztingot is kínál, melyek itt tekinthetők meg. Visszatérve a Valkey-hez, nyissa meg az AppHost projekt Program.cs fájlját, és módosítsa a következőképpen:
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();
A cache az IResourceBuilder interfész egy implementációja, amely információt tartalmaz a Valkey szolgáltatás összeállításához.
A WithDataVolume egy kötetet hoz létre a gyorsítótár adatok tárolására, a WithPersistence pedig biztosítja a gyorsítótár adatok folyamatos tárolását.
Eddig úgy tűnik, hogy a docker-compose volumes funkciójához hasonló szerepet tölt be.
Természetesen Ön is könnyedén létrehozhatja ezt.
Azonban ez túlmegy a cikk terjedelmén, ezért most nem tárgyaljuk.
Go nyelvi Echo szerver létrehozása
Most hozzáadunk egy egyszerű Go nyelvi szervert.
Először hozzon létre egy munkaterületet a megoldás mappájában a go work init paranccsal.
A .NET fejlesztők számára a Go munkaterület hasonló a megoldáshoz.
Ezután hozzon létre egy EchoServer nevű mappát, lépjen be oda, majd futtassa a go mod init EchoServer parancsot.
Ez a parancs Go modult hoz létre. A modul a .NET fejlesztők számára hasonló a projekthez.
Ezután hozzon létre egy main.go fájlt, és írja bele a következőket:
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}
Ez a szerver akkor fut, amikor az Aspire AppHost elindul, és a PORT környezeti változót injektálja, amire figyelnie kell, majd beolvassa ezt a portot és elindítja a szervert.
Ez egy egyszerű szerver, amely fogadja a name lekérdezési paramétert, és Hello, {name} választ ad vissza.
Most adjuk hozzá ezt a szervert a dotnet aspire-hoz.
Echo szerver hozzáadása az aspire-hoz
Lépjen vissza az Aspire AppHost projekthez, ahová a Valkey-t is hozzáadta, és adja hozzá a Go nyelvhez tartozó közösségi hosztingot.
1dotnet add package CommunityToolkit.Aspire.Hosting.Golang
Ezután nyissa meg a Program.cs fájlt, és adja hozzá a következő utasítást:
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();
Itt az echoServer az IResourceBuilder interfész egy implementációja, amely információt tartalmaz a Go nyelvi szerver összeállításához.
Az imént hozzáadott AddGolangApp metódus egy egyéni hoszt kiterjesztési metódusa a Go nyelvi szerver hozzáadásához.
Látható, hogy rögzített 3000-es portot használ, és injektálja a PORT környezeti változót.
Végül a WithExternalHttpEndpoints lehetővé teszi a külső hozzáférést.
A teszteléshez látogasson el a http://localhost:3000/?name=world címre, és látni fogja, hogy a Hello, world jelenik meg.
Azonban a jelenlegi dotnet aspire-ban súlyos büntetés vonatkozik a non-dotnet projektekre. Ez pedig...
Projekt kiterjesztés
Hogyan történik a horizontális skálázás?
Jelenleg a dotnet aspire csak a AddProject metódussal hozzáadott .NET projektek buildereihez biztosítja a WithReplica opciót.
Azonban a Go nyelvi hosztok vagy az AddContainer típusú külső projektek esetében ez az opció nem áll rendelkezésre.
Ezért külön terheléselosztót (load balancer) vagy fordított proxyt (reverse proxy) kell használni a közvetlen megvalósításhoz.
Azonban ezáltal a fordított proxy SPOF (Single Point of Failure) lehet, ezért a fordított proxynak célszerű WithReplica opciót biztosítania.
Ezért elkerülhetetlen, hogy a fordított proxy .NET projekt legyen.
Eddig olyan módszereket használtam erre a problémára, mint az nginx, a trafik, vagy a saját megvalósítás, de ha a .NET projekt korlátozása érvényesül, akkor azonnal nem volt megoldás a kezemben. Ezért kerestem egy .NET-ben megvalósított fordított proxyt, és szerencsére a YARP egy lehetőség volt. A YARP egy .NET-ben megvalósított fordított proxy, amely terheléselosztóként is funkcionálhat, és számos funkciót kínál, ezért jó választásnak ítéltem.
Most hozzáadjuk a YARP-ot.
Reverse proxy konfigurálása YARP-pal
Először hozzon létre egy projektet a YARP használatához.
1dotnet new web -n ReverseProxy
Ezután lépjen be a projektbe, és telepítse a YARP-ot.
1dotnet add package Yarp.ReverseProxy --version 2.2.0
A telepítés befejezése után nyissa meg a Program.cs fájlt, és írja be a következőket:
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"}");
Ez a kód a YARP használatához szükséges alapvető kód.
A routes tartalmazza a fordított proxy által használt útvonalinformációkat, a clusters pedig a fordított proxy által használt klaszterinformációkat.
Ezeket az információkat a LoadFromMemory metódus tölti be a fordított proxyba.
Végül a MapReverseProxy metódus segítségével a fordított proxy leképeződik és elindul.
Éles használathoz adja hozzá a reverse proxy projektet referenciaként az aspire apphost projekthez, majd adja hozzá és módosítsa a Program.cs fájlban a következő utasítást:
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();
Mostantól a reverse proxy hivatkozhat az echo szerverre. A külső kérések a reverse proxy-n keresztül érkeznek, és az echo szerverre továbbítódnak.
Reverse proxy módosítása
Először is módosítani kell a reverse proxyhoz rendelt projekt figyelési címét.
Távolítsa el az applicationUrl értéket a Properties/launchSettings.json fájlból.
Ezután nyissa meg a Program.cs fájlt, és módosítsa jelentősen az alábbiak szerint:
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"}");
Először is módosítjuk a routes és clusters információit.
Hozzáadjuk az echo-route-ot és az echo-cluster-t, és beállítjuk, hogy az echo szerverre küldjön kéréseket.
Ezenkívül módosítjuk az echo szerver címét, hogy azt környezeti változóból olvassa be.
Ennek a címnek a szabálya: services__{service-name}__http__{index}.
Az echo szerver esetében a szolgáltatás neve echo-server, és mivel egyetlen példányról van szó, az index 0.
Ha ASP.NET Core szervert adunk hozzá, és a WithReplica segítségével több példány is létrejöhet, akkor az indexet növelni kell.
A kivételkezelésben szereplő http://localhost:8080 egy értelmetlen, szemét érték.
Most indítsa el a projektet, és látogasson el a http://localhost:3000/?name=world címre, és továbbra is a Hello, world üzenetet fogja látni.
Bővítési ötletek
Most megerősítettük, hogy a dotnet aspire-hoz Go szervert adunk, és reverse proxy-n keresztül továbbítjuk a kéréseket. Ezt a folyamatot programozottan is meg lehet valósítani. Például, az echo szerver esetében a szolgáltatás neve után sorszámozást adhatunk hozzá több példány létrehozásához, és automatikusan hozzáadhatunk beállításokat a reverse proxyhoz.
Módosítsa a reverse proxy-t és az echo szervert használó kódot az aspire apphost projekt Program.cs fájljában a következőképpen:
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}
Ezután módosítsa a reverse proxy projekt Program.cs fájlját a következőképpen:
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};
Hozzáadjuk a célállomás beállításokat a 8 bővített echo szerver példányhoz.
Most a reverse proxy rendelkezik a bővített echo szerverek célállomás adataival, és képes továbbítani a kéréseket.
Ha a korábbi http://localhost:3000/?name=world címre lép, továbbra is a Hello, world üzenetet fogja látni.
Összefoglalás
Ez a cikk bemutatta a Go szerver dotnet aspire-hoz való hozzáadását, és a kérések reverse proxy-n keresztüli továbbításának folyamatát. Azonban a bővítésről még nem írtam meg mindent, és egy különálló repository-ban elkészítettem egy példát, amely környezeti változók segítségével programozottabban is megvalósítható. A részletes projektkonfigurációért és a kódért tekintse meg a snowmerak/AspireStartPack oldalt.
Személy szerint azt várom, hogy a dotnet aspire a docker compose alternatívájaként, és felhőalapú telepítőeszközként is betöltse a saját szerepét. Már létezik generátor, amely docker compose vagy k8s manifesteket generál, így úgy gondolom, hogy az átlagos fejlesztők számára javult az infrastruktúraeszközök elérhetősége.