Udforsk kørsel af Go-servere skalerbart i dotnet Aspire
dotnet aspire?
dotnet aspire er et værktøj, der er udviklet til at hjælpe udviklere med cloud-native udvikling og konfiguration, efterhånden som cloud-native miljøer bliver mere udbredte. Dette værktøj gør det muligt for .NET-udviklere nemt at implementere .NET-projekter og forskellige cloud-native infrastrukturer samt tjenester eller containere på andre sprog.
Det er naturligt, at med udgivelsen og driften af alt fra Docker til k8s, er et betydeligt antal brancher, industrier og udviklere skiftet til, eller er allerede skiftet til, cloud-native miljøer fra traditionelle on-premise miljøer. Det er nu et modent område. Derfor mener jeg, at det ikke er nødvendigt at forklare de tidligere ulemper vedrørende værtsnavne, portkonfiguration, firewalls og metrikstyring.
På baggrund af ovenstående forklaringer er det sandsynligvis stadig uklart, hvad dotnet aspire er. Dette skyldes, at Microsoft heller ikke har givet en præcis definition. Jeg vil derfor heller ikke give en særskilt definition. Dog vil jeg i denne artikel bruge de grundlæggende funktioner i dotnet aspire, som jeg har forstået dem, så du kan bruge det som reference til at definere din egen forståelse.
Projektkonfiguration
Oprettelse af et dotnet aspire-projekt
Hvis du ikke har en dotnet aspire-skabelon, skal du først installere den. Installer skabelonen med følgende kommando. Hvis du ikke har .NET, skal du selv installere det.
1dotnet new install Aspire.ProjectTemplates
Opret derefter en ny løsning i en passende mappe.
1dotnet new sln
Derefter skal du i løsningsmappen udføre følgende kommando for at oprette et projekt ud fra aspire-apphost-skabelonen.
1dotnet new aspire-apphost -o AppHost
Dette vil oprette et aspire-apphost-projekt, der kun indeholder simpel kode til opsætning.
Tilføjelse af Valkey
Lad os nu tilføje Valkey.
Før vi tilføjer det, skal det nævnes, at dotnet aspire tilbyder forskellige tredjepartsløsninger via community hosting. Valkey kan naturligvis også understøttes af denne community hosting, og det kan nemt bruges via følgende nuget-pakke.
1dotnet add package Aspire.Hosting.Valkey
Der tilbydes også forskellige integrerede hosting-muligheder, som kan findes her.
Tilbage til Valkey, åbn Program.cs-filen i AppHost-projektet og rediger den som følger.
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 er en implementering af IResourceBuilder-interfacet, som indeholder information til at bygge Valkey-tjenesten.WithDataVolume opretter et volumen til lagring af cache-data, og WithPersistence muliggør permanent lagring af cache-data.
Indtil videre ser det ud til at have en lignende funktion som volumes i docker-compose.
Det er naturligvis noget, I nemt kan oprette.
Men da dette overskrider omfanget af denne artikel, vil jeg ikke diskutere det nu.
Oprettelse af en Go-sprog Echo-server
Lad os nu tilføje en simpel Go-sprogserver.
Først opretter vi et workspace i løsningsmappen via go work init.
For .NET-udviklere kan et Go-workspace betragtes som værende lig med en løsning.
Opret derefter en mappe kaldet EchoServer, flyt ind i den, og kør go mod init EchoServer.
Denne kommando opretter et Go-modul. For .NET-udviklere kan et modul opfattes som værende lig med et projekt.
Opret derefter filen main.go og skriv følgende kode:
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}
Denne server lytter efter PORT-miljøvariablen, som injiceres, når Aspire AppHost kører, og starter serveren på den angivne port.
Det er en simpel server, der modtager en name-forespørgsel og returnerer Hello, {name}.
Lad os nu tilføje denne server til dotnet aspire.
Tilføjelse af Echo-serveren til Aspire
Gå tilbage til Aspire AppHost-projektet, hvor Valkey blev tilføjet, og tilføj community hosting for Go-sproget.
1dotnet add package CommunityToolkit.Aspire.Hosting.Golang
Åbn derefter filen Program.cs og tilføj følgende sætninger.
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();
Her er echoServer en implementering af IResourceBuilder-interfacet, der indeholder information til at bygge Go-sprogserveren.
Den nyligt tilføjede AddGolangApp-metode er en udvidelsesmetode for en brugerdefineret host til at tilføje en Go-sprogserver.
Du kan se, at den bruger port 3000 konstant og injicerer PORT-miljøvariablen.
Endelig gør WithExternalHttpEndpoints den tilgængelig udefra.
For at teste det kan du besøge http://localhost:3000/?name=world, og du vil se "Hello, world" blive vist.
Men i øjeblikket har dotnet aspire en stor straf for non-dotnet-projekter. Og det er...
Projektudvidelse
Hvordan foretager man så horisontal skalering?
I øjeblikket tilbyder dotnet aspire kun WithReplica-indstillingen til buildere for .NET-projekter, der er tilføjet via AddProject-metoden.
Men for Go-sprog-hoste eller eksterne projekter som AddContainer tilbydes denne indstilling ikke.
Derfor skal man selv implementere det ved hjælp af en separat load balancer eller reverse proxy.
Men dette kan gøre reverse proxyen til et SPOF (Single Point of Failure), så det er bedst, at reverse proxyen tilbyder WithReplica-indstillingen.
I så fald skal reverse proxyen uundgåeligt være et .NET-projekt.
Hidtil har jeg brugt metoder som Nginx, Trafik og egen implementering til at løse dette problem, men med begrænsningen af et .NET-projekt havde jeg ingen umiddelbar løsning. Derfor ledte jeg efter en reverse proxy implementeret i .NET og fandt heldigvis YARP som et valg. YARP er en reverse proxy implementeret i .NET, der også kan fungere som en load balancer og tilbyder forskellige funktioner, hvilket gjorde den til et godt valg.
Lad os nu tilføje YARP.
Konfiguration af reverse proxy med YARP
Først oprettes et projekt til brug af YARP.
1dotnet new web -n ReverseProxy
Gå derefter til projektet og installer YARP.
1dotnet add package Yarp.ReverseProxy --version 2.2.0
Når installationen er færdig, åbner du filen Program.cs og skriver følgende kode:
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"}");
Denne kode er grundlæggende for brugen af YARP.routes indeholder ruteinformation, som reverse proxyen vil bruge, og clusters indeholder klyngeinformation, som reverse proxyen vil bruge.
Disse oplysninger indlæses i reverse proxyen med LoadFromMemory-metoden.
Til sidst bruges MapReverseProxy-metoden til at mappe og køre reverse proxyen.
For at bruge det i praksis skal du tilføje reverse proxy-projektet som en reference i aspire apphost-projektet og tilføje og ændre følgende sætninger i Program.cs-filen.
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();
Nu kan reverse proxyen henvise til echo-serveren. Anmodninger, der kommer udefra, modtages nu af reverse proxyen og videresendes til echo-serveren.
Redigering af Reverse Proxy
Først skal lytteadressen for det projekt, der er tildelt reverse proxyen, ændres.
Fjern applicationUrl inde i Properties/launchSettings.json-filen.
Åbn derefter filen Program.cs og rediger den omfattende som vist nedenfor.
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"}");
Først ændres informationen om routes og clusters.echo-route og echo-cluster tilføjes for at sende anmodninger til echo-serveren.
Derefter ændres echo-serverens adresse til at blive læst fra miljøvariablerne.
Reglen for denne adresse er services__{service-name}__http__{index}.
For echo-serveren er servicenavnet echo-server, og da det er en enkelt instans, bruges indekset 0.
Hvis man tilføjer en ASP.NET Core-server, kan flere instanser oprettes via WithReplica, så indekset kan øges.
Den undtagelseshåndterede http://localhost:8080 er en meningsløs skrammelværdi.
Kør nu projektet, og hvis du besøger http://localhost:3000/?name=world, vil du stadig se "Hello, world" blive vist.
Udvidelsesideer
Nu har vi tilføjet en Go-server til dotnet aspire og bekræftet, at anmodninger videresendes via en reverse proxy. Så nu kan vi udvide denne proces til at blive implementeret programmatisk. For eksempel kan vi oprette flere instanser af echo-serveren ved at tilføje nummerering efter servicenavnet og automatisk tilføje konfigurationer til reverse proxyen.
Rediger koden, der bruger reverse proxyen og echo-serveren i aspire apphost-projektets Program.cs-fil, som følger:
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}
Og rediger derefter Program.cs-filen i reverse proxy-projektet som følger:
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};
Destinationsindstillinger tilføjes for de 8 forøgede echo-serverinstanser.
Nu har reverse proxyen destinationsoplysninger for de forøgede echo-servere og kan videresende anmodninger.
Hvis du besøger den eksisterende http://localhost:3000/?name=world, vil du stadig se "Hello, world" blive vist.
Afslutning
I denne artikel har jeg beskrevet processen med at tilføje en Go-server til dotnet aspire og videresende anmodninger via en reverse proxy. Dog er udvidelsen endnu ikke fuldt ud skrevet, og et eksempel, der kan implementeres mere programmatisk via miljøvariabler, er blevet skrevet i et separat repository. For detaljeret projektkonfiguration og kode henvises til snowmerak/AspireStartPack.
Jeg forventer personligt, at dotnet aspire kan udfylde sin egen rolle som et alternativ til Docker Compose og som et værktøj til cloud-implementering. Der findes allerede en generator til at generere Docker Compose- eller k8s-manifests, og jeg tror, at den har gjort infrastrukturelle værktøjer mere tilgængelige for almindelige udviklere.