.NET Web API × OpenTelemetry でメトリクスを収集し、Prometheus と Grafana で可視化する方法

C#

前回の記事では、.NET Web API のトレース情報を取得し、Jaeger を使って可視化する方法を紹介しました。今回はその続編として、アプリケーションのメトリクスを収集し、Prometheus と Grafana を用いて可視化する手順を解説します。

環境

以下の環境で動作確認を行っています。

oswindows11(4cpu、16GB)
.NET8.0.304
IDEVisual Studio 2022 Community
Jaeger(コンテナ)2.8.0
Docker28.3.0

記事のコードは、解説をわかりやすくするために、エラーチェックなど一部の処理を省略しています。

OpenTelemetryの設定

ASP.NET Core Web API プロジェクトにメトリクスを出力するための設定を行います。

まずは以下のライブラリを NuGet 経由でインストールしてください。

パッケージ名概要
OpenTelemetry.Extensions.Hosting (1.12.0)DI(依存関係注入)を通じた OpenTelemetry の構成用拡張ライブラリ
OpenTelemetry.Instrumentation.AspNetCore (1.12.0)ASP.NET Core のリクエスト処理(受信)に関するメトリクスを出力
OpenTelemetry.Instrumentation.Http (1.12.0)HttpClient による外部リクエスト(送信)に関するメトリクスを出力
OpenTelemetry.Exporter.Prometheus.AspNetCore(1.12.0-beta.1)Prometheus用のエンドポイントを構成するためのライブラリ。※ベータ版
Npgsql.OpenTelemetry (9.0.3)PostgreSQL(Npgsql)に関するメトリクスを出力

メトリクスの出力設定

Program.cs に以下の設定を追加します。Instrumentation メソッドにより、自動的にメトリクスを取得できるようになります。また、Prometheus に対応するため AddPrometheusExporter と app.UseOpenTelemetryPrometheusScrapingEndpoint を追加します。これにより、Prometheusがメトリクスをスクレイピングするためのエンドポイントを構築できます。

builder.Services.AddOpenTelemetry()
	.ConfigureResource(r => r.AddService(builder.Environment.ApplicationName))
	.WithMetrics(m => m
		.AddAspNetCoreInstrumentation()
		.AddNpgsqlInstrumentation()
		.AddHttpClientInstrumentation()
		.AddPrometheusExporter()
	);

(略)
app.UseOpenTelemetryPrometheusScrapingEndpoint();

また以下の設定を行います。デフォルトではHTTPリクエストはHTTPSにリダイレクトされます。Prometheusがローカル実行しているアプリのエンドポイントにアクセスできるように、開発環境の場合にリダイレクト機能を外します。あくまで開発環境だけの一時的な設定です(詳細な理由は後述)。

if (!app.Environment.IsDevelopment())
{
	app.UseHttpsRedirection();
}

確認

アプリを実行すると、コンソール上に Prometheus 用のエンドポイント URL(例:http://localhost:5139/metrics)が表示されます。この URL にブラウザでアクセスし、メトリクスが表示されれば成功です。

リダイレクトを無効にしていない場合は、https://localhost:7027にリダイレクトされます。

OSSツールの設定(Prometheus / Grafana)

PrometheusとGrafanaをDockerで起動させます。

Prometheus

以下の内容で docker-compose.yml を作成します。設定ファイルをバインドするため ./config ディレクトリを作成します。

services:
  prometheus:
    image: prom/prometheus:main
    container_name: prometheus
    volumes:
      - ./config:/etc/prometheus
    ports:
      - "9090:9090"
    restart: unless-stopped

config/prometheus.yml の内容は以下のとおりです。

scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 10s
    static_configs:
      - targets: ["host.docker.internal:5139"]

コンテナ内からlocalhost:5139にアクセスしても、ホストOSで実行されているPrometheus用エンドポイントにはアクセスできません。そこで、コンテナからホストにアクセスするためのドメインとして用意されている「host.docker.internal」を利用します。

.NET Web API のデフォルト証明書は localhost に対してのみ有効です。そのため https://host.docker.internal:7027 にリダイレクトされると以下の TLS エラーになります。

Error scraping target: Get "https://host.docker.internal:7027/metrics": tls: failed to verify certificate: x509: certificate is valid for localhost, not host.docker.internal

この問題を回避するため、HTTPSリダイレクトを無効化しました。アプリもDockerで動かしている、PrometheusをホストOSに直接インストールしているなど、お互いが問題なく通信できる場合はHTTPSリダイレクトの無効化は不要です。またprometheus.ymlで直接localhostを指定して問題ありません。

コンテナを docker compose up -d で起動させたらlocalhost:9090にアクセスします。その後、「Status > Target health」を確認して、エンドポイントのStateがUPであれば成功です。

Grafana

Prometheus に続いて、Grafana の設定を行います。docker-compose.yml に以下を追加します。通常はデータの永続化を行うためにストレージやローカルディレクトリへのバインド設定を行いますが、本記事では説明を簡略化するため省略しています(Prometheusも同様)。

  grafana:
    image: grafana/grafana:main-ubuntu
    container_name: grafana
    depends_on:
      - prometheus
    ports:
      - "3000:3000"
    restart: unless-stopped

localhost:3000 にアクセスし、初期 ID/Password(admin/admin)でログインします。パスワード変更後、Data Sources > Add Data Source > Prometheus を選択し、以下のように設定します:

URL:http://prometheus:9090 (← Docker Compose 上のサービス名を指定)

これで Grafana 上でメトリクスを可視化できるようになります。ダッシュボードを作成して好きなクエリをグラフ化しましょう。

おまけ

Grafana ではトレース情報も扱うことが可能です。Data Source に Jaeger を追加すれば、Explore タブからトレース情報を確認できます。Prometheus と Jaeger を同じダッシュボードから参照できるのは非常に便利です。

まとめ

.NET アプリケーションのパフォーマンスを定量的に把握するには、トレースとあわせてメトリクスの可視化が重要です。OpenTelemetry は最小限の設定でこれを実現できる強力なツールであり、Prometheus + Grafana との組み合わせはローカル開発環境でも簡単に導入可能です。

コメント