Mencoba Menjalankan Server Go Secara Terukur di dotnet aspire
dotnet aspire?
dotnet aspire adalah sebuah alat yang dikembangkan untuk membantu para pengembang dalam pengembangan dan konfigurasi cloud-native, seiring dengan meningkatnya lingkungan cloud-native. Alat ini memungkinkan pengembang .NET untuk dengan mudah menyebarkan proyek .NET, berbagai infrastruktur cloud-native, serta layanan atau kontainer dalam bahasa lain.
Tentu saja, alat ini dirilis dan dioperasikan dari docker hingga k8s. Saat ini, banyak sektor, industri, dan pengembang yang beralih ke lingkungan cloud-native dari lingkungan on-premise yang ada, atau telah beralih. Ini adalah bidang yang sudah matang. Oleh karena itu, saya pikir tidak perlu menjelaskan ketidaknyamanan yang ada terkait nama host, konfigurasi port, firewall, dan manajemen metrik.
Maka dari itu, berdasarkan penjelasan di atas, Anda mungkin tidak akan memahami apa itu dotnet aspire. Hal ini karena bahkan Microsoft sendiri tidak memberikan definisi yang tepat. Oleh karena itu, saya juga tidak akan memberikan definisi khusus. Namun, karena dalam tulisan ini saya akan menggunakan fungsi dasar dotnet aspire yang saya pahami, silakan merujuknya dan menentukan posisi Anda sendiri.
Konfigurasi Proyek
Pembuatan Proyek dotnet aspire
Jika Anda tidak memiliki templat dotnet aspire, Anda harus menginstal templat terlebih dahulu. Instal templat dengan perintah berikut. Jika Anda tidak memiliki .net, silakan instal sendiri.
1dotnet new install Aspire.ProjectTemplates
Kemudian, buat solusi baru di folder yang sesuai.
1dotnet new sln
Setelah itu, jalankan perintah berikut di folder solusi untuk membuat proyek dengan templat aspire-apphost.
1dotnet new aspire-apphost -o AppHost
Maka, proyek aspire-apphost yang hanya berisi kode sederhana untuk pengaturan akan dibuat.
Penambahan Valkey
Sekarang, mari kita tambahkan Valkey secara sederhana.
Sebelum menambahkan, dotnet aspire menyediakan berbagai solusi pihak ketiga melalui community hosting. Tentu saja, valkey juga dapat menerima dukungan dari community hosting ini, dan dapat digunakan dengan mudah melalui paket nuget berikut.
1dotnet add package Aspire.Hosting.Valkey
Selain itu, berbagai hosting terintegrasi juga disediakan, yang dapat Anda lihat di sini. Kembali ke valkey, buka file Program.cs di proyek AppHost dan modifikasi sebagai berikut.
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
adalah implementasi dari interface IResourceBuilder
yang memiliki informasi untuk membangun layanan valkey.WithDataVolume
membuat volume untuk menyimpan data cache, dan WithPersistence
memungkinkan data cache disimpan secara persisten.
Sejauh ini, terlihat seperti peran yang mirip dengan volumes
di docker-compose
.
Tentu saja, Anda juga dapat membuatnya dengan mudah.
Namun, karena hal ini di luar cakupan artikel ini, saya tidak akan membahasnya sekarang.
Pembuatan Server Echo Bahasa Go
Selanjutnya, mari kita tambahkan server bahasa Go sederhana.
Pertama, buat workspace dengan go work init
di folder solusi.
Bagi pengembang .NET, Go workspace dapat dianggap mirip dengan solusi.
Kemudian, buat folder bernama EchoServer, pindah ke dalamnya, dan jalankan go mod init EchoServer
.
Perintah ini membuat Go module. Module dapat dianggap mirip dengan proyek bagi pengembang .NET.
Lalu, buat file main.go
dan tulis sebagai berikut.
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}
Server ini, ketika Aspire AppHost dijalankan, akan membaca port yang diberikan oleh variabel lingkungan PORT
yang harus didengarkan, lalu menjalankan server.
Ini adalah server sederhana yang menerima query name
dan mengembalikan Hello, {name}
.
Sekarang, mari kita tambahkan server ini ke dotnet aspire.
Menambahkan Server Echo ke aspire
Kembali ke proyek Aspire AppHost tempat Valkey ditambahkan, tambahkan community hosting untuk bahasa Go.
1dotnet add package CommunityToolkit.Aspire.Hosting.Golang
Kemudian, buka file Program.cs dan tambahkan pernyataan berikut.
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();
Di sini, echoServer
adalah implementasi dari interface IResourceBuilder
yang memiliki informasi untuk membangun server bahasa Go.
Metode AddGolangApp
yang baru saja ditambahkan adalah metode ekstensi dari custom host untuk menambahkan server bahasa Go.
Dapat dilihat bahwa port 3000 digunakan secara tetap, dan variabel lingkungan PORT
diinjeksikan.
Terakhir, WithExternalHttpEndpoints
memungkinkan akses dari luar.
Untuk pengujian, jika Anda mengakses http://localhost:3000/?name=world
, Anda akan melihat Hello, world
dicetak.
Namun, saat ini dotnet aspire memiliki penalti berat yang diberikan kepada proyek non-dotnet. Yaitu...
Ekstensi Proyek
Lalu, Bagaimana Cara Melakukan Skala Horizontal?
Saat ini, dotnet aspire hanya menyediakan opsi WithReplica
untuk builder proyek dotnet yang ditambahkan dengan metode AddProject
.
Namun, opsi ini tidak disediakan untuk host bahasa Go atau proyek eksternal seperti AddContainer
.
Oleh karena itu, Anda harus mengimplementasikannya sendiri dengan menggunakan load balancer atau reverse proxy terpisah.
Namun, jika demikian, reverse proxy tersebut dapat menjadi SPOF, jadi reverse proxy sebaiknya menyediakan opsi WithReplica
.
Maka, mau tidak mau reverse proxy harus berupa proyek dotnet.
Selama ini, saya telah menggunakan metode seperti nginx, trafik, atau implementasi langsung untuk masalah ini, tetapi jika ada batasan bahwa itu harus berupa proyek dotnet, saya tidak punya cara untuk saat ini. Jadi, saya mencari reverse proxy yang diimplementasikan dengan dotnet, dan untungnya ada pilihan YARP. YARP adalah reverse proxy yang diimplementasikan dengan dotnet, dapat berperan sebagai load balancer, dan menyediakan berbagai fungsi, sehingga saya menilai ini adalah pilihan yang baik.
Sekarang, mari kita tambahkan YARP.
Konfigurasi Reverse Proxy dengan YARP
Pertama, buat proyek untuk menggunakan YARP.
1dotnet new web -n ReverseProxy
Kemudian, pindah ke proyek dan instal YARP.
1dotnet add package Yarp.ReverseProxy --version 2.2.0
Setelah instalasi selesai, buka file Program.cs dan tulis sebagai berikut.
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"}");
Kode ini adalah kode dasar untuk menggunakan YARP.routes
berisi informasi rute yang akan digunakan oleh reverse proxy, dan clusters
berisi informasi cluster yang akan digunakan oleh reverse proxy.
Informasi ini dimuat ke reverse proxy dengan metode LoadFromMemory
.
Terakhir, reverse proxy dipetakan dan dijalankan dengan menggunakan metode MapReverseProxy
.
Dan untuk penggunaan sebenarnya, tambahkan referensi proyek reverse proxy ke proyek aspire apphost, lalu tambahkan dan modifikasi pernyataan berikut di file 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();
Sekarang, reverse proxy dapat mereferensikan echo server. Struktur berubah sehingga permintaan yang masuk dari luar diterima di reverse proxy dan diteruskan ke echo server.
Modifikasi Reverse Proxy
Pertama, alamat listening proyek yang dialokasikan ke reverse proxy harus diubah.
Hapus applicationUrl
di dalam file Properties/launchSettings.json
.
Kemudian, buka file Program.cs dan modifikasi secara besar-besaran seperti di bawah ini.
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"}");
Pertama, modifikasi informasi untuk routes
dan clusters
.
Tambahkan echo-route
dan echo-cluster
masing-masing, dan atur agar permintaan dikirim ke echo server.
Kemudian, modifikasi agar alamat echo server dibaca dari variabel lingkungan dan digunakan.
Aturan alamat ini adalah services__{service-name}__http__{index}
.
Dalam kasus echo server, nama layanannya adalah echo-server
, dan karena ini adalah instance tunggal, 0
digunakan sebagai indeks.
Jika Anda menambahkan server asp .net core, beberapa instance dapat dibuat melalui WithReplica
, jadi Anda dapat menggunakannya dengan meningkatkan indeks.http://localhost:8080
yang ditangani pengecualian adalah nilai sampah yang tidak berarti.
Sekarang jalankan proyek, dan jika Anda mengakses http://localhost:3000/?name=world
, Anda akan tetap melihat Hello, world
dicetak.
Ide untuk Ekspansi
Sekarang, kita telah mengonfirmasi bahwa kita telah menambahkan server Go ke dotnet aspire dan meneruskan permintaan melalui reverse proxy. Maka, sekarang kita dapat memperluasnya sehingga proses ini dapat diimplementasikan secara programatik. Misalnya, kita dapat membuat beberapa instance dengan menambahkan penomoran setelah nama layanan untuk echo server, dan secara otomatis menambahkan pengaturan untuk reverse proxy.
Modifikasi kode yang menggunakan reverse proxy dan echo server di file Program.cs dari proyek aspire apphost sebagai berikut.
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}
Kemudian, modifikasi file Program.cs dari proyek reverse proxy sebagai berikut.
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};
Tambahkan pengaturan tujuan untuk instance echo server yang telah meningkat menjadi 8.
Sekarang reverse proxy memiliki informasi tujuan untuk echo server yang meningkat dan dapat meneruskan permintaan.
Jika Anda mengakses http://localhost:3000/?name=world
yang ada, Anda akan tetap melihat Hello, world
dicetak.
Penutup
Dalam tulisan ini, saya telah menjelaskan proses menambahkan server Go ke dotnet aspire dan meneruskan permintaan melalui reverse proxy. Namun, mengenai ekspansi, saya belum menulis semuanya, dan saya telah membuat contoh yang dapat diimplementasikan secara lebih programatik melalui variabel lingkungan di repositori terpisah. Untuk detail konfigurasi proyek dan kode, silakan merujuk ke snowmerak/AspireStartPack.
Saya pribadi berharap dotnet aspire dapat memainkan perannya sendiri sebagai alternatif dari docker compose, dan sebagai alat penyebaran cloud. Generator yang sudah ada untuk membuat docker compose atau k8s manifest (generator), saya pikir aksesibilitas alat infrastruktur untuk pengembang umum telah meningkat.