Часть 1. Перевод приложений в Aspire
Часть 2. Локальное развертывание с помощью Aspire
Что такое Aspire?
Если быть кратким, то Aspire - это стек для создания облачных приложений. Из коробки доступно большое количество функций: начиная от автоматической контейнеризации приложений и передачи строк подключения до предоставления различных сервисов.
Подробнее можно прочитать в этой статье.
Что будет внутри этого цикла статей?
Первым делом мы подключим два существующих приложения: ASP.NET в качестве API и консольное приложение, реализующее этот интерфейс в виде дискорд-бота (каюсь, с фронтом знаком недостаточно хорошо, чтобы сделать приложение, которое было бы нестыдно показать).
Во второй и последней статье мы опубликуем приложения локально в Docker Desktop.
А теперь начнем.
Подготовка
В рамках данной статьи мы будем подключать следующие два проекта к .NET Aspire: Web API и Discord-бота, подключенного к нему. Сервер осуществляет хранение и изменение состояния "банковских счетов" в базе данных, а Discord-бот предоставляет пользователям возможность регистрироваться в системе, создавать новые "банковские счета", пополнять их, снимать с них деньги и осуществлять переводы денег с одного счета на другой.
Подробнее про запуск Discord-бота, если вы захотите повторить все действия на данных проектах можно прочитать тут.
Также для запуска проекта с .NET Aspire необходимо установить Docker Desktop (в случае Windows)
Чтобы перевести проект на Aspire, первым делом создадим пустое приложение .NET Aspire:
Изначально нас будет ожидать почти пустой файл Program.cs в проекте TestAspireProject.AppHost. Это основной проект, управляющий запуском остальных проектов.
Первым делом подключим существующие проекты: перенесем в папку с решением TestAspireProject папки проектов и добавим существующие проекты (не забываем перенести CommonClasses):
И заменим код Program.cs в проекте TestAspireProject.AppHost на следующий (также не забудьте заменить токен в Program.cs проекта AspireDiscordApp:
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddProject<Projects.WebAPI>("api"); // Добавляем в запуск API
var bot = builder.AddProject<Projects.AspireDiscordApp>("bot"); // добавляем в запуск бота
builder.Build().Run();
И... Все заработало, бот действительно откликается на наши команды. Но этого на самом деле недостаточно, потому что мы не использовали основные возможности Aspire, а лишь запустили другие проекты с его помощью, поэтому продолжим настраивать приложения.
Первым делом создадим базу данных в контейнере и передадим ее серверу API. Для этого сначала нужно установить NuGet-пакет Aspire.Hosting.PostgreSQL (ну либо любой другой, список доступных провайдеров можно найти по запросу Aspire.Hosting)
Для этого изменим Program.cs проекта TestAspireProject.AppHost следующим образом:
var builder = DistributedApplication.CreateBuilder(args);
var dbServer = builder.AddPostgres("db") // Добавляем Postgres
.WithPgAdmin(); // И включаем для него PgAdmin
var db = dbServer.AddDatabase("BankBase"); // Создаем внутри Postgres базу данных с именем "BankBase"
var api = builder.AddProject<Projects.WebAPI>("api")
.WithReference(db); // Добавляем ссылку на базу данных
var bot = builder.AddProject<Projects.AspireDiscordApp>("bot");
builder.Build().Run();
После запуска, в панели управления Aspire при просмотре сведений о проекте api можно заметить добавление переменной среды "ConnectionStrings__BankBase", с помощью которой можно будет подключиться к базе данных postgres. Подключимся же по ней. Для этого следует добавить в зависимости проекта WebAPI добавить проект TestAspireProject.ServiceDefaults, после чего изменить файл Program.cs проекта WebAPI (а заодно временно добавим DataBase.EnsureCreated() в конструктор BankDbContext):
// Эту строку следует добавить сразу же после создание builder:
builder.AddServiceDefaults();
// Также следует заменить название строки подключения
options.UseNpgsql(builder.Configuration.GetConnectionString("BankBase")));
Создадим тестового пользователя через swagger и проверим, что он добавился в базу:
После этого перезапустим приложение и перестанем радоваться: в базе пропали все изменения. Это связано с тем, что для базы не было выделено место на диске, в связи с чем, изменения хранятся ровно в течении времени работы приложения. Выделим же место для базы, заодно добавив статический пароль для пользователя (по умолчанию он генерируется каждый раз при перезапуске приложения, поэтому, если явно не указать его, доступ к базе будет утерян). В файл Program.cs внесем следующие изменения
// Добавляем ресурс с паролем
var dbPassword = builder.AddParameter("DatabasePassword");
// После чего указываем данный пароль для базы данных
var dbServer = builder.AddPostgres("db", password: dbPassword)
// И указываем, что необходимо выделить место на диске для базы
.WithDataVolume().WithPgAdmin();
Также необходимо указать пароль в appsettings.json того же самого проекта:
"Parameters": {
"DataBasePassword": "<Ваш пароль>"
}
После этого в панели управления Aspire можно увидеть обновленную строку подключения к базе, а изменения перестанут удаляться после перезапуска.
Последнее, что осталось сделать в рамках данного проекта - передавать в API Discord-бота адрес сервера. Для этого изменим код в Program.cs AppHost'а следующим образом, после чего добавятся две новые переменные среды:
var bot = builder.AddProject<Projects.AspireDiscordApp>("bot")
// Добавляем ссылку на базовый адрес API
.WithReference(api);
Поэтому изменим строку с созданием HttpClientFactory:
// Получаем базовый адрес из переменных среды
services.AddHttpClient("WebAPI", x=> x.BaseAddress = new Uri(Environment.GetEnvironmentVariable("services__api__https__0")));
Также можно передавать переменные среды напрямую с помощью метода .WithEnviroment(), в котором необходимо указать название переменной среды и ее значение. Добавим передачу токена для Discord именно таким образом:
// TestAspireProject.AppHost Program.cs:
// Добавим параметр из appsettings.json (его нужно заранее добавить)
var discordToken = builder.AddParameter("DiscordToken");
// А также добавим переменную среды в приложение:
var bot = builder.AddProject<Projects.AspireDiscordApp>("bot")
.WithReference(api).WithEnvironment("token", discordToken);
// AspireDiscordAppProgram.cs:
// Получаем токен из переменных среды:
var token = Environment.GetEnvironmentVariable("token");
Заключение
В рамках данной статьи был произведен переход от двух отдельных проектов к единой системе с помощью Aspire. Был рассмотрен простой пример, на примере которого можно проследить основные этапы ручного перехода на Aspire.
Репозиторий с итоговым проектом доступен тут. (Также туда сразу же залиты изменения, требуемые для развертывания консольного приложения)
В рамках следующей статьи будет рассмотрена публикация приложений на локальной машине.