Esecuzione di un server Go in modo estensibile in dotnet aspire
dotnet aspire?
dotnet aspire๋ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ํ๊ฒฝ์ด ๋์ด๋จ์ ๋ฐ๋ผ, ๊ฐ๋ฐ์๋ค์ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ๊ฐ๋ฐ ๋ฐ ๊ตฌ์ฑ์ ๋๊ธฐ ์ํด ๋ง๋ค์ด์ง ๋๊ตฌ์ ๋๋ค. ์ด ๋๊ตฌ๋ .NET ๊ฐ๋ฐ์๋ค์ด ๋ท๋ท ํ๋ก์ ํธ ๋ฐ ๋ค์ํ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์ธํ๋ผ, ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ธ์ด๋ก ๋ ์๋น์ค๋ ์ปจํ ์ด๋ ๋ฑ์ ์ฝ๊ฒ ๋ฐฐํฌํ ์ ์๊ฒ ํด์ค๋๋ค.
๋น์ฐํ docker์์ k8s๊น์ง ์ถ์ ๋ฐ ์ด์๋๋ฉฐ, ๊ธฐ์กด์ ์จํ๋ ๋ฏธ์ค ํ๊ฒฝ์์ ์๋นํ ๋ง์ ๋ถ์ผ, ์ฐ์ , ๊ฐ๋ฐ์๋ค์ด ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ํ๊ฒฝ์ผ๋ก ์ด์ ํ๊ณ ์๊ณ , ์ด์ ํ ์ํ์ ๋๋ค. ์ด์ ๋ ์ฑ์๋ ๋ถ์ผ์ฃ . ๊ทธ๋ ๊ธฐ์ ํธ์คํธ ์ด๋ฆ, ํฌํธ ๊ตฌ์ฑ, ๋ฐฉํ๋ฒฝ, ๋ฉํธ๋ฆญ ๊ด๋ฆฌ ๋ฑ์ ๋ํด ๊ธฐ์กด์ ๋ถํธํจ์ ๋ํด ์ค๋ช ํ ํ์๊ฐ ์์ ๊ฑฐ๋ผ ์๊ฐํฉ๋๋ค.
๊ทธ๋์ ๋น์ฅ ์์ ์ค๋ช ๋ค๋ก ๋ฏธ๋ฃจ์ด๋ด๋ dotnet aspire๊ฐ ๋ญ์ง ๋์ ํ ๊ฐ์ด ์ ์กํ ๊ฒ๋๋ค. ์๋๋ฉด, ์ด๊ฑด ๋ง์ดํฌ๋ก์ํํธ๋ ์ ํํ ์ ์๋ฅผ ๋ด๋ฆฌ์ง ์๊ณ ์์ต๋๋ค. ๊ทธ๋์ ์ ๋ ๋ณ๋ค๋ฅธ ์ ์๋ฅผ ๋ด๋ฆฌ์ง ์๊ฒ ์ต๋๋ค. ๋ค๋ง, ์ด ๊ธ์์ ์ ๊ฐ ์ดํดํ dotnet aspire์ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ๊ฒ์ด๋ฏ๋ก, ์ฐธ๊ณ ํ์ฌ ๋ณธ์ธ๋ง์ ์์น๋ฅผ ์ ํ์๋ฉด ๋ ๊ฒ๊ฐ์ต๋๋ค.
ํ๋ก์ ํธ ๊ตฌ์ฑ
dotnet aspire ํ๋ก์ ํธ ์์ฑ
๋ง์ฝ dotnet aspire ํ ํ๋ฆฟ์ด ์๋ค๋ฉด, ํ ํ๋ฆฟ๋ถํฐ ์ค์นํด์ผํฉ๋๋ค. ๋ค์ ๋ช ๋ น์ด๋ก ํ ํ๋ฆฟ์ ์ค์นํฉ๋๋ค. ๋ง์ฝ .net์ด ์๋ค๋ฉด, ๊ทธ๊ฑด ๋ณธ์ธ์ด ์ค์นํด์ฃผ์ธ์.
1dotnet new install Aspire.ProjectTemplates
๊ทธ๋ฆฌ๊ณ ์ ๋นํ ํด๋์์ ์๋ก์ด ์๋ฃจ์ ์ ์์ฑํฉ๋๋ค.
1dotnet new sln
๊ทธ ํ, ์๋ฃจ์ ํด๋์์ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ aspire-apphost ํ ํ๋ฆฟ์ ํ๋ก์ ํธ๋ฅผ ์์ฑํฉ๋๋ค.
1dotnet new aspire-apphost -o AppHost
๊ทธ๋ผ ์ธํ ์ ์ํ ๊ฐ๋จํ ์ฝ๋๋ง ์กด์ฌํ๋ aspire-apphost ํ๋ก์ ํธ๊ฐ ์์ฑ๋ฉ๋๋ค.
Valkey ์ถ๊ฐ
๊ทธ๋ผ ๊ฐ๋จํ๊ฒ Valkey๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
๋ฐ๋ก ์ถ๊ฐํ๊ธฐ ์ ์, dotnet aspire๋ community hosting์ ํตํด ๋ค์ํ ์๋ํํฐ ์๋ฃจ์
์ ์ ๊ณตํฉ๋๋ค.
๋น์ฐํ valkey๋ ์ด๋ฌํ ์ปค๋ฎค๋ํฐ ํธ์คํ
์ ์ง์์ ๋ฐ์ ์ ์์ผ๋ฉฐ, ๋ค์ nuget ํจํค์ง๋ฅผ ํตํด ์ฝ๊ฒ ์ด์ฉํ ์ ์์ต๋๋ค.
1dotnet add package Aspire.Hosting.Valkey
์ธ์ ๋ค์ํ ํตํฉ๋ ํธ์คํ
์ ์ ๊ณตํ๋, ์ด๊ณณ์์ ํ์ธํ ์ ์์ต๋๋ค.
๋ค์ valkey๋ก ๋์์์ AppHost ํ๋ก์ ํธ์์ Program.cs ํ์ผ์ ์ด์ด ๋ค์๊ณผ ๊ฐ์ด ์์ ํฉ๋๋ค.
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๋ valkey ์๋น์ค๋ฅผ ๋น๋ํ ์ ์๋ ์ ๋ณด๋ฅผ ๊ฐ์ง IResourceBuilder ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์
๋๋ค.WithDataVolume์ ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋ณผ๋ฅจ์ ์์ฑํ๋ฉฐ, WithPersistence๋ ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ง์์ ์ผ๋ก ์ ์ฅํ ์ ์๋๋ก ํฉ๋๋ค.
์ฌ๊ธฐ๊น์ง ๋ณด๋ฉด docker-compose์ volumes์ ๋น์ทํ ์ญํ ์ ํ๋ ๊ฒ์ผ๋ก ๋ณด์
๋๋ค.
๋น์ฐํ๊ฒ๋ ์ด๋ ์ฌ๋ฌ๋ถ๋ค๋ ์ด๋ ต์ง ์๊ฒ ๋ง๋์ค ์ ์์ต๋๋ค.
ํ์ง๋ง ์ด ๊ธ์ ๋ฒ์๋ฅผ ๋์ด๊ฐ๋ฏ๋ก ์ง๊ธ ์ด์ผ๊ธฐํ์ง๋ ์๊ฒ ์ต๋๋ค.
Go ์ธ์ด ์์ฝ ์๋ฒ ์์ฑ
๊ทธ๋ผ ๊ฐ๋จํ Go ์ธ์ด ์๋ฒ๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
์ผ๋จ ์๋ฃจ์
ํด๋์์ go work init์ ํตํด ์ํฌ์คํ์ด์ค๋ฅผ ์์ฑํฉ๋๋ค.
๋ท๋ท ๊ฐ๋ฐ์์๊ฒ Go ์ํฌ์คํ์ด์ค๋ ์๋ฃจ์
๊ณผ ์ ์ฌํ ๊ฑฐ๋ผ ๋ณด์๋ฉด ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ EchoServer๋ผ๋ ํด๋๋ฅผ ๋ง๋ค๊ณ , ์์ผ๋ก ์ด๋ํ ํ go mod init EchoServer๋ฅผ ์คํํฉ๋๋ค.
์ด ๋ช
๋ น์ด๋ฅผ ํตํด Go ๋ชจ๋์ ์์ฑํฉ๋๋ค. ๋ชจ๋์ ๋ท๋ท ๊ฐ๋ฐ์์๊ฒ ํ๋ก์ ํธ์ ์ ์ฌํ ๊ฒ์ผ๋ก ์ธ์งํ์๋ฉด ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ main.go ํ์ผ์ ๋ง๋ค์ด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํฉ๋๋ค.
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}
์ด ์๋ฒ๋ Aspire AppHost๊ฐ ์คํ๋ ๋, listening์ ํด์ผํ PORT ํ๊ฒฝ ๋ณ์๋ฅผ ์ฃผ์
ํด์ฃผ๋ฉด, ํด๋น ํฌํธ๋ฅผ ์ฝ์ด์ ์๋ฒ๋ฅผ ์คํํฉ๋๋ค.
๊ฐ๋จํ๊ฒ name ์ฟผ๋ฆฌ๋ฅผ ๋ฐ์์ Hello, {name}์ ๋ฐํํ๋ ์๋ฒ์
๋๋ค.
์ด์ ์ด ์๋ฒ๋ฅผ dotnet aspire์ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
์์ฝ ์๋ฒ๋ฅผ aspire์ ์ถ๊ฐํ๊ธฐ
๋ค์ Valkey๋ฅผ ์ถ๊ฐํ๋ Aspire AppHost๋ก ํ๋ก์ ํธ๋ก ์ด๋ํด์ Go ์ธ์ด๋ฅผ ์ํ ์ปค๋ฎค๋ํฐ ํธ์คํ ์ ์ถ๊ฐํฉ๋๋ค.
1dotnet add package CommunityToolkit.Aspire.Hosting.Golang
๊ทธ๋ฆฌ๊ณ Program.cs ํ์ผ์ ์ด์ด ๋ค์ ๊ตฌ๋ฌธ์ ์ถ๊ฐํฉ๋๋ค.
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();
์ฌ๊ธฐ์ echoServer๋ Go ์ธ์ด ์๋ฒ๋ฅผ ๋น๋ํ ์ ์๋ ์ ๋ณด๋ฅผ ๊ฐ์ง IResourceBuilder ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์
๋๋ค.
๋ฐฉ๊ธ ์ถ๊ฐํ AddGolangApp ๋ฉ์๋๋ Go ์ธ์ด ์๋ฒ๋ฅผ ์ถ๊ฐํ๊ธฐ ์ํ ์ปค์คํ
ํธ์คํธ์ ํ์ฅ ๋ฉ์๋์
๋๋ค.
๊ณ ์ ์ ์ผ๋ก 3000 ํฌํธ๋ฅผ ์ฌ์ฉํ๋ฉฐ, PORT ํ๊ฒฝ ๋ณ์๋ฅผ ์ฃผ์
ํด์ฃผ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก WithExternalHttpEndpoints๋ ์ธ๋ถ์์ ์ ๊ทผํ ์ ์๋๋ก ํ๋ ๊ฒ์
๋๋ค.
ํ
์คํธ๋ฅผ ์ํด http://localhost:3000/?name=world๋ก ์ ์ํด๋ณด์๋ฉด, Hello, world๊ฐ ์ถ๋ ฅ๋๋ ๊ฑธ ํ์ธํ์ค ์ ์์ ๊ฒ๋๋ค.
ํ์ง๋ง ํ์ฌ dotnet aspire์๋ non-dotnet project์๊ฒ ์ฃผ์ด์ง๋ ๋ฌด๊ฑฐ์ด ํจ๋ํฐ๊ฐ ์์ต๋๋ค.
๊ทธ๊ฒ์ ๋ฐ๋ก...
ํ๋ก์ ํธ ํ์ฅ
์ํ ํ์ฅ์ ๊ทธ๋ผ ์ด๋ป๊ฒ ํด?
ํ์ฌ dotnet aspire๋ AddProject ๋ฉ์๋๋ก ์ถ๊ฐ๋ ๋ท๋ท ํ๋ก์ ํธ์ ๋ํ ๋น๋์๋ง WithReplica ์ต์
์ ์ ๊ณตํฉ๋๋ค.
ํ์ง๋ง Go ์ธ์ด ํธ์คํธ๋ AddContainer๊ฐ์ ์ธ๋ถ ํ๋ก์ ํธ์ ๋ํด์๋ ์ด ์ต์
์ ์ ๊ณตํ์ง ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ์ ๋ณ๋์ load balancer๋ reverse proxy๋ฅผ ์ฌ์ฉํด์ ์ง์ ๊ตฌํํด์ผ ํฉ๋๋ค.
ํ์ง๋ง ์ด๋ฌ๋ฉด ํด๋น reverse proxy๊ฐ SPOF๊ฐ ๋ ์ ์๊ธฐ์, reverse proxy๋ WithReplica ์ต์
์ ์ ๊ณตํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ํ์ฐ์ ์ผ๋ก reverse proxy๋ ๋ท๋ท ํ๋ก์ ํธ์ฌ์ผ ํฉ๋๋ค.
์ฌํ ์ด๋ฌํ ๋ฌธ์ ์ ๋ํด์ nginx, trafik, ์ง์ ๊ตฌํ ๋ฑ์ ๋ฐฉ๋ฒ์ ์จ์์ง๋ง, ๋ท๋ท ํ๋ก์ ํธ๋ผ๋ ์ ํ์ด ๊ฑธ๋ฆฌ๋ฉด ์ ์์์๋ ๋น์ฅ ๋ฐฉ๋ฒ์ด ์์์ต๋๋ค.
๊ทธ๋์ ๋ท๋ท์ผ๋ก ๊ตฌํ๋ reverse proxy๋ฅผ ์ฐพ์๋ณด์๊ณ , ๋คํํ YARP๋ผ๋ ์ ํ์ง๊ฐ ์์์ต๋๋ค.
YARP๋ ๋ท๋ท์ผ๋ก ๊ตฌํ๋ reverse proxy๋ก, load balancer ์ญํ ๋ ํ ์ ์๊ณ , ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ์์๊ธฐ์ ์ข์ ์ ํ์ด๋ผ๊ณ ํ๋จํ์ต๋๋ค.
๊ทธ๋ผ ์ด์ YARP๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
YARP๋ก reverse proxy ๊ตฌ์ฑ
๋จผ์ YARP๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ํ๋ก์ ํธ๋ฅผ ์์ฑํฉ๋๋ค.
1dotnet new web -n ReverseProxy
๊ทธ๋ฆฌ๊ณ ํ๋ก์ ํธ๋ก ์ด๋ํด์ YARP๋ฅผ ์ค์นํฉ๋๋ค.
1dotnet add package Yarp.ReverseProxy --version 2.2.0
์ค์น๊ฐ ๋๋๋ฉด, Program.cs ํ์ผ์ ์ด์ด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํฉ๋๋ค.
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"}");
์ด ์ฝ๋๋ YARP๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ๊ธฐ๋ณธ์ ์ธ ์ฝ๋์
๋๋ค.routes๋ reverse proxy๊ฐ ์ฌ์ฉํ ๋ผ์ฐํธ ์ ๋ณด๋ฅผ, clusters๋ reverse proxy๊ฐ ์ฌ์ฉํ ํด๋ฌ์คํฐ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
์ด ์ ๋ณด๋ค์ LoadFromMemory ๋ฉ์๋๋ก reverse proxy์ ๋ก๋๋ฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก MapReverseProxy ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ reverse proxy๋ฅผ ๋งคํํ๊ณ ์คํํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ค์ฌ์ฉ์ ์ํด aspire apphost ํ๋ก์ ํธ์์ reverse proxy ํ๋ก์ ํธ๋ฅผ ์ฐธ์กฐ๋ก ๋ฃ์ด์ฃผ๊ณ , 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();
์ด์ reverse proxy๊ฐ echo server๋ฅผ ์ฐธ์กฐํ ์ ์์ต๋๋ค.
์ธ๋ถ์์ ๋ค์ด์ค๋ ์์ฒญ์ reverse proxy์์ ๋ฐ๊ณ echo server๋ก ๋๊ฒจ์ฃผ๋ ๊ตฌ์กฐ๋ก ๋ณ๊ฒฝ๋๊ณ ์์ต๋๋ค.
Reverse proxy ์์
์ผ๋จ์ reverse proxy์ ํ ๋น๋ ํ๋ก์ ํธ์ listening ์ฃผ์๋ฅผ ๋ณ๊ฒฝํด์ผํฉ๋๋ค.Properties/launchSettings.json ํ์ผ ๋ด๋ถ์ applicationUrl์ ์ ๊ฑฐํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ Program.cs ํ์ผ์ ์ด์ด ์๋์ ๊ฐ์ด ๋๋์ ์ผ๋ก ์์ ํฉ๋๋ค.
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"}");
๋จผ์ routes์ clusters์ ๋ํ ์ ๋ณด๋ฅผ ์์ ํฉ๋๋ค.
๊ฐ๊ฐ echo-route์ echo-cluster๋ฅผ ์ถ๊ฐํ์ฌ, echo server๋ก ์์ฒญ์ ๋ณด๋ด๋๋ก ์ค์ ํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ echo server์ ์ฃผ์๋ฅผ ํ๊ฒฝ ๋ณ์๋ก๋ถํฐ ์ฝ์ด์์ ์ฌ์ฉํ๋๋ก ์์ ํฉ๋๋ค.
์ด ์ฃผ์์ ๊ท์น์ services__{service-name}__http__{index}์
๋๋ค.
echo server์ ๊ฒฝ์ฐ, ์๋น์ค ์ด๋ฆ์ด echo-server์ด๊ณ , ๋จ์ผ ์ธ์คํด์ค์ด๊ธฐ์ ์ธ๋ฑ์ค๋ก 0์ ์ฌ์ฉํฉ๋๋ค.
๋ง์ฝ asp .net core ์๋ฒ๋ฅผ ์ถ๊ฐํ๋ค๋ฉด, WithReplica๋ฅผ ํตํด ์ฌ๋ฌ ์ธ์คํด์ค๊ฐ ์์ฑ๋ ์ ์์ผ๋ฏ๋ก ์ธ๋ฑ์ค๋ฅผ ์ฆ๊ฐ์์ผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
์์ธ ์ฒ๋ฆฌ๋์ด ์๋ http://localhost:8080์ ์๋ฌด๋ฐ ๋ป์ด ์๋ ์ฐ๋ ๊ธฐ ๊ฐ์
๋๋ค.
์ด์ ํ๋ก์ ํธ๋ฅผ ์คํํ๊ณ , http://localhost:3000/?name=world๋ก ์ ์ํด๋ณด์๋ฉด, ์ฌ์ ํ Hello, world๊ฐ ์ถ๋ ฅ๋๋ ๊ฑธ ํ์ธํ์ค ์ ์์ ๊ฒ๋๋ค.
ํ์ฅ ์์ด๋์ด
์ด์ dotnet aspire์ Go ์๋ฒ๋ฅผ ์ถ๊ฐํ๊ณ , reverse proxy๋ฅผ ํตํด ์์ฒญ์ ์ ๋ฌํ๋ ๊ฒ์ ํ์ธํ์ต๋๋ค.
๊ทธ๋ฌ๋ฉด ์ด์ ์ด ๊ณผ์ ์ programmaticํ๊ฒ ๊ตฌํํ ์ ์๋๋ก ํ์ฅํ ์ ์์ ๊ฒ๋๋ค.
์๋ฅผ ๋ค์ด, echo server์ ๋ํด์ ์๋น์ค ์ด๋ฆ ๋ค์ ๋๋ฒ๋ง์ ์ถ๊ฐํ์ฌ ์ฌ๋ฌ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ , reverse proxy์ ๋ํ ์ค์ ์ ์๋์ผ๋ก ์ถ๊ฐํ ์ ์์ต๋๋ค.
aspire apphost ํ๋ก์ ํธ์ Program.cs ํ์ผ์ reverse proxy์ echo server๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํฉ๋๋ค.
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}
๊ทธ๋ฆฌ๊ณ reverse proxy ํ๋ก์ ํธ์ Program.cs ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํฉ๋๋ค.
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};
8๊ฐ๋ก ๋์ด๊ฐ echo server ์ธ์คํด์ค์ ๋ํด ๋ชฉ์ ์ง ์ค์ ์ ์ถ๊ฐํฉ๋๋ค.
์ด์ reverse proxy๋ ๋์ด๋ echo server๋ค์ ๋ํ ๋ชฉ์ ์ง ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ , ์์ฒญ์ ์ ๋ฌํด์ค ์ ์๊ฒ ๋์์ต๋๋ค.
๊ธฐ์กด์ http://localhost:3000/?name=world๋ก ์ ์ํด๋ณด์๋ฉด, ์ฌ์ ํ Hello, world๊ฐ ์ถ๋ ฅ๋๋ ๊ฑธ ํ์ธํ์ค ์ ์์ ๊ฒ๋๋ค.
๋ง์น๋ฉฐ
์ด ๊ธ์์๋ dotnet aspire์ Go ์๋ฒ๋ฅผ ์ถ๊ฐํ๊ณ , reverse proxy๋ฅผ ํตํด ์์ฒญ์ ์ ๋ฌํ๋ ๊ณผ์ ์ ์ค๋ช
ํ์ต๋๋ค.
๋ค๋ง ํ์ฅ์ ๊ดํด์๋ ์์ง ๋ชจ๋ ์์ฑํ์ง ์์๊ณ , ํ๊ฒฝ ๋ณ์๋ฅผ ํตํด ์ข ๋ programmaticํ๊ฒ ๊ตฌํํ ์ ์๋ ์์ ๋ฅผ ๋ณ๋ ๋ ํฌ์ ์์ฑํด๋์์ต๋๋ค.
์์ธํ ํ๋ก์ ํธ ๊ตฌ์ฑ๊ณผ ์ฝ๋๋ snowmerak/AspireStartPack์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
์ ๋ ๊ฐ์ธ์ ์ผ๋ก dotnet aspire๊ฐ docker compose์ ๋์์ผ๋ก์จ, ๊ทธ๋ฆฌ๊ณ ํด๋ผ์ฐ๋ ๋ฐฐํฌ ํด๋ก์จ ์์ ๋ง์ ๋กค์ ์ํํ ์ ์๋ค๊ณ ๊ธฐ๋ํฉ๋๋ค.
์ด๋ฏธ docker compose๋ k8s manifest๋ฅผ ์์ฑํ๋ ์ ๋๋ ์ดํฐ๊ฐ ์กด์ฌํ์ฌ, ์ผ๋ฐ ๊ฐ๋ฐ์๊ฐ ์ธํ๋ผ ๋๊ตฌ์ ๋ํ ์ ๊ทผ์ฑ์ด ๋ ์ข์์ง์ง ์์๋ ์๊ฐํฉ๋๋ค.