Azure Functions(isolated)でApplication Insightsの分散トレーシングを取得する

Azure

2023年8月16日に、Azure Functionsのisolatedワーカーにおいて、Application Insights統合がGAしたという発表がありました。

このGAによって、isolatedなAzure Functionsでも非常に簡単に分散トレーシングを取得することができるようになりました。今回は外部APIとCosmosDBのトレーシングを実際に取得してみたいと思います。

結論

  • 「Microsoft.ApplicationInsights.WorkerService」と「Microsoft.Azure.Functions.Worker.ApplicationInsights」の2つのパッケージを追加する
  • 起動設定に2行追加するだけで、自動的に依存関係をトレーシングしてくれる

環境

言語.NET 6(6.0.413)
関数の言語isolated(分離ワーカープロセス)
CosmosDBServerless
OSWindows 11

ローカル(もしくはAzure上)でAzure Functionsを実行できる準備が整っている前提で話を進めます。

Azure Functionsの設定

パッケージの追加

Application Insights用のパッケージとして以下の2つを追加します。

パッケージ名バージョン
Microsoft.ApplicationInsights.WorkerService2.21.0
Microsoft.Azure.Functions.Worker.ApplicationInsights1.0.0

当時の最新のバージョンを使用しています。当たりまえかもしれませんが、基本的に最新のバージョンを使用しましょう。

実は、上記2つのパッケージをインストールした後、ローカルでAzure Functionsを起動してHTTPリクエストを送っても、正常にレスポンスが返却されずに「The operation was aborted」というエラーが発生するようになってしまいました。「Microsoft.Azure.Functions.Worker」のバージョンが古かったことが原因であり、1.8.0から1.19.0にアップデートしたことで解消しました。開発が盛んなパッケージは頻繁に更新されるため、定期的に確認するとよいと思います。私は下記のissueのおかげて解決することができました。

また、CosmosDB接続用に以下のパッケージをインストールしています。

パッケージ名バージョン
Microsoft.Azure.Cosmos3.35.2-preview

CosmosDBパッケージはあえてプレビュー版を使用しています。CosmosDBとの接続はGatewayモードとDirectモードの2種類あります。.NETのデフォルトはDirectモードです。DirectモードはTCP(GatewayモードはHTTP)を使用しますが、TCPの場合は3.33.0-preview以上でないと自動的に依存関係をトラッキングしてくれません。手動でトラッキングするコードを追加すれば問題はありませんが、自動で行ってほしかったのでプレビュー版を使用しています。

Program.csの設定

18、19行目がApplication Insightsの設定です。ConfigureFunctionsApplicationInsightsで分散トレーシングに必要な依存関係のテレメトリーを作成してくれるようです。

    public static void Main()
    {
        var host = new HostBuilder()

            .ConfigureAppConfiguration(builder =>
            {
                builder.AddAzureAppConfiguration(options =>
                {
                    options.Connect(appcs_con_string);
                });
            })
            .ConfigureServices(s =>
            {
          // HttpClientの登録
                s.AddHttpClient();

         // Application Insights用に下記2つを追加する
                s.AddApplicationInsightsTelemetryWorkerService();
                s.ConfigureFunctionsApplicationInsights();

                //CosmosDBクライアントの登録
                s.AddSingleton(serviceProvider =>
                {
                    return new CosmosClient(endpoint, key);
                });
            })
            .ConfigureFunctionsWorkerDefaults(app =>
            {
                app.UseAzureAppConfiguration();
            })
            .Build();
        host.Run();
    }

上記の設定に関しては下記Azureドキュメントにまとまっていますので、詳細については以下をご覧ください。

アプリの設定

外部API呼び出しとCosmosDBへのアクセスは以下の通りです。例によって必要な個所のみ抜粋しています。外部APIはアメリカの統計情報が取得できるAPIを使っています。

CosmosDBはポイント読み取りを1回だけ実行しています。

        public Test1(ILoggerFactory loggerFactory, IHttpClientFactory httpClientFactory, TelemetryClient telemetryClient, CosmosClient cosmosClient)
        {
            _logger = loggerFactory.CreateLogger<Test1>();
            _cosmosClient = cosmosClient;
            _httpClient = httpClientFactory.CreateClient();
            _telemetryClient = telemetryClient;
        }

        [Function("Test1")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
        {

            var res = new US();
            try
            {
                // 外部API呼び出し
                var request = new HttpRequestMessage(HttpMethod.Get, uri);
                var apiResponse = await _httpClient.SendAsync(request);
                var body = await apiResponse.Content.ReadAsStringAsync();
                res = JsonSerializer.Deserialize<US>(body);

                // CosmosDBへの読み取り
                var con = _cosmosClient.GetContainer(containerName, colletionName);
                var readResult = await con.ReadItemAsync<CosmosEntity>(id, new PartitionKey(id));

            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex.Message);
            }
 
            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
            response.WriteString($"{res?.data.First().Population}");
            return response;
        }

また、Application Insightsの接続文字列をappsettings.jsonや環境変数に設定する必要があります。接続文字列はAzure PortalのApplication Insights概要ページから取得できます。

appsettings.jsonで設定する場合

    {
        "ApplicationInsights":
        {
            "ConnectionString" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        },
        "Logging":
        {
            "LogLevel":
            {
                "Default": "Warning"
            }
    }

local.settings.jsonで設定する場合

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "APPLICATIONINSIGHTS_CONNECTION_STRING": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}

動作確認

以上で設定完了です。見てもらったらわかると思いますが、Program.csに2行追加しただけですがこれで依存関係を取得してデータをApplication Insightsに送信してくれます。

それでは実際にローカルでAzure Functionsを起動して数回リクエストを送ってみます。Application Insightsのパフォーマンスページを見てみると、データが連携されていることが分かります。

1つ1つのリクエストをドリルダウンして詳しく見ていきます。リクエストを受け付けてから「外部API呼び出し」→「CosmosDB読み取り」の順に処理が流れている様子がトレースされています。各外部接続でどの程度時間がかかったかが自動で記録されており、ボトルネックの調査などに非常に役立ちそうです。

下記のトレースは、Azure Functions起動後初回リクエストの様子です。AzureのVM上で実行しているため、インスタンスのメタデータへの問い合わせが記録されています。また、CosmosDBに対してはデータを読み取る前にGETで4つほどリクエストが行われている様子も分かります。

CosmosDBにDirectモードで接続する際は、初めにコンテナ情報を取得します。cosmos-xxx-japaneastへのリクエストはそのためのものだと思います。CosmosDBの通信モードについては下記の記事でまとめていますので、よろしければ参照ください。

まとめ

今回は、Application Insights SDKを使ってisolatedなAzure Functionsの外部接続(API呼び出しとCosmosDB)のトレーシングを取得してみました。

設定は非常に簡単で、Azure Functions起動時の処理に2行追加するだけで自動的にトレーシングを取得することができました。もちろん、追加の情報を記録したい場合は自身でテレメトリを送信できますので必要に応じてカスタマイズするとよいでしょう。またすべてを自動で取得してくれるわけではありません。自動取得対象は下記にまとめられています。

パフォーマンスチューニングの際に、どこで処理が遅いのかの調査に非常に役立つトレーシングを簡単に追加できるようになりました。.NETでAzure Functionsを使う場合、in-processとisolatedの2種類あります。今後はisolatedが主流になっていく予定ですので、isolatedのAzure Functionsがますます使いやすくなっており、Application Insights統合のGAはとてもうれしいGAだと思います。

コメント