銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

OpenTelemetry .NETを理解する(10) .NET 8 におけるOpenTelemetry 概要

OpenTelemetryに関する記事をしばらく書いていなかったので、改めて .NET 8 がリリースされた時点でのOpenTelemetry への対応についてまとめてみたいと思います。 .NET 7のときはリリースノートにもOpenTelemetry対応が記載されていたのですが、.NET 8では記載されていません。

tech.tanaka733.net

learn.microsoft.com

個人的にはOpenTelemetry .NETに向けて、 .NET Runtime、SDK側での大きな対応はもう不要ということではないかと思っています。また、OpenTelemetryに関して .NET 側のドキュメントでも記載されるようになりました。そこで、これらのドキュメントを中心に .NET 8におけるOpenTelemetry対応をまとめてみることにします。長くなりそうなので、概要・メトリクス・トレース・ロギングの4回に分けて書く予定です。

.NET における監視とOpenTelemetry

OpenTelemetryの位置づけ

まず、OpenTelemetryで収集される情報の位置は、デバッガー、単体テスト、診断ツールと並んで「監視のためのインストルメンテーション」となっています。デバッガーや診断ツールが実際に動いている環境を操作したり、情報を取得したりするのに対し、遠隔からシステムの動いている状況を把握できる=テレメトリーデータの取得を実現する要素となっています。

learn.microsoft.com

.NETにおいてOpenTelemetryを利用した監視については以下に説明されています。

learn.microsoft.com

このドキュメントで改めてデバッグと監視の違いを説明しています。

侵入性があり、アプリケーションの動作に影響を与える可能性があるデバッグとは異なり、監視はプライマリ操作に対して透過的であり、パフォーマンスへの影響が継続的に使用できる程度に十分小さくなるように意図したものです。

.NETにおける監視を実現する方法はいくつかありますが、アプリケーションを再ビルドできるのであればOpenTelemetryのような明示的なコードが最も強力です。また、スタートアップフックによりアプリにアセンブリを挿入しますが、これは以前紹介したOpenTelemetry .NETの自動計装で利用されています。これらに加えて、EventPipeを利用する方法もありこれは dotnet-monitorという診断ツールで利用されています。

tech.tanaka733.net

.NET におけるOpenTelemetryの実装

OpenTelemetryの実装において、.NETが他の言語と異なるのはOpenTelemetryのAPIを利用せずに、.NETのAPIを利用することでOpenTelemetryによる計装が可能になっている点です。

これらのAPIを呼び出すことで、OpenTelemetryのテレメトリーデータとして記録できる作りになっています。

OpenTelemetryの配布については、NuGetパッケージを利用しますが、用途別に分けられたパッケージ構成になっています。OpenTelemetryのコア機能を提供するOpenTelemetryをはじめとし、計装についてはOpenTelemetry.Instrumentation.xxxという名前で提供されています。例えば、ASP.NET Coreの計装はOpenTelemetry.Instrumentation.AspNetCore、HTTPの送信の計装はOpenTelemetry.Instrumentation.Httpとなっています。 OpenTelemetryではテレメトリーデータの送信までが責務で、データの保存や可視化についてはオブザーバビリティバックエンドとよばれるツールやサービスを利用する想定です。そのためバックエンドにあわせた形式で送信するexporterとよばれる機能ごとにNuGetライブラリが提供されています。主にテスト目的でコンソールに出力するOpenTelemetry.Exporter.Console、OTLPプロトコルとよばれるOpenTelemetry標準のフォーマットで送信するOpenTelemetry.Exporter.OpenTelemetryProtocolなどが用意されています。

ドキュメントでは、OSSなオブザーバビリティバックエンドであるPrometheus・Grafana・Jaegerを利用した実装サンプルとAzure MonitorとApplication Insightsを利用した実装サンプルが用意されています。

この中でPrometheusはscrape方式(pull式)なデータ取得に対し、OpenTelemetryでは通常push方式なデータ送信であるため、OpenTelemetry.Exporter.Prometheus.AspNetCoreではASP.NET Coreのエンドポイントを利用してscrapeのためのエンドポイントを提供する実装となっています。また、Azure Monitorが専用のexporterが必要なのに対し、JaegerやNew RelicのようなOTLPプロトコルをサポートするバックエンドではOpenTelemetry.Exporter.OpenTelemetryProtocolを利用し接続先を指定するだけで送信が可能になります。

OpenFeature .NET SDKを試してみた(2) 名前付きクライアント

前回の記事のにコメントをいただいたので名前付きクライアントを試してみました。

tech.tanaka733.net

プロバイダーは一度に1つ登録できるのですが、名前を付けることで複数登録してクライアント取得時に分けることができます。前回の記事では前半だけ書いていたので不正確になっていました。

openfeature.dev

github.com

openfeature.dev

名前付きクライアントを利用するサンプルコードとしては引用したドキュメントの通りなのですが、実際に試すためにflagdのJSONファイルを変えて複数起動し、別々のプロバイダーとして登録しました。

以下をdemo1.jsonとして保存し、

{
  "flags": {
    "myStringFlag": {
      "state": "ENABLED",
      "variants": {
        "key1": "val1",
        "key2": "val2",
        "key3": "val3"
      },
      "defaultVariant": "key1"
    }
  }
}

defaultVariantだけ変えたこれをdemo2.jsonとして保存し、

{
  "flags": {
    "myStringFlag": {
      "state": "ENABLED",
      "variants": {
        "key1": "val1",
        "key2": "val2",
        "key3": "val3"
      },
      "defaultVariant": "key2"
    }
  }
}

コンテナを分けて起動します。

docker run --rm -it --name flagd1 -p 8013:8013 -v ${PWD}:/etc/flagd ghcr.io/open-feature/flagd:latest start --uri file:./etc/flagd/demo1.json
docker run --rm -it --name flagd2 -p 8014:8013 -v ${PWD}:/etc/flagd ghcr.io/open-feature/flagd:latest start --uri file:./etc/flagd/demo2.json

そして、C#コードでは別のProviderとして作成します。

var flagdProvider1 = new FlagdProvider(new Uri("http://localhost:8013"));
var flagdProvider2 = new FlagdProvider(new Uri("http://localhost:8014"));

そしていくつかのパターンで試してみました。

//名前を指定しないとデフォルトのプロバイダーが使われる
var client = Api.Instance.GetClient();
var val = await client.GetStringValue("myStringFlag", "null");
Console.WriteLine(val); //val1

//名前を指定すると指定した名前で登録されたプロバイダーが使われる
client = Api.Instance.GetClient("provider2");
val = await client.GetStringValue("myStringFlag", "null");
Console.WriteLine(val); //val2

//指定した名前が存在しないとデフォルトのプロバイダーが使われる
client = Api.Instance.GetClient("provider3");
val = await client.GetStringValue("myStringFlag", "null");
Console.WriteLine(val); //val1

//同じ名前で別のプロバイダーを登録すると上書きされる
Api.Instance.SetProvider("provider2", flagdProvider1);
client = Api.Instance.GetClient("provider2");
val = await client.GetStringValue("myStringFlag", "null");
Console.WriteLine(val); //val1

GetClient メソッドの引数に指定した名前で登録されたプロバイダーがあれば、それが使われます。指定した名前がない場合はデフォルトのプロバイダーが登録されます。.NET SDKには説明が見つからなかったのですが、OpenFeatureが定めている仕様にはこの動作が書かれており、期待される通りの挙動です。

openfeature.dev

前回はbool型の値、今回はstring型の値を使ったので、次回はOpenFeatureではどんな型とその値を取得するどんな評価APIが用意されているかを確認します。

Radiusを知る(10) v0.29のリリースとロードマップ

Radiusに関してしばらく記事を書いていなかったので最近のRadiusについてまとめようと思います。

v0.29のリリース

最新版は1/6にリリースされたv0.29です。

github.com

ハイライトとしては2点あり、1つはエッジバージョン(edge version)の公開です。最新の機能を取り込んだバージョンをエッジバージョンとして利用できるようになりました。以下のドキュメントから手順を確認できます。

edge.docs.radapp.io

2つ目はdevcontainer機能としてrad CLIが利用できるようになりました。devcontainerやCodespaces にrad CLIとその依存関係を自動的にインストールできるようになります。

github.com

またリリースノートには記載なく、どの時点で更新されたかわかりませんが、Visual Studio Codeで利用するBicepの拡張機能ですが、Radiusを使う場合には本家のBicep拡張機能をアンインストールし、Radius Bicepをインストールするように案内されています。

edge.docs.radapp.io

ロードマップ

ロードマップが公開され、ブログ記事が出ています。

blog.radapp.io

github.com

ロードマップのポイントとしては、

  • 現在の目標と計画を表している
  • 実装予定時期などは公開しない
  • リリースのたびに評価しなおす
  • 現時点ではプロジェクト管理者の優先順位が反映されているが、パートナーが増えるにつれて見直される可能性がある

といったところです。

現時点でのロードマップで個人的に興味があるのは、レシピについては、AzureだけでなくAWSなどさまざまなリソースを対象にすることRadiusで展開する前にすでにプロビジョニングされているリソースに接続できるようにすることRadiusのアプリケーショングラフに関するダッシュボードサーバーレスコンテナランタイムへの対応、といったあたりです。