GoSuda

Futtassunk Go szervereket skálázhatóan a .NET Aspire keretrendszerben

By snowmerak
views ...

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.