銀の光と碧い空

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

OpenTelemetry .NETを理解する (9) .NET 7でのOpenTelemetry対応の強化

この記事はOpenTelemetry Advent Calendarの6日目の記事です。

qiita.com

先日リリースされた .NET 7ではObservabilityへの投資がリリースブログに項目にあります。

devblogs.microsoft.com

.NETにおけるOpenTelemetry対応は、.NET本体ではなくOpenTelemetry .NETというプロジェクトが進め、ライブラリとして提供しています。

github.com

それでは.NET本体が行った改善とはどのようなものでしょうか。まずは3点からなる改善内容を確認します。

Activity.Current Change Eventの導入

.NET におけるOpenTelemetry対応が他の言語と異なっている点の1つに、OpenTelemetryのSpanに対応するものとして.NETに従来からあったActivityクラスというものに対応させています。

learn.microsoft.com

そのため、Spanに対する操作をActivityクラスから行うことができます。一方、Spanの追跡には内部的にはAsyncLocalを利用しています。

learn.microsoft.com

Span Contextの変化を追跡する場合、AsyncLocalのコンストラクタ経由でイベントハンドラを設定することができますが、Activity経由で同じことができるようになったのが今回の改善点です。

Activity.CurrentChanged += CurrentChanged;

void CurrentChanged(object? sender, ActivityChangedEventArgs e)
{
    Console.WriteLine($"Activity.Current value changed from Activity:  {e.Previous?.OperationName} to Activity: {e.Current?.OperationName}");
}

パフォーマンスに優れたActivityのプロパティ列挙メソッドの導入

ActivityのTags、Links、Eventsに対してよりパフォーマンスの高いプロパティ列挙メソッドが提供されました。C# 7.2で導入された読み取り専用参照機能を利用しており、余分なアロケーションも発生しません。

void setTag()
{
    var a = Activity.Current;
    a.SetTag("key1", "value1");
    a.SetTag("key2", "value2");

    foreach (ref readonly KeyValuePair<string, object?> tag in a.EnumerateTagObjects())
    {
        Console.WriteLine($"{tag.Key}, {tag.Value}");
    }
}
setTag();

パフォーマンスに優れたActivityLinkとActivityEvent のプロパティ列挙メソッドの導入

ActivityLinkとActivityEventに関しても同様にプロパティを列挙するメソッドが用意されています。

ActivityLink link = new ActivityLink(default, new ActivityTagsCollection(tags));

foreach (ref readonly KeyValuePair<string, object?> tag in link.EnumerateTagObjects())
{
    // 
}

ActivityEvent e = new ActivityEvent("SomeEvent", tags: new ActivityTagsCollection(tags));

foreach (ref readonly KeyValuePair<string, object?> tag in e.EnumerateTagObjects())
{
    //
}

まとめ

実際、今回追加された機能をOpenTelemetry .NETを使う際に利用することはあまりないのではと思います。OpenTelemetry .NET 内部で利用するケースを想定しているのではないでしょうか。.NET 7を含めた最近の.NETはパフォーマンスの改善に大きく投資をしています。OpenTelemetry利用時にもその恩恵を受けられるような機能追加と考えられます。

.NET 7で導入されたビルトインコンテナサポートを試してみた

.NET 7になって .NET SDKだけでコンテナのビルドができるようになりました。

devblogs.microsoft.com

このブログの最初にある通り、Microsoft.NET.Build.Containers というライブラリを追加すると利用できる機能ですが、(temporary)とあるのでそのうち不要になる(SDKなどに組み込まれる)かもしれません。dotnet publish --os linux --arch x64 -c Release -p:PublishProfile=DefaultContainerというようにdotnet publishコマンドだけでコンテナイメージのビルドが実行されます。

% dotnet publish --os linux --arch x64 -c Release -p:PublishProfile=DefaultContainer

MSBuild version 17.4.0+18d5aef85 for .NET
  復元対象のプロジェクトを決定しています...
  復元対象のすべてのプロジェクトは最新です。
  my-awesome-container-app -> /Users/ttanaka/Repos/my-awesome-container-app/bin/Release/net7.0/linux-x64/my-awesome-container-app.dll
  my-awesome-container-app -> /Users/ttanaka/Repos/my-awesome-container-app/bin/Release/net7.0/linux-x64/publish/
  Building image 'my-super-awesome-app' with tags 1.2.3-alpha2,latest on top of base image mcr.microsoft.com/dotnet/aspnet:7.0-alpine
  Pushed container 'my-super-awesome-app:1.2.3-alpha2' to Docker daemon
  Pushed container 'my-super-awesome-app:latest' to Docker daemon

プロジェクトのどこかにDockerfileが作成されるわけでもなく、内部でビルドが行われます。

また、ビルドしたイメージはデフォルトでdocker://のdocker daemonへとpushされます。

ビルドされるイメージをカスタマイズしたい場合、現時点で利用可能な設定がこちらにあります。PropertyGroupに指定するものと、ItemGroupに指定するものがあります。

github.com

例えばこのように指定します。

  <PropertyGroup>
    <ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:7.0-alpine</ContainerBaseImage>
    <ContainerImageName>my-super-awesome-app</ContainerImageName>
    <ContainerImageTags>1.2.3-alpha2;latest</ContainerImageTags>
    <ContainerRegistry>registry.mycorp.com:1234</ContainerRegistry>
  </PropertyGroup>
  • ContainerBaseImage:ビルドするイメージのベースイメージ。デフォルトではプロジェクトの種類に応じて決められる。
  • ContainerImageName:ビルドしたイメージの名前
  • ContainerImageTagあるいはContainerImageTags:ビルドしたイメージに付与するタグ。1つの場合はContainerImageTagで、複数の場合はContainerImageTags。ただし、ContainerImageTagsは後述のバグがあり回避策が必要。
  • ContainerRegistry:pushするレジストリ。今の所認証不要なレジストリのみサポート。

デフォルトでベースとなるイメージはプロジェクトの種類に応じて以下のようになります。

  • ASP.NET Core アプリの場合、mcr.microsoft.com/dotnet/aspnet
  • 自己完結型アプリの場合、mcr.microsoft.com/dotnet/runtime-deps
  • 他のすべてのアプリでは、mcr.microsoft.com/dotnet/runtime

ContainerImageTagsでタグを複数指定したい場合、既知の不具合があるため以下の設定をcsprojに追加して回避します。

  <ItemGroup>
    <!-- Work around https://github.com/dotnet/sdk-container-builds/issues/236 -->
    <ContainerImageTags Include="$(ContainerImageTags)" />
  </ItemGroup>

便利なようですが、いくつか現時点での制約があります。

  • Linux x64アーキテクチャのイメージのみサポート
  • 認証なしのリポジトリへのpushのみサポート

また、run commandの変更はサポートしない方針と記載されています。

以上をふまえると現状ではブログにある通り、ローカル開発環境や認証なしのリポジトリにプッシュするようなCI/CDでの利用がメインとなりそうです。

.NET 7についてまとめてみた

自分の勉強がてらまとめています。公式ブログを日本語で、すぐ目を通せることを目的にしています。この記事では.NET 7のテーマについてまとめています。

devblogs.microsoft.com

.NET 7の新機能

フレームワークごとの新機能はそれぞれ公式ブログより提供されています。

devblogs.microsoft.com

devblogs.microsoft.com

devblogs.microsoft.com

devblogs.microsoft.com

devblogs.microsoft.com

devblogs.microsoft.com

シナリオ

.NET でできることになったことの一例が公式ブログやドキュメントで紹介されています。

ユニファイド

1つの基本ライブラリ

.NET 7 を使用すると、1 つの SDK、1 つのランタイム、1 つの基本ライブラリ セットを使用してさまざまな種類のアプリ (クラウド、Web、デスクトップ、モバイル、ゲーム、IoT、AI) を構築できます。

.NET 7をターゲットにする

プロジェクトで次のように指定すると.NET 7で利用可能なすべてのAPIが利用できます。

<TargetFramework>net7.0</TargetFramework>

これに加えて、以下のターゲットフレームワークモニカー(TFM)を指定すると、OS固有のAPIが利用可能になります。

  • net7.0-android
  • net7.0-ios
  • net7.0-maccatalyst
  • net7.0-macos
  • net7.0-tvos
  • net7.0-windows

ARM64

L3キャッシュサイズの読み取りの改善や、LSEアトミック命令の導入によりランタイムのパフォーマンスを改善した。組み込み関数を利用するライブラリを最適化するため、Vector64, Vector128, Vector256などのクロスプラットフォーム ヘルパーを導入した。これらによるパフォーマンスの改善の詳細については以下のブログを参照。

devblogs.microsoft.com

Linux での .NET サポートの強化

.NET 6 は Ubuntu 22.04 (Jammy) に含まれており、apt install dotnet6 コマンドでインストールできます。さらに、すぐに使用できる最適化されたビルド済みの超小型コンテナーイメージも提供しています。

64 ビット IBM Power のサポート

RHEL8.7およびRHEL9.1をターゲットとするppc64le (64-bit IBM Power) アーキテクチャをサポートしました。

モダン

.NET MAUIが.NET 7に含まれ、Blazorも改善を進めました。

Announcing .NET MAUI for .NET 7 General Availability - .NET Blog

Blazor - .NET Blog

また、.NET Upgrade Assistantにより.NET FrameworkやUWPなアプリを、.NET 6および.NET 7に移行する手助けをしてくれます。

.NET 6への移行

LTSである .NET 6がリリースされてから、Microsoftのサービスを中心に多数のサービスが.NET 6への移行を行い、その記事が公開されています。

クラウドネイティブ

dotnet publishコマンドにより直接コンテナイメージを生成できるなどのコンテナのネイティブサポートの強化、オブザーバビリティ獲得のためOpenTelemetry対応の強化など、クラウドネイティブへの投資を続けています。

devblogs.microsoft.com

シンプル

C# 11

C#11、F# 7が追加されました。

devblogs.microsoft.com

devblogs.microsoft.com

特にC#11では、新機能の静的仮想インターフェイス メンバーも活用したGeneric Mathが導入され、数値演算をもつクラスを定義するのが簡単になります。

learn.microsoft.com

また、エスケープ シーケンスを必要とせずに、空白、改行、埋め込み引用符、およびその他の特殊文字を含む任意のテキストを含めることができる生の文字列リテラル(raw string literal)が導入されています。

.NET ライブラリ

.NET ライブラリにも以下のものを初めてとする機能追加が行われています。

  • Microsoft.Extensions の Null 許容注釈
  • System.Composition.Hostingでコンテナに単一のオブジェクトインスタンスを許可
  • TimeStamp、DateTime、DateTimeOffset、TimeOnly へのマイクロ秒とナノ秒の追加
  • Microsoft.Extensions.Cachingにメトリクスサポートを追加
  • tar.gzを扱うSystem.Formats.Tar API
  • DateOnlyなど新しく追加された型に対応した型コンバーター
  • System.Text.Json コントラクトのカスタマイズ
  • System.Text.Json 型がユーザー定義型階層のポリモーフィック シリアル化と逆シリアル化をサポート

.NET SDK

.NET CLIにパーサーとタブ補完が追加されました。また、.NET テンプレートに制約の概念が追加され、どのテンプレートが表示されるかをユーザー環境に応じて制御することができます。

NuGet

NuGet6.4がリリースされました。

devblogs.microsoft.com

その中の新機能として、多数のプロジェクトがあるソリューションにおいて利用するパッケージのバージョンをソリューションで制御できる、NuGet の中央パッケージ管理機能が追加されました。

devblogs.microsoft.com

パフォーマンス

.NET 7でもパフォーマンス改善が引き続き行わています。

TL;DR: .NET 7 is fast. Really fast. A thousand performance-impacting PRs went into runtime and core libraries this release, never mind all the improvements in ASP.NET Core and Windows Forms and Entity Framework and beyond. It’s the fastest .NET ever. If your manager asks you why your project should upgrade to .NET 7, you can say “in addition to all the new functionality in the release, .NET 7 is super fast.” Stephen Toub - MSFT

一言で言うと: .NET 7は速いです。本当に速いです。今回のリリースでは、ASP.NET CoreやWindows Forms、Entity Frameworkなどの改良を差し置いて、ランタイムとコアライブラリに1000ものパフォーマンスに影響を与えるPRが投入されました。これは史上最速の.NETです。もしあなたの上司が、あなたのプロジェクトが.NET 7にアップグレードすべき理由を尋ねたら、「リリースに含まれるすべての新機能に加えて、.NET 7は超高速です」と言えばよいのです。

詳細はこのブログにまとめらています。

devblogs.microsoft.com