Когда я первый раз услышал про .NET Aspire, я подумал что это какая-то очередная лажа от Майкрософта, про которую все забудут через неделю.

Особенно, учитывая какую дичь часто завозят в шарп (например те же ужасно спроектированные Primary Constructor'ы про которые я писал, или вот прикол-пропозал от самого Тоуба). Так что ожидания у меня, честно говоря, были ниже нуля.

Но попробовав его лично, я был, честно говоря, шокирован. Трепещите, жависты!! Трепещите гошники! Трепещите питонисты - такого вы еще точно не видели.

Я даже представить не мог, что DevEx можно сделать настолько офигительным.

Эта картинка с Рэнди Ортоном - символизирует меня, ошеломленного великой мощью Aspire
Эта картинка с Рэнди Ортоном - символизирует меня, ошеломленного великой мощью Aspire

Собственно, о чем это я

Так вот, что же такое .NET Aspire?

> .NET Aspire is an opinionated, cloud ready stack for building observable, production ready, distributed applications. .NET Aspire is delivered through a collection of NuGet packages that handle specific cloud-native concerns. Cloud-native apps often consist of small, interconnected pieces or microservices rather than a single, monolithic code base.

Aspire - это темплейт + opinionated стек для микросервисных приложений. Он предоставляет следующие фичи:

  • Оркестрация - Aspire делает простым запуск и подключение многопроектных приложений и их зависимостей к локальным средам разработки. Он сам поднимает все апи, динамически генерирует докерфайлы, реплицирует сервисы при необходимости, сам поднимает нужную инфраструктуру - докер или подман контейнеры с базой, брокерами и кешом

  • Готовый тулинг - в темплейте, Aspire по умолчанию генерит код и настройки для OpenTelemetry - логи, метрики и трейсы - трех столбов observability. Не нужно возиться с секретами, каким-то своим поднятием Prometheus'а - Aspire сам коллектит всю телеметрию с сервисов, чтобы затем показывать это все в админке

  • Крутой локальный дашборд - все метрики, список контейнеров, логи, трейсы - в одной готовой админке, причем смотрящейся достаточно неплохо. Не надо ничего поднимать локально в докер композах - ни графану ни прометеус. Коллектор .NET Aspire'а получит все самостоятельно

  • Легкий деплой в Azure/k8s - по словам команды которая работает над Aspire, на релизе можно будет в одну кнопку запубликовать весь проект на облако в Azure, либо сгенерировать k8s манифесты чтобы разворачивать на своей/чужой инфраструктуре

Например - у вас есть продукт который находится в монорепе на 30 сервисов. Как правило, в микросервисах про локальное тестирование работы нескольких сервисов одновременно можно забыть - самому это настраивать как правило очень больно. С Aspire - вы сможете без проблем локально потестить то что вам надо, например сценарии failover'ов.

Хотя Aspire и ориентирован в первую очередь на распределенные приложения, его можно применять даже если у вас всего один сервис - например чтобы локально продебажить observability - посмотреть собираются ли локально метрики и трейсы. На мой взгяд - достаточно полезно.

Простота интеграции в существующие проекты

Одним из моих самых больших concern'ов было то, что Aspire будет очень сложно прикрутить в существующие проекты. Но тут Майки это смогли очень неплохо обыграть.

На на самом деле, все невероятно просто. Надо сделать всего две вещи:

  1. Создать отдельный проект AppHost, и в нём зареферить проекты которые ты хочешь запустить, в DistributedApplicationBuilder зарегистрировать соответствующие проекты

  2. Для нужных фичей, которые идут в Aspire из коробки (например OpenTelemetry, Service Discovery, коннект в PostgeSQL), нужно поставить отдельный Nuget и просто заменить регистрацию в Startup, либо вызвать Extension метод из ServiceDefaults

Всё! Aspire настроен.

Погнали тестить

Давайте теперь глянем на примере. Возьмем стандартный Starter Application темплейт.

Для начала, надо поставить Aspire. Для этого выполняем две команды:

dotnet workload update
dotnet workload install aspire

Если вы используете Rider, так же как и я, то нужно дополнительно поставить плагин: https://blog.jetbrains.com/dotnet/2024/02/19/jetbrains-rider-and-the-net-aspire-plugin/

Если используете Preview версию Visual Studio, насколько я помню там Aspire поставлен по умолчанию.

Итак, генерируем проект в нашей IDE, получаем следующую структуру:

Структура проекта
Структура проекта

Быстренько пробежимся по проектам:

  • TestAspire.AppHost - основной проект где мы описываем наше распределенное приложение - какие контейнеры поднимать (База, Брокер, Кэш), что от чего зависит, и так далее.

  • TestAspire.ServiceDefaults - проект, содержащий extension method'ы для телеметрии, Service Discovery и другие настройки. Этот проект подключается во все остальные

  • TestAspire.ApiService - API на ASP.NET Core, обычный WeatherApi из темплейта для webapi

  • TestAspire.Web - Blazor фронтенд приложение, тоже дефолтный темплейт для блазора

AppHost

Данный проект является ядром всего Aspire. По сути, чтобы Aspire подключить и воспользоваться его фишками, достаточно только добавить этот проект, и прикрутить нужные нам зависимости. Сам проект состоит всего из одного файла (не считая appsettings.json), со следующим содержимым:

// Program.cs
var builder = DistributedApplication.CreateBuilder(args);

var apiService = builder
  .AddProject<Projects.TestAspire_ApiService>("apiservice");

builder.AddProject<Projects.TestAspire_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService);

builder.Build().Run();

Недурно, верно? За плату в 10 строк и 1 проект, мы получаем набор невероятных фичей, работающих из коробки, про которые я писал ранее (админка, телеметрия, и т.д).

Здесь важна именно регистрация сервисов:

var apiService = builder
  .AddProject<Projects.TestAspire_ApiService>("apiservice");

builder.AddProject<Projects.TestAspire_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService);

Чтобы добавить проект, достаточно вызвать одни метод расширения. Строка в методе - это название сервиса либо контейнера если приложение будет докеризированно. Для Api мы просто добавляем проект, но для Blazor - мы дополнительно добавляем референс на apiService - я покажу это дальше, это будет использоваться для Service Discovery .

Что меня достаточно впечатлило, это возможность легкого поднятия нужной инфраструктуры. Например:

// Качаем доп нугет пакет - Aspire.Host.Postgres
// Есть еще подобные для Kafka, Redis, и другой инфры
builder.AddPostgres("database").WithPgAdmin();

Т.е фактически, теперь не надо хранить кучу docker-compose.yaml с инфраструктурой. Не надо писать shell скрипты или makefile'ы чтобы их запускать. Не нужно ничего запускать вручную. Не надо париться насчет volume'ов, network'ов и прочих вещей между контейнерами, когда ты к примеру подключаешь локально тот же Postgres. Не нужно настраивать env переменные контейнеров. Захотел к постгресу подключить pgadmin - сделал это в одну строку, без конфигурации с оригинальным докером.

И все работает мать его локально. Из коробки. С докером и инфрой.

Это. Просто. Топ.

ServiceDefaults

Этот проект - добавляет некие "платформенные" extension методы, которые подключают одинаковые инфраструктурные библиотеки во все проекты внутри Solution'а. По умолчанию при генерации темплейта, там настроен сбор телеметрии. В случае, если надо добавить какой-то особый коллектор (например Prometheus), то этот код добавляется именно туда.

Метод builder.AddServiceDefaults(); добавляет все нужные зависимости в executable проекты.

ApiService

Обычное webapi приложение для погоды, из темплейта. Не отличается вообще ничем от дефолтного, кроме как добавлением ServiceDefaults:

// Program.cs -> ApiService
...
...

builder.AddServiceDefaults();

...
...

Web

Обычное Blazor приложение. Точно также, как и для ApiService, все что в проекте меняется по отношению к дефолтному такому же темплейту но без Aspire это следующие детали:

// Program.cs

// 1
builder.AddServiceDefaults();

// 2
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
    // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
    // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
    client.BaseAddress = new("https+http://apiservice");
});

Здесь изменение не одно, а два:

  1. Добавляем дефолтные сервисы (телеметрию, настройки) из ServiceDefaults

  2. Добавляем http клиента на ApiService, но в особом формате адреса - https+http://apiservice. Это позволит воспользоваться Service Discovery - не хардкодить url'ы между проектами. Aspire сделает это автоматически.

Админка стандартного Blazor приложения. Дезигн конечно неоч, но зато работает
Админка стандартного Blazor приложения. Дезигн конечно неоч, но зато работает

Дашборд Aspire

Больше всего в разработке я обожаю две вещи: Developer Experience и красивый визуал. В обоих этих пунктах я всегда стараюсь дотошно выстраивать мой флоу разработки - я сильно запариваюсь на ранних стадиях проектов, чтобы их можно было легко тестировать и дебажить локально.

И готовая админка, которую тебе не надо поднимать и как-то там настраивать - это прям бальзам на душу.

Покажу основные страницы куда точно стоит зайти. Начнем с основной:

Resources

Страница ресурсов - тут показываются все контейнеры приложения
Страница ресурсов - тут показываются все контейнеры приложения

Здесь у нас список всех контейнеризованных приложений. Тут могут быть как .NET приложения, так и обычные имаджи, например тот же Postgres или Kafka. Тажке можно фронт запускать, все что запускается через npm - например тот же Vue на vite или Next.js.

У каждого приложения есть статус, описание, а также ссылки на просмотр логов, деталей по контейнерам и на endpoint (например на swagger). Достаточно удобно.

Сюда еще много что хотят прикрутить, например видел пропозал на кнопку для рестарта сервисов, если они упали. (Ссыль на issue)

Traces

Мое любимое - трейсинг. Причем, выглядит даже лучше чем в Jaeger'е по умолчанию
Мое любимое - трейсинг. Причем, выглядит даже лучше чем в Jaeger'е по умолчанию

Есть страница с тресами - причем заметьте, оно отлично работает в совокупе! На скрине как раз показан пример с трейсами покрывающими сразу два сервиса - фронт и вебапи. Причем хочу заметить, сама страница выглядит неплохо, на мой личный взгляд сильно красивее чем дефолтная страница Jaeger'а.

Structured logs

Страница с структурными логами
Страница с структурными логами

Точно также есть инфа по логам - плюс хочу обратить внимание, в проекте по умолчанию настроена привязка логов к трейсам - и по некоторым логам, которые относятся к запросам можно вытянуть и посмотреть на трейс

Metrics

Страница с метриками. Прикольно что для дефолтных метрик тут собирается описание
Страница с метриками. Прикольно что для дефолтных метрик тут собирается описание

Метрики могут быть как свои, так и дефолтные. Дефолтных метрик достаточно много - и данные по GC, и по локам, по kestrel'у, много по чему.

Это только начало...

То что меня поразило больше всего - это только Preview проекта. Он еще даже не в релизе! Сам проект развивается очень активно, сторонние контрибьюторы во всю активно предлагают улучшения.

Если это всего лишь превью, то мне даже страшно представить чего ждать от релиза. И я если честно не представляю, как другие платформы собираются конкурировать с дотнетом по удобству разработки (кроме наверное разве что Vercel - они тоже невероятно крутые штуки делают, там удобство разработке как минимум не уступает)

В любом случае, настоятельно рекомендую попробовать Aspire. Это одна их тех вещей, которая реально получилась хорошо, и мне кажется сможет сильно поменять взгляд на то, как должна выглядеть платформенная разработка.

(UPD: Добрые люди в комментах подсказали, что оказывается он уже релизнулся, 2 недели назад, первый GA релиз можно найти здесь)

Комментарии (36)


  1. Zohan_LV
    02.06.2024 10:45

    если честно, из статьи я понял как круто она помогает для локальной разработки & дебага, а что по другим аспектам этой истории? т.е. для всякой статистики есть куда более продвинутые тулы, для деплоймента та же история...


    1. default_g00se Автор
      02.06.2024 10:45
      +3

      Хороший вопрос. Касаемо деплоя - как мне кажется опыт будет напоминать деплой через Vercel - да, вроде как есть другие облака, можно настроить по-своему CI-CD, но при этом тебе вообще ничего настраивать не нужно, не нужно шарить в инфре. Как минимум для раскатки и тестирования MVP звучит как очень удобная штука

      По поводу телеметрии - в целом мне кажется дашборд как раз и предназначен для локальной разработки, и преследует 3 цели:

      1. Не надо настраивать докер-копоузы
      2. Не надо поднимать достаточно жирные коллекторы и вьюверы телеметрии, Aspire делает все в хост процессе без поднятия считай 3-4 контейнеров
      3. Автоматически настроенные, базовые, но +- полезные дашборды.


  1. Hovanella
    02.06.2024 10:45
    +2

    Вроде уже релиз был? А то в статье написано , что ток ревью.


    1. default_g00se Автор
      02.06.2024 10:45
      +1

      Щас глянул, вы правы, буквально 2 недели назад выпустили первый GA релиз - https://github.com/dotnet/aspire/releases


      Спасибо, добавил приписку в конце статьи


  1. mikegordan
    02.06.2024 10:45

    aggregation join например от 2x api за 1 запрос из коробки завезли?


    1. mikegordan
      02.06.2024 10:45

      никто так и не ответил, походу не завезли )


      1. VanKrock
        02.06.2024 10:45
        +1

        Кажется никто не понял о чём речь, 2 разных запроса от разных сервисов, пишешь ручку, она дёргает запросы, аггрегирует и возвращает результат


  1. Sorcer
    02.06.2024 10:45

    Попробывал этот аспире. Быстро и выглядит красиво - да, но капнув поглубже..Этот аспире пока, ток для мелких проектов. Отдельно сервис не перезапустишь. Изменения в одним файле - рестарт всего и вся, включая базы и брокеры.

    Оркестрация на уровне - запустить всё. А если мне надо например 3 проекта из 10?

    Настраивать да, проще, быстрее, веб уи с логами есть. Но если ты настраиваешь такой проект, то логирование через тот ж seq, так же будет. Потом не понятно что он там в докере генерит и если надо кастом, то как это пилить ...


    1. MadL1me
      02.06.2024 10:45
      +1

      Отдельно сервис не перезапустишь.

      Тут согласен. Но разработка ведется прям оч активно

      Касаемо рестарта сервисов - как раз в статье была ссылка - там ишью прям в приоритете сейчас разрабатывается, думаю в ближайшей версии добавят. Тут как раз таки круто, что т.к проект новый, можно набрасывать на официальную репу идеи, и если они годные, то их достаточно быстро будут реализовывать


    1. default_g00se Автор
      02.06.2024 10:45
      +2

      Оркестрация на уровне - запустить всё. А если мне надо например 3 проекта из 10?

      Касаемо оркестрации, самое простое что можно сделать - это например комментить все проекты кроме трех нужных. А еще, если у тебя например есть 2 разных частых конфигурации запуска (конкретных 3 проекта и конкретных 5 проектов) - то можно сделать два разных AppHost проекта под разные конфигурации


      1. asilzhan11
        02.06.2024 10:45
        +2

        Или через args получать нужные проекты и настроить конфигурацию запуска с нужными args


  1. Wesha
    02.06.2024 10:45

    Ну, по крайней мере, хоть сюда "копилота" не вкорячили. Кажется...


  1. prostoandr88
    02.06.2024 10:45

    классная конечно штука ,но я не понимаю как мне обращаться извне к своему api которое запустил через Aspire,он чисто на localhost запускает и что бы я не писал в конфиге он не переключается на локальное ip компа,или так и задумано?


    1. Phoenix-616
      02.06.2024 10:45

      Да, так и задумано. Это набор хаков для разработки с опциональной возможностью деплоиться в Azure и генерировать манифесты для k8s


  1. Maccimo
    02.06.2024 10:45

    Трепещите, жависты!!

    Не понял, с чего бы это?
    Ну появилась у вас какая-то полезная Ынтырпрайз дот шляпа, рады за вас. Но «трепетать»???
    У вас имена методов с прописной буквы начинаются, а имя типа string со строчной.

    Если это всего лишь превью, то мне даже страшно представить чего ждать от релиза.

    Однажды я уже встречал очень восторженного человека. Потом оказалось, что он на антидепрессантах.


    1. MagMagals
      02.06.2024 10:45
      +5

      потому что имя функции и тип объекта это разные все же вещи, и string это алиас от String


    1. Hovanella
      02.06.2024 10:45

      Я до сих пор не могу понять , кто придумал кейс , где самое важное слово в методе - ГЛАГОЛ , пишется с маленькой буквы. Из-за такой и подобных мелочей и выбрал дотнет, а не Джаву.


    1. default_g00se Автор
      02.06.2024 10:45
      +3

      Трепещите, жависты!!
      Не понял, с чего бы это?

      Да ладно вам, это всего лишь шутка была, не воспринимайте это всерьёз :)

      У вас имена методов с прописной буквы начинаются, а имя типа string со строчной.


      Очень странная претензия - если писал код на 3-4 языках, то на синтаксис не обращаешь внимания, а просто к нему привыкаешь и пишешь по конвенции. У разных языков разные конвенции, в этом ничего плохого нет


      1. xxxDef
        02.06.2024 10:45
        +1

        кроме блоков которые обозначаются отступами.


    1. posledam
      02.06.2024 10:45

      А когда в Java завезут async/await на уровне языка? :)


      1. AccountForHabr
        02.06.2024 10:45

        А когда в дотнет завезут green threads?


        1. posledam
          02.06.2024 10:45
          +2

          https://github.com/dotnet/runtimelab/issues/2398

          Вообще грин треды это можно сказать костыль для языков и платформ, у которых уже имеется тонна синхронных библиотек, которые не получится в обозримое десятилетие переписать на асинк.

          В dotnet всё на уровне языка, платформы, core-библиотек и всех современных библиотек поддерживает единый асинхронный API, а также кооперативную отмену, это всё работает очень быстро и эффективно.

          Также есть Channels (как в Go). Есть F# для ФП. Довольно сложно что-то такое найти, чего нет в dotnet или чего не умеет делать dotnet.


          1. AccountForHabr
            02.06.2024 10:45

            Ну т.е. у ms пока не получается да?

            1. Вообще async/await это можно сказать костыль для языков и платформ, которые не смогли в обозримое десятилетие завести green thread и переписали свои библиотеки под async await.

            2. Единообразия кмк мало в Task/ValueTask, да и вы забыли написать что написание асинхронного кода сложнее, и часто провоцирует ошибки, да и само разделения кода на синхронный асинхронный - лишняя сущность.

            3. как в Go нет- go каналы на уровне языка.


            1. mayorovp
              02.06.2024 10:45
              +1

              На кой чёрт вообще нужны каналы на уровне языка, если каналы на уровне библиотеки гораздо более настраиваемы и расширяемы?


              1. AccountForHabr
                02.06.2024 10:45

                Возможно, для того что бы использовать спец синтаксис для каналов.


            1. VanKrock
              02.06.2024 10:45

              Нет, просто это не нужно в dotnet, там есть инструменты гораздо лучше, те же Task


              1. AccountForHabr
                02.06.2024 10:45

                Чем лучше то?


            1. posledam
              02.06.2024 10:45

              Вы по ссылке-то переходили? В dotnet-е в рамках эксперимента сделали настоящие грин треды, и сравнили с асинхронной моделью. Оказалось, что грин треды работают хуже, и от грин тредов больше проблем, чем хоть какой-то видимой пользы.

              https://github.com/dotnet/runtimelab/blob/bec51070f1071d83f686be347d160ea864828ef8/docs/design/features/greenthreads.md

              Единообразие как раз таки одно из важнейших преимуществ. Вместо того, чтобы жить с разными конкурирующими Promise/Future/etc. библиотеками, которые не совместимы друг с другом, что является деградацией, тут единое эффективное API, на котором построено вообще всё.

              Именно async/await практически нивелирует сложность для рядового разработчика, так как по сути, код выглядит и ведёт себя как синхронный, но парочкой потоков может выполнять сотни, тысячи задач. Мы можете спокойно запускать огромное количество тасков, и не париться за производительность, или что потоков не хватит.

              Разделение кода на синхронный/асинхронный происходит очевидным образом, если вам нужно использовать вызов асинхронного метода, значит ваша функция становится асинхронной. Это ни какая-то скрытая лютая магия, всё прозрачно, при этом сложностей не добавляет, только несколько лишних букв. Стоит признать, что это приводит к засилью async/await слов в коде.

              Насчёт каналов, в Go каналы это единственный способ взаимодействия с горутинами, ещё бы они не были на уровне языка. Это не фича, это потребность. В dotnet-е коммуникацию можно обеспечить разными способами, в том числе полностью аналогичным образом, как в Go. При чём Channels в dotnet более развитые, чем в Go.


  1. mister_m0j0
    02.06.2024 10:45
    +2

    А в чем прикол с lock statement pattern?


    1. default_g00se Автор
      02.06.2024 10:45

      То что это "Proposal Champion", который пилится оч активно, а по факту - это еще одна супер бесполезная фича которая сильнее засахаривает язык. Сахар ради сахара, вместо того чтобы делать полезные фичи которые ждет комьюнити (например те же трейты "shapes and extensions")


      1. VladD-exrabbit
        02.06.2024 10:45
        +3

        Это вовсе не бесполезная фича.
        Из-за того, что в дотнете каждый объект может служить мьютексом, приходится выделять дополнительные байты в заголовке каждого объекта для обслуживающей информации: номер потока, держащего блокировку, и текущее количество рекурсивных блокировок (подробнее тут). Чтобы изгнать эту в обычном случае ненужную информацию из объекта, нужно для начала запилить специальный тип объекта, который таки будет служить мьютексом.
        Вот этим и занимается данный proposal.


  1. sdramare
    02.06.2024 10:45
    +1

    Я, честно говоря, концептуально не понимаю этот проект. В реальности написанием кода и разворачиванием инфраструктуры занимаются абсолютно разные люди. И инфра это свой отдельный мир со тераформами, ансиблами, сайдкарами и прочим скриптами. Зачем им этот фреймворк - не понятно. Так же как не понятно зачем он дев команде, которая пилит бизнес фичи, а не занимается управлением докер кластера. По сути это выглядит применимым только для мелких проектов, которые делает один человек-оркестр. Но на мелких проектах и докер-компос/деплоймент файл написать не проблема, тут фреймворк не нужен.


    1. VanKrock
      02.06.2024 10:45
      +1

      На самом деле это не всегда так, тут зависит от компании, есть компании где есть отдельные devops, есть компании, где команда должна сама настраивать выкатку своего сервиса от и до


  1. granit1986
    02.06.2024 10:45
    +1

    Как я понял - в случае, когда у нас у каждого сервиса свой реп и солюшен - Aspire будет бесполезен?


    1. default_g00se Автор
      02.06.2024 10:45
      +1

      Я бы так не сказал. Во первых, можно сделать по AppHost'у на каджый сервис в репе - можно будет хотя бы для 1 сервиса, получать плюхи аспайра, например для поднятия инфраструктуры

      Но еще, есть возможность сделать монорепу на гит сабмодулях - и там уже сделать верхнеуровневый солюшн, в который можно запихать нужный сет проектов, для их запуска


    1. xxxDef
      02.06.2024 10:45

      Вот да, как по мне это самый главный вопрос, дает ли этот aspire возможность поднять все, если этотвсе лежит в разных репах и солюшенах.

      Как обычно в маленьких примерах все выглядет красиво, а вот примеров из реальной жизни чо то как то не наблюдается