LoginSignup
8
2

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

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


この記事は .NET Aspire に搭載されているダッシュボードではなく、Prometheus、Jaeger、Grafana を使う記事です。

開発環境

  • Windows 11
  • Visual Studio 2022 17.10.0 Preview 1.0
  • Docker Desktop 4.26.1 (131620)

使用するソースコード

Visual Studio を立ち上げると .NET Aspire のプロジェクトテンプレートが4つあります。今回はそのうちの1つ .NET Aspire Starter Applicaiton を使います。

image.png

Preview 3 から Starter Applicationの選択すると、Redis Cacheを Docker Containerで使用するかを選択できるようになりました。今回はチェックを入れて選択しておきます。

image.png

もし Mac 環境のように Visual Studio を使用しない場合は dotnet コマンドで Starter Application を作成します。

dotnet new aspire-starter -o starterapp --use-redis-cache true

dotnet コマンドで .NET Aspire アプリケーションをプロジェクトテンプレートから新規作成する場合は Asipre Workload を事前にインストールしておく必要があります。詳しくはこちらをご確認ください。

Install .NET Aspire

この Starter アプリケーションの中身は webfrontend を Blazor Web App, backend が ASP.NET Core APIで構成してあります。さらに webfrontend では .NET 8 から使用可能になった 外部 Redis を使った出力キャッシュを使用するようにしました。実はこの Starter アプリケーションで構築した内容は、手動で構築した前回 .NET Aspire をデプロイすると前々回 .NET Aspire を使ってみるで手動で構築した結果とほぼ同じものです。

Prometheus を Setup する

セットアップは3段階で行います。

  1. Exporter, Endpoint公開の設定
  2. Prometheus の設定用 yaml ファイルの作成
  3. Prometheus コンテナの起動設定

1. Exporter, Endpoint公開の設定

まず Prometheus 用のライブラリのインストールが必要です。ServiceDefault プロジェクトの Nuget パッケージマネージャーを開いて、OpenTelemetry.Exporter.Prometheus.AspNetCore を選択してインストールしてください。

インストールするバージョンに注意してください。
2024年4月現在、OpenTelemetry.Exporter.Prometheus.AspNetCore はまだ Stable バージョンがリリースされていません。最新版を使用するとうまく動かないことがあります。そのような時は Nuget Gallery を参照して一つ前のバージョンを確認し、インストールするようにしてください。
Nuget Gallery | OpenTelemetry.Exporter.Prometheus.AspNetCore

dotnet add package --prerelease OpenTelemetry.Exporter.Prometheus.AspNetCore

image.png

次に Prometheus 用の Exporter を実装して、さらに Prometheus からアクセスするためのエンドポイントを公開する実装をします。といっても、ServiceDefaults に Prometheus 用のテンプレートコードがコメントアウトされた状態で実装済みなので、ただコメントアウトを外せば実装は終わりです。ServiceDefaultsプロジェクトの Extensions.cs の次の箇所です。

Extensions.cs
    private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
    {
        var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

        if (useOtlpExporter)
        {
            builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
            builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
            builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
        }

        // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
+       builder.Services.AddOpenTelemetry()
+           .WithMetrics(metrics => metrics.AddPrometheusExporter());
-       // builder.Services.AddOpenTelemetry()
-       //    .WithMetrics(metrics => metrics.AddPrometheusExporter());

        // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package)
        // builder.Services.AddOpenTelemetry()
        //    .UseAzureMonitor();

        return builder;
    }

...

    public static WebApplication MapDefaultEndpoints(this WebApplication app)
    {
        // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
+       app.MapPrometheusScrapingEndpoint();
-       // app.MapPrometheusScrapingEndpoint();

        // All health checks must pass for app to be considered ready to accept traffic after starting
        app.MapHealthChecks("/health");

        // Only health checks tagged with the "live" tag must pass for app to be considered alive
        app.MapHealthChecks("/alive", new HealthCheckOptions
        {
            Predicate = r => r.Tags.Contains("live")
        });

        return app;
    }

上のコメントを外すと Prometheus の Exporeter が有効になります。下のコメントを外すと Prometheus からアクセスしてもらうためのエンドポイントが公開されます。Prometheus はメトリクスを対象システムから Pull するモデルなのでエンドポイントの公開をしています。

2. Prometheus の設定用 yaml ファイルの作成

本当に、心から、とても残念なことに Prometheus は 設定用 yaml ファイルの作成が必須です。全部コードで書けないと .NET Aspire の便利さが薄れてしまうのですが仕方ありません。

まず AppHost プロジェクトのプロジェクトファイルがある場所より1つ上の階層に prometheus フォルダを作成します。

image.png

次にそのフォルダの中に prometheus.yml というファイルを作ります。

prometheus\prometheus.yml
global:
  scrape_interval: 1s # makes for a good demo

scrape_configs:
  - job_name: 'metricsapp'
    static_configs:
      - targets: ['host.docker.internal:5433','host.docker.internal:5004'] # hard-coded port matches launchSettings.json

この yaml ファイルの中の 5433, 5004 というポート番号ですが、これは .NET Aspire Starter プロジェクトの Blazor プロジェクトと WebAPI プロジェクトのポート番号です。つまり環境によって異なります。それぞれのプロジェクトの Properties\launchSettings.json を開いて、 profile が http の applicationUrl に記載されている Url の ポート番号を確認してそれを使用するように変更してください。5433, 5004はあくまでも私の環境でのポート番号ですので、そのままコピペでは動きません。

3. Prometheus コンテナの起動設定

最後に Prometheus のコンテナを立てて、コンテナ内の Prometheus がymlファイルを読み込めるようにします。AppHostプロジェクトの Program.cs を開き、次のように追加します。

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

var cache = builder.AddRedisContainer("cache");

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

builder.AddProject<Projects.AspireSampleApp_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

+ builder.AddContainer("prometheus", "prom/prometheus")
+    .WithBindMount("../prometheus", "/etc/prometheus")
+    .WithEndpoint(9090, hostPort: 9090);

builder.Build().Run();

AddContainerメソッドの第一引数には自分で好きな名前を付けます。第二引数が Image 名です。どこの Container Registry を使っているのか確認できていないのですが、おそらく Docker Hubだと思われます。(mcr.microsoft.comには見当たらなかったので)

WithBindMount でメソッドの第一引数がマウントするローカルマシン側のパスで、第二引数がコンテナ内部でそのマウント先を使用するためのパスです。つまり Prometheus は先ほど作成した prometheus.ymlファイルを /etc/prometheus/prometheus.yml というパスで読み込むことができます。 Docker に慣れている人であれば Bind Mount のことだな、とすぐお分かりいただけるでしょう。

WithVolumeMount メソッドによる Docker の BindMount は .NET Aspire Preview 4 で WithBindMount メソッドに分割されました。WithVolumeMount メソッドは Docker のボリュームを使用する VolumeMount のみの機能になりました。

WithEndpoint メソッドの第一引数は コンテナ側のポート番号で第二引数はローカルマシン側のポート番号です。docker コマンドの -p オプションとは左右の順番が逆なので注意してください。

WithServiceBinding メソッドは.NET Aspire Preview 3でWithEndpoint に名称変更されました。

4. 実行してみる

では起動してみましょう。Resources に Prometheus のコンテナがポート 9090 で立ち上がっていることが分かります。

image.png

Docker Desktopを見ると、Prometheusコンテナがいますね。Redis コンテナは Starter プロジェクトで元からセットアップしてあるので一緒に立ち上がります。

image.png

webfrontend を開いて、画面を適当に遷移したり、リロードしてから http://localhost:9090 を開きます。適当なメトリクスを選ぶとこのようにデータが表示されます。

image.png

Jaeger を Setup する

Prometheus はメトリクスだけなので、ログ・トレースの収集は別の仕組みが必要です。いくつかの有名なツールがありますが、Go言語で作られた Jaeger を使ってみましょう。
セットアップは2段階で行います。

  1. Exporter の設定
  2. Jaeger コンテナの起動設定

1. Exporter の設定

既定でダッシュボード向けの Exporter が設定されていますが、今回はその設定には触らず Jaeger 用に Exporter を設定しましょう。次の一行を加えるだけです。

Extensions.cs
    private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
    {
        var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

        if (useOtlpExporter)
        {
            builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
            builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
            builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());
        }

        // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package)
        builder.Services.AddOpenTelemetry()
+          .WithTracing(configure: tracing => tracing.AddOtlpExporter(configure: options => options.Endpoint = new Uri("http://localhost:4317")))
           .WithMetrics(metrics => metrics.AddPrometheusExporter());

        // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package)
        // builder.Services.AddOpenTelemetry()
        //    .UseAzureMonitor();

        return builder;
    }

Endpoint として指定している http://localhost:4317 は Jaeger が gRPC でデータを受け付けるポートとして指定されているものなので、変更できません。Jaeger が公開・受付するポートの一覧は次をご確認ください。

Jaeger - Getting Started

2. Jaeger コンテナの起動設定

Jaeger のコンテナを立てて画面とデータを受け付けるためのポートを公開します。AppHostプロジェクトの Program.cs を開き、次のように追加します。

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

var cache = builder.AddRedisContainer("cache");

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

builder.AddProject<Projects.AspireSampleApp_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.AddContainer("prometheus", "prom/prometheus")
    .WithBindMount("../prometheus", "/etc/prometheus")
    .WithEndpoint(9090, hostPort: 9090);

+ builder.AddContainer("jaeger", "jaegertracing/all-in-one")
+    .WithEndpoint(16686, hostPort: 16686)
+    .WithEndpoint(4317, hostPort: 4317);

builder.Build().Run();

16686ポートを公開していますが、これは Jaeger の画面を開くためです。4317については上で説明しましたね。

3. 実行してみる

では起動してみましょう。Resources の Jaeger 行があると思います。Details 列の View をクリックすると下に詳細が表示されて、ポート 16686, 4317を公開した状態 で立ち上がっていることが分かります。

image.png

Docker Desktop もみてみましょう。 Jaeger コンテナが立ち上がっていますね。

image.png

今回も webfrontend を開いて、画面を適当に遷移したり、リロードしてから http://localhost:16686 を開きます。Service に webfrontend, Operation に GET を選択すると余計なデータが表示されなくて見やすいでしょう。 /weather にアクセスした履歴があります。この行をクリックすると・・・

image.png

綺麗にトレースが表示されました。いい感じです。localhost:56416 への GET と SETX は Redis へのアクセスですね。

image.png

Redis へアクセスしたトレースをクリックすると、詳細が確認できます。

image.png

Grafana を Setup する

Grafana は Prometheus と  Jaeger で収集したデータを格好よく見やすくしてくれるダッシュボードを作ることができます。なので、.NET Aspire とは無関係にコンテナを立ち上げて設定すればいいのですが、せっかくなので.NET Aspire 管理でやってみましょう。

素の Grafana を立ち上げる

AppHost プロジェクトの Program.cs に次のコードを追加します。

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

var cache = builder.AddRedisContainer("cache");

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

builder.AddProject<Projects.AspireSampleApp_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.AddContainer("prometheus", "prom/prometheus")
    .WithBindMount("../prometheus", "/etc/prometheus")
    .WithEndpoint(9090, hostPort: 9090);

builder.AddContainer("jaeger", "jaegertracing/all-in-one")
    .WithEndpoint(16686, hostPort: 16686)
    .WithEndpoint(4317, hostPort: 4317);

+ builder.AddContainer("grafana", "grafana/grafana")
+    .WithEndpoint(containerPort: 3000, hostPort: 3000);

builder.Build().Run();

.NET Aspire でコンテナを扱うのもそろそろ慣れてきましたね。.NET Aspire でコンテナ管理をするとイメージのPullからコンテナ起動・削除してくれるはかなり便利です。

起動して http://localhost:3000 にアクセスし、admin/admin でログインします。もしアクセスしても何も表示されない場合はリロードを繰り返すと表示されると思います。初期表示がちょっと重いです。

image.png

初期画面が表示されます。まずデータソースの設定が必要です。Prometheus と Jaeger のそれぞれ1つずつについて設定しましょう。DATA SOURCES をクリックします。

image.png

Prometheus をクリックします。

image.png

Connection の URL に http://host.docker.internal:9090 と入力します。  localhost ではないので注意してください。

image.png

画面の一番下で Save&Test をクリックします。このように Successfully queried the Prometheus API. と表示されれば接続成功です。

image.png

では続けて Jaeger もセットアップしましょう。左サイドバーに Add new connection リンクがあるのでクリックします。検索ボックスに Ja と入力すれば Jaeger が見つかるはずです。クリックします。

image.png

Add new data source をクリックします。

image.png

HTTP の URL に http://host.docker.internal:16686 と入力します。やはり  localhost ではないので注意してください。

画面の一番下で Save&Test をクリックします。このように Data source connected and services found. と表示されれば接続成功です。

image.png

ではダッシュボードでデータを軽く見てみましょう。Home に戻り、DASHBOARDS をクリックします。

image.png

作業を始める前にまずダッシュボードを保存しておきましょう。フロッピーディスクアイコンをクリックして保存します。(保存を意味するアイコンとしてフロッピーディスクって、今時どうなんでしょうね...)

image.png

image.png

Add Visualization をクリックします。

image.png

Prometheus を選択します。

image.png

Metricsで何か選びます。ここでは scrape_duration_seconds を選びました。選んだら、 Run queries ボタンをクリックすると、上部にグラフが表示されます。時間幅を選んでいい感じにしたら、右上のApplyをクリックします。

image.png

メトリクスは無事に表示されました。次にトレースをみてみましょう。右上の Add → Visualization をクリックします。

image.png

トレースの一覧を表示させたいので、表示形式を変えます。右上の Time series をクリックします。

image.png

Table を選択します。

image.png

Data source に jaeger を指定して、Query Type を Search、Service Name を webfrontend、Operation Name に GET を指定したら、右上の Refresh dashboard のアイコンをクリックします。

image.png

するとこのようにトレースが一覧で表示されます。もし表示されない場合は表示期間を調整してください。古いトレースが表示されずにデータがない場合がよくあります。右上の Apply をクリックします。

image.png

ダッシュボードが表示されます。今追加したばかりのトレースの一覧から、/weather へのアクセスをしている行の先頭のリンクを別タブで開いてみましょう。

image.png

トレース情報が表示されました。

image.png

デザインを読み込み・保存できるようにする

今のままでは、実行を停止すると Grafana のダッシュボードがリセットされてしまいます。データソースとダッシュボードはファイル形式で保存することができますので、それを立ち上げ時に読み込むようにした方が現実的です。

まず、AppHost プロジェクトに追加した Grafana の設定を修正します。

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

var cache = builder.AddRedisContainer("cache");

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

builder.AddProject<Projects.AspireSampleApp_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.AddContainer("prometheus", "prom/prometheus")
     .WithBindMount("../prometheus", "/etc/prometheus")
     .WithEndpoint(9090, hostPort: 9090);

builder.AddContainer("jaeger", "jaegertracing/all-in-one")
     .WithEndpoint(16686, hostPort: 16686)
     .WithEndpoint(4317, hostPort: 4317);

var grafana = builder.AddContainer("grafana", "grafana/grafana")
+     .WithBindMount("../grafana/config", "/etc/grafana")
+     .WithBindMount("../grafana/dashboards", "/var/lib/grafana/dashboards")
     .WithEndpoint(containerPort: 3000, hostPort: 3000);

builder.Build().Run();

AppHost プロジェクトの1つ上の階層に grafana というフォルダを作って、そこに設定ファイルを格納する想定です。フォルダを作ります。

image.png

そして便利なサンプルがあります。.NET Aspire のサンプルページに ASP.NET Core 用の Grafana のサンプルがあるので、その grafana フォルダの中身をそっくり grafana フォルダにコピーします。
https://github.com/dotnet/aspire-samples/tree/main/samples/Metrics/grafana

フォルダ構成がややこしいのですが、このようになります。

image.png

それでは再び実行してみましょう。http://localhost:3000 にアクセスし、admin/admin でログインします。かっこいいダッシュボードが表示されます。

image.png

残念なことにこのサンプルには Jaeger のトレースを一覧を表示する機能がないので、上記手順を参考に追加してみてください。

まとめ

.NET Aspire では OpenTelemetry のセットアップが完了していますので、使いたい AMP の Exporter を公開すればすぐに使えることがわかりました。とても便利ですね。また、Docker Desktopを裏側で使ってイメージのダウンロードからコンテナ稼働・削除もしてくれるので環境構築がとても楽です。

.NET Aspire のダッシュボードがものすごく便利なので開発時にはそれを使えばいいのですが、本番環境では使えません。ローカル環境で Prometheus, Jaeger, Grafana のセットアップをして、本番環境へデプロイする前にテストをする場合があると思います。そういった場合にこの記事がお役に立てば嬉しいです。

.NET Aspire Preview 4のリリースとともに、.NET Aspire のダッシュボードは個別にセットアップすることが可能となりました。コンテナでの提供となります。しかし、2024年4月現在、コンテナで稼働するダッシュボードは収集したデータを永続化できないため本番運用はまだ控えた方が良いでしょう。このコンテナで稼働するダッシュボードは 

  • .NET 以外の環境で使用する
  • .NET Aspire を使用できない.NET プロジェクトで使用する

と考えておくことを、2024年4月時点ではお勧めします。詳しくはこちらをご確認ください。
.NET Aspire のダッシュボードを単独で使う

8
2
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
8
2