LoginSignup
1
1

.NET Aspire のダッシュボードを単独で使う

Last updated at Posted at 2024-04-01

この記事は .NET Aspire に関する一連の記事の一部です。

.NET Aspire + Dapr についてはこちらをご覧ください。メインは Dapr についてですが、.NET Aspire を使用する場合についても記載があります。

.NET Aspire のダッシュボード

.NET Aspire は AppHost プロジェクトを起動するとダッシュボードという Web アプリケーションが立ち上がります。

image.png

ダッシュボードでは.NET Aspire 管理下にした.NETアプリケーションやコンテナ、実行ファイルなどのログ、環境変数などの詳細情報や、メトリクス、分散トレースなどのテレメトリデータを簡単に見ることができるため、非常に便利です。

.NET Aspire リリース直後からこのダッシュボードを単独で利用できないか、という issue が上がっていましたが、.NET Aspire Preview 4のリリース時にこの.NET Aspire ダッシュボードを単独で使用できるイメージの公開がアナウンスされました。

Standalone Aspire dashboard sample app

単独で使えるということは、.NET Aspire で管理していないアプリケーションや、.NET 以外の言語で開発中のアプリケーションでもダッシュボードが使用できるということです。実際の使い方をご紹介します。

ダッシュボードを動かす

.NET Aspire ダッシュボードは Docker イメージとして公開されました。次のコマンドでイメージを Pull して、実際に起動することができます。

docker run --rm -it -p 18888:18888 -p 4317:18889 -d --name aspire-dashboard mcr.microsoft.com/dotnet/nightly/aspire-dashboard:8.0.0-preview.6

18888 と 4317 の2つのポート番号を公開しています。18888 はダッシュボードの Web 画面で 4317 は OpenTelemetery のデータを受け付けます。

http://localhost:18888 を開くと次のようなログイン画面が表示されます。

image.png

入力するトークンはコンテナログに表示されています。Docker Desktop の画面から稼働している aspire-dashboard コンテナをクリックしてログを表示します。

image.png

t= の後ろのトークンをコピーして、ログイン画面に貼り付けてログインすると、ダッシュボードが表示されます。

image.png

オプションに --rm がついていますので、コンテナを停止するとコンテナは削除されます。
この後、このダッシュボードにテレメトリデータを送信しますので、コンテナはそのまま稼働させておいてください。

テレメトリデータをダッシュボードに送信する

ではダッシュボードにテレメトリデータを送信してみましょう。OpenTelemetry のデータを送信するセットアップをする場合、.NET Aspire の サービスの既定値(Service Defaults) プロジェクトを利用するとあっという間です。.NET Aspire Starter アプリケーションを使った方がセットアップ済みなので簡単なのですが、今回は WebApi プロジェクトを作成して Service Default プロジェクトを適用することにします。

開発環境

  • Windows 11
  • Visual Studio 2022 17.10.0 Preview 6

WebApi プロジェクトを作成し、OpenTelemetry をセットアップする

Visual Studio の新しいプロジェクトの作成画面で、ASP.NET Core Web API テンプレートを選択します。右上の検索ボックスで API を選ぶと探しやすいです。

image.png

追加情報画面では、HTTPS 用の構成のチェックボックスを外しておきましょう。

image.png

プロジェクトが作成できたら、実行してみましょう。

image.png

ランダムに生成された天気予報データが json 形式で取得できます。

では、.NET Aspire の サービスの既定値(Service Defaults) プロジェクトを追加します。ソリューションファイルを右クリック→追加→新しいプロジェクト を選択します。

image.png

プロジェクトの種類に「.NET Aspire」と入力すると、.NET Aspire サービスの既定値 がすぐ見つかるはずです。これを選択してソリューションに追加します。

image.png

ソリューションに2つのプロジェクトが存在する状態になります。

image.png

WebApi プロジェクトから、今追加した ServiceDefaults プロジェクトをプロジェクト参照します。ServiceDefaults プロジェクトファイルを WebApi プロジェクトにドラッグ&ドロップしてください。すると、WebApi プロジェクトにプロジェクト参照が追加されます。ちゃんと参照が追加されたかを確認するためには、プロジェクトファイルを選択します。(選択すると自動的に開きます)

WebApi.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\DashboardDemo.ServiceDefaults\DashboardDemo.ServiceDefaults.csproj" />
  </ItemGroup>

</Project>

ProjectReference 要素で サービスの既定値(ServiceDefaults) プロジェクトのパスを参照していることがわかります。

では、WebApi プロジェクトで サービスの既定値(ServiceDefaults) を利用するためのセットアップをします。WebApi プロジェクトの Program.cs を開き、次の2行を追加します。

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

// Add services to the container.

+ builder.AddServiceDefaults();

var app = builder.Build();

// Configure the HTTP request pipeline.

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
});

+ app.MapDefaultEndpoints();

app.Run();

internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

最後にダッシュボードに対して OpenTelemetry のテレメトリデータを送信します。OpenTelemetryに関する設定はすべて環境変数で行うことができます。最低限、次の2つを設定すればOKです。

  • OTEL_EXPORTER_OTLP_ENDPOINT
  • OTEL_SERVICE_NAME

WebApi プロジェクトの appsettings.Deveopment.json を開いて、次のように環境変数を2つ追加します。

WebApi/appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
- }
+ },
+ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
+ "OTEL_SERVICE_NAME": "WebApi"
}

実行します。すると、立ち上げておいたダッシュボードに構造化ログ・メトリクス・トレースが表示されます。

image.png

image.png

image.png

Blazor Web プロジェクトを作成し、トレースを確認する

プロジェクトが1つしかないと、複数スパンについてトレース情報がしっかりと表示されるのかがわかりません。これを確認するために、 Web プロジェクトを作成し、 WebApi を呼び出すようにしてみましょう。

ソリューションファイルを右クリック→追加→新しいプロジェクト を選択します。 ダイアログの検索条件で「Web」を選択し、表示された一覧から Blazor Web App を選びます。

image.png

「Include sample page」にチェックを入っていることを確認して、作成します。

image.png

Web プロジェクトでも サービスの既定値(ServiceDefaults)プロジェクト について WebApi プロジェクトと同様のセットアップをします。サービスの既定値(ServiceDefaults) プロジェクトファイルを Web プロジェクトファイルにドラッグ&ドロップしてプロジェクト参照するようにセットアップしてから、Program.csファイルを開いて次のように2行追加します。

Web/Program.cs
using DashboardDemo.Web.Components;

var builder = WebApplication.CreateBuilder(args);

+ builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

+ app.MapDefaultEndpoints();

app.Run();

appsettings.Development.json ファイルにも WebApi プロジェクトと全く同じ設定をします。ただし、サービス名は「Web」に変更しておきます。

Web/appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
- }
+ },
+ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
+ "OTEL_SERVICE_NAME": "Web"
}

これで OpenTelemetry の準備は終わりました。次に Web プロジェクトから WebApi を呼び出して天気予報データを取得するように実装します。 Web プロジェクトに WeatherApiClient.cs ファイルを追加して、次の実装に入れ替えてください。ただし namespcae は作成時のままにしておいてください。

Web/WeatherApiClient.cs
namespace DashboardDemo.Web;

public class WeatherApiClient(HttpClient httpClient)
{
    public async Task<WeatherForecast[]> GetWeatherAsync()
    {
        return await httpClient.GetFromJsonAsync<WeatherForecast[]>("/weatherforecast") ?? [];
    }
}
public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

今作成した WeatherApiClient を DI する設定を Program.cs に追加実装します。

Web/Program.cs
using DashboardDemo.Web;
using DashboardDemo.Web.Components;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

+ builder.Services.AddHttpClient<WeatherApiClient>(client =>
+ {
+     client.BaseAddress = new Uri("http://localhost:5028");
+ });

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapDefaultEndpoints();

app.Run();

localhost の ポート番号に 5028 を指定していますが、これは WebApi のポート番号です。みなさんの環境では異なる数字のはずですので、適宜修正してください。 WebApi/Properties/launchSettings.json を開き、profiles が http の中にある applicationUrl を確認してください。

最後に Weather ページを修正して、WebApi にアクセスしてデータを取得するようにします。次の内容に丸ごと変更します。

Web/Components/Pages/Weather.razor
@page "/weather"
@attribute [StreamRendering]

@inject WeatherApiClient WeatherApi

<PageTitle>Weather</PageTitle>

<h1>Weather</h1>

<p>This component demonstrates showing data.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await WeatherApi.GetWeatherAsync();
    }

}

これで実装はおしまいです。今のままだと実行しても WebApi プロジェクトしか起動しませんので、WebApi プロジェクトが起動した後に Web プロジェクトが起動するようにスタートアップ設定を変更します。

ソリューションファイルを右クリック→スタートアップ プロジェクトの構成 を選択します。表示されたダイアログで、次のように設定します。

  1. マルチスタートアップ プロジェクトを選ぶ
  2. WebApi プロジェクトが1番上になるようにして、アクションに開始を選ぶ
  3. Web プロジェクトが上から2番目になるようにして、アクションに開始を選ぶ

image.png

実行してみましょう。WebApi と Web ページが立ち上がったはずです。Weather ページを開いてみましょう。

image.png

問題なく天気予報データが取得できています。では、ダッシュボードを見てみましょう。トレース画面を見てみると、Web と WebApi のトレースがあります。 詳細列の表示リンクをクリックします。

image.png

トレースが各スパンと共に表示されています。良い感じですね。

image.png

Azure Container Apps でダッシュボードを動かす

ダッシュボードを本番環境で運用することはできるでしょうか。先に結論を言うと、可能です。状況によっては便利かもしれません。まずはやってみましょう。

まず、.NET Aspire Starter アプリケーションを何も修正せずそのまま Azure Container Apps にデプロイします。すると、webfrontend と apiservice という2つのアプリケーションが稼働します。

image.png

この2つアプリケーションそれぞれについて、環境変数を次のように設定します。

  • OTEL_EXPORTER_OTLP_ENDPOINT: http://dashboard:4317
  • OTEL_SERVICE_NAME: webfrontend または apiservice

http://dashboard となっているのは、これからデプロイするダッシュボードのアプリ名を dashboard にするからです。

webfrontend
image.png

apiservice
image.png

ここに、ダッシュボードをデプロイします。コンテナアプリの作成画面で、コンテナアプリ名を dashboard と入力して、コンテナーをクリックします。

image.png

次のように選択・入力してからイングレスをクリックします。

  • イメージのソースを「Docker Hub またはその他のレジストリ」
  • レジストリログインサーバーを mcr.microsoft.com
  • イメージとタグを dotnet/nightly/aspire-dashboard:8.0.0-preview.6

image.png

 次のように選択・入力してから確認・作成をクリックします。

  • イングレスを「有効」
  • イングレス トラフィックを「どこからでもトラフィックを受け入れます」にする
  • クライアント証明書モードを「無視」
  • ターゲットポートを 18888
  • 追加のTCPポートでターゲットポートを 18889、公開されたポートを 4317

image.png

デプロイが成功したら、ダッシュボードを開きます。ログインを求められますので、dashboard アプリのリビジョンからコンソールログを開きます。

image.png

トークンをコピーして、ログインします。その後、Webfrontend を開いて Weather ページに何度かアクセスしてみましょう。

問題なく、ログ・トレース・メトリクスが表示されます。
image.png

Preview 4でのリリース直後は動作が不安定でしたが、Preview 6では安定して情報が表示されるようになりました。
しかし、残念ながらこの方法ではダッシュボードに送信されたテレメトリデータを保存することができません。dashboard コンテナを再起動したら、それまで送信されたテレメトリデータは消えてしまいます。それを理解した上での運用であれば本番環境での運用も良いと思います。

まとめ

.NET Aspire のダッシュボードを単独で動かすことで、ZipKin や Prometheus、Grafana などのツール類をセットアップすることなく、ログ・メトリクス・トレースを画面で簡単に確認することができます。OpenTelemetryは Java, Python, Node.js にも対応していますので、.NET Aspire ダッシュボードは言語を問わず使用可能です。

メインの用途はローカル開発時の状況確認ですが、本番運用でもデータ保存を気にしないのであれば採用できる場合があると思います。今回 Azure Container Apps へのダッシュボードのデプロイ時にはイングレスをインターネット公開しましたが、セキュリティに十分注意するのであれば仮想ネットワーク内部のみからのアクセスのみに限定するのが良いでしょう。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1