Попытка масштабируемого запуска Go-сервера в dotnet aspire
dotnet aspire?
dotnet aspire — это инструмент, созданный для помощи разработчикам в разработке и настройке облачных нативных приложений в условиях растущей популярности облачных нативных сред. Этот инструмент позволяет .NET-разработчикам легко развертывать .NET-проекты, различную облачную нативную инфраструктуру, а также сервисы и контейнеры, написанные на других языках.
Само собой разумеется, что от docker до k8s происходит выпуск и эксплуатация, и значительная часть областей, отраслей и разработчиков переходит в облачную нативную среду из существующей локальной среды, и уже перешла. Теперь это зрелая область. Поэтому, я полагаю, нет необходимости объяснять существующие неудобства, связанные с именами хостов, конфигурацией портов, брандмауэрами, управлением метриками и т. д.
Поэтому, даже судя по вышеприведенным объяснениям, вы, вероятно, понятия не имеете, что такое dotnet aspire. Это потому, что даже Microsoft не дает точного определения. Поэтому я тоже не буду давать никаких особых определений. Однако, поскольку в этой статье я буду использовать основные функции 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 также может получить поддержку от этого community hosting и его можно легко использовать через следующий пакет nuget.
1dotnet add package Aspire.Hosting.Valkey
Поскольку он предоставляет различные интегрированные хостинги, вы можете проверить их здесь. Вернемся к valkey и откроем файл Program.cs в проекте AppHost и изменим его следующим образом.
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
— это реализация интерфейса IResourceBuilder
, который содержит информацию для создания службы valkey.WithDataVolume
создает том для хранения данных кэша, а WithPersistence
позволяет постоянно хранить данные кэша.
Глядя на это, кажется, что он выполняет аналогичную роль volumes
в docker-compose
.
Конечно, вы также можете легко создать его.
Однако, это выходит за рамки данной статьи, поэтому я не буду говорить об этом сейчас.
Создание эхо-сервера на языке Go
Теперь давайте добавим простой сервер на языке Go.
Сначала создайте рабочую область с помощью go work init
в папке решения.
Для .NET-разработчиков рабочая область Go аналогична решению.
Затем создайте папку с именем EchoServer, перейдите в нее и выполните go mod init EchoServer
.
Эта команда создает модуль Go. Модуль можно считать аналогичным проекту для .NET-разработчиков.
Затем создайте файл 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, он принимает переменную среды PORT
, которую необходимо прослушивать, считывает этот порт и запускает сервер.
Это простой сервер, который получает запрос name
и возвращает Hello, {name}
.
Теперь давайте добавим этот сервер в dotnet aspire.
Добавление эхо-сервера в aspire
Снова перейдите в проект Aspire AppHost, где вы добавили Valkey, и добавьте community hosting для языка 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
— это реализация интерфейса IResourceBuilder
, который содержит информацию для создания сервера на языке Go.
Только что добавленный метод AddGolangApp
— это метод расширения для пользовательского хоста для добавления сервера на языке Go.
Вы можете увидеть, что он постоянно использует порт 3000 и вводит переменную среды PORT
.
Наконец, WithExternalHttpEndpoints
позволяет получить доступ извне.
Для тестирования, если вы перейдете по адресу http://localhost:3000/?name=world
, вы увидите, что выводится Hello, world
.
Однако в настоящее время dotnet aspire имеет тяжелый штраф для non-dotnet проектов. Это именно...
Расширение проекта
Как насчет горизонтального масштабирования?
В настоящее время dotnet aspire предоставляет опцию WithReplica
только для строителей .NET-проектов, добавленных с помощью метода AddProject
.
Однако он не предоставляет эту опцию для хостов на языке Go или внешних проектов, таких как AddContainer
.
Поэтому вам нужно будет реализовать его самостоятельно, используя отдельный балансировщик нагрузки или обратный прокси.
Однако, поскольку этот обратный прокси может стать единой точкой отказа, обратный прокси должен предоставлять опцию WithReplica
.
В этом случае обратный прокси неизбежно должен быть .NET-проектом.
До сих пор мы использовали такие методы, как nginx, trafik или прямая реализация, чтобы решить эту проблему, но когда появляется ограничение на .NET-проекты, у меня нет никаких способов прямо сейчас. Поэтому я поискал обратный прокси, реализованный на .NET, и, к счастью, была возможность YARP. YARP — это обратный прокси, реализованный на .NET, который также может выполнять роль балансировщика нагрузки и предоставлял различные функции, поэтому я счел его хорошим выбором.
Теперь давайте добавим YARP.
Настройка обратного прокси с помощью YARP
Сначала создайте проект для использования 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
содержат информацию о маршрутах, которые будет использовать обратный прокси, а clusters
— информацию о кластерах, которые будет использовать обратный прокси.
Эта информация загружается в обратный прокси с помощью метода LoadFromMemory
.
Наконец, используйте метод MapReverseProxy
, чтобы сопоставить обратный прокси и запустить его.
Затем, для практического использования, добавьте ссылку на проект обратного прокси в проект aspire apphost и добавьте и измените следующую инструкцию в файле 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();
Теперь обратный прокси может ссылаться на эхо-сервер. Структура изменяется так, что входящие извне запросы принимаются обратным прокси и передаются эхо-серверу.
Изменение обратного прокси
Прежде всего, необходимо изменить адрес прослушивания проекта, назначенного обратному прокси.
Удалите applicationUrl
внутри файла Properties/launchSettings.json
.
Затем откройте файл 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
соответственно, чтобы отправлять запросы на эхо-сервер.
И измените его, чтобы использовать адрес эхо-сервера, считывая его из переменной среды.
Правило для этого адреса — services__{service-name}__http__{index}
.
В случае эхо-сервера имя службы — echo-server
, и, поскольку это один экземпляр, индекс 0
используется.
Если вы добавляете сервер asp .net core, несколько экземпляров могут быть созданы через WithReplica
, поэтому вы можете использовать их, увеличивая индекс.
Исключение http://localhost:8080
— это мусорное значение, которое ничего не значит.
Теперь запустите проект и перейдите по адресу http://localhost:3000/?name=world
, и вы увидите, что по-прежнему выводится Hello, world
.
Идеи по расширению
Теперь мы проверили, что добавляем сервер Go в dotnet aspire и передаем запросы через обратный прокси. Теперь мы можем расширить этот процесс для программной реализации. Например, для эхо-сервера можно создать несколько экземпляров, добавив нумерацию после имени службы, и автоматически добавить настройки для обратного прокси.
Измените код, использующий обратный прокси и эхо-сервер, в файле Program.cs проекта aspire apphost следующим образом.
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}
И измените файл 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 экземпляров эхо-сервера.
Теперь обратный прокси имеет информацию о назначениях для увеличенных эхо-серверов и может передавать запросы.
Если вы перейдете по адресу http://localhost:3000/?name=world
, вы по-прежнему увидите, что выводится Hello, world
.
В заключение
В этой статье было описано, как добавить сервер Go в dotnet aspire и передавать запросы через обратный прокси. Однако расширение еще не полностью написано, и я написал пример, который можно более программно реализовать с помощью переменных среды, в отдельном репозитории. Для получения подробной информации о конфигурации проекта и коде см. snowmerak/AspireStartPack.
Лично я ожидаю, что dotnet aspire сможет выполнять свою роль в качестве альтернативы docker compose и инструмента развертывания в облаке. Уже существует генератор, который генерирует docker compose или k8s manifest, поэтому я думаю, что доступность инструментов инфраструктуры для обычных разработчиков улучшилась.