Azure Functions を利用したプライベート可用性テストについて

Last Update: feedback 共有

こんにちは、Azure Monitoring サポート チームの北山です。

Application Insights には可用性テストの機能があります。
可用性テストの機能をご利用いただく事で、監視対象の Web サイトに対して自動的にリクエストを送信することが可能です。
監視対象の Web サイトからのレスポンスをチェックし、期待したレスポンスが返却されない場合にアラートを通知する事が可能です。

Application Insights 可用性テスト

Application Insights のサービスから監視対象の Web サイトに対して定期的にリクエストを送信するため、監視対象の Web サイトはパブリックからの通信を許可いただく必要がございます。
しかし、お客様の環境によってはパブリックからの通信を許可していない可能性がございます。
この場合は、標準の可用性テストがご利用いただけません。

この記事では、TimerTrigger 関数で指定された構成に従って定期的に実行される、独自のテスト ロジックが実装された Azure Functions から、Application Insights API の一つである TrackAvailability() を使って可用性テストの結果を送信する方法について説明します。

目次

Azure Functions を用いたプライベート可用性テスト構築の手順

1. VNET 統合を実施した Azure Functions を用意します。

パブリックからの通信を許可していない Web サイトにアクセスするために、VNET 統合した Azure Functions リソースを準備します。
VNET 統合を実施すると、Azure Functions からの通信が VNET を経由します。
監視対象の Web サイトにアクセス可能な VNET 上に Azure Functions リソースをデプロイすることで、定期的に監視対象の Web サイトへテストのためのリクエストが可能です。

Azure Functions の VNET 統合の方法につきましては、下記の公開情報をご参考くださいませ。

上記公開情報をご参考に、ランタイムが .NET の Azure Functions リソースをご準備くださいませ。

弊社検証環境では、Windows OS の .NET 6.0 にて確認いたしました。

2. Azure Functions にテスト ロジックを実装します。

Visual Studio Code を使って作成

下記の公開情報をもとに、Azure Functions にデプロイする関数を作成します。
Visual Studio Code を使って関数を作成するための前提条件などは、下記の公開情報をご一読ください。

上記公開情報に従って、HTTP Triger の関数を作成します。
※ ここでは、C# (.NET 8 Isolated) のランタイムを選択しております。








プロジェクト ファイルに、下記のコードを追加して Application Insights SDK を導入します。

1
<PackageReference Include="Microsoft.Azure.WebJobs.Logging.ApplicationInsights" Version="3.0.35" /> <!-- Ensure you’re using the latest version --> 

注意 : C# の Functions に Application Insights SDK を導入する場合は、標準の Application Insights SDK ではなく必ず “Microsoft.Azure.WebJobs.Logging.ApplicationInsights” をインストールしてください。

その後、HTTP Triger のクラスに下記のようにコードを修正します。
※ 検証環境では TimerTrigger1 というクラス名で動かしています。

using 宣言は下記のとおりです。

1
2
3
4
5
6
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using System.Diagnostics;

プライベート変数として TelemetryClient クラスの変数を定義し、コンストラクタは下記のとおり改修します。

1
2
3
4
5
6
7
private readonly TelemetryClient telemetryClient;

public TimerTrigger1(ILoggerFactory loggerFactory, TelemetryConfiguration telemetryConfiguration)
{
_logger = loggerFactory.CreateLogger<TimerTrigger1>();
this.telemetryClient = new TelemetryClient(telemetryConfiguration);
}

Run 関数は下記のとおりです。
Application Insights へ送信するテスト結果インスタンス (AvailabilityTelemetry) を生成し、テストを実施、結果を指定し TrackAvailability() を呼び出して Application Insights へ結果を出力します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[Function("TimerTrigger1")]
public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer)
{
string testName = "Function Name";
string location = Environment.GetEnvironmentVariable("REGION_NAME");
var availability = new AvailabilityTelemetry
{
Name = testName,
RunLocation = location,
Success = false,
};

availability.Context.Operation.ParentId = Activity.Current.SpanId.ToString();
availability.Context.Operation.Id = Activity.Current.RootId;
var stopwatch = new Stopwatch();
stopwatch.Start();

try
{
using (var activity = new Activity("AvailabilityContext"))
{
activity.Start();
availability.Id = Activity.Current.SpanId.ToString();
// Run business logic
await RunAvailabilityTestAsync();
}
availability.Success = true;
}

catch (Exception ex)
{
availability.Message = ex.Message;
throw;
}

finally
{
stopwatch.Stop();
availability.Duration = stopwatch.Elapsed;
availability.Timestamp = DateTimeOffset.UtcNow;
telemetryClient.TrackAvailability(availability);
telemetryClient.Flush();
}
}

別途下記のようなビジネス ロジックを実装し、この中で監視対象の Web サイトへアクセスします。
アクセスした結果は、適宜呼び出し元に返却します。
テスト結果が NG の場合は、例外をスローしても良いかもしれません。
例外をスローする場合は、必ず呼び出し元で Catch して Application Insights へ結果を出力しましょう。

1
2
3
4
5
6
7
8
private async Task RunAvailabilityTestAsync()
{
using (var httpClient = new HttpClient())
{
// TODO: Replace with your business logic
await httpClient.GetStringAsync("https://www.bing.com/");
}
}

TimerTrigger1 クラスの全体はこのような感じで実装しております。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using System.Diagnostics;

namespace Company.Function
{
public class TimerTrigger1
{
private readonly ILogger _logger;
private readonly TelemetryClient telemetryClient;

public TimerTrigger1(ILoggerFactory loggerFactory, TelemetryConfiguration telemetryConfiguration)
{
_logger = loggerFactory.CreateLogger<TimerTrigger1>();
this.telemetryClient = new TelemetryClient(telemetryConfiguration);
}

[Function("TimerTrigger1")]
public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer)
{
string testName = "Function Name";
string location = Environment.GetEnvironmentVariable("REGION_NAME");
var availability = new AvailabilityTelemetry
{
Name = testName,
RunLocation = location,
Success = false,
};

availability.Context.Operation.ParentId = Activity.Current.SpanId.ToString();
availability.Context.Operation.Id = Activity.Current.RootId;
var stopwatch = new Stopwatch();
stopwatch.Start();

try
{
using (var activity = new Activity("AvailabilityContext"))
{
activity.Start();
availability.Id = Activity.Current.SpanId.ToString();
// Run business logic
await RunAvailabilityTestAsync();
}
availability.Success = true;
}

catch (Exception ex)
{
availability.Message = ex.Message;
throw;
}

finally
{
stopwatch.Stop();
availability.Duration = stopwatch.Elapsed;
availability.Timestamp = DateTimeOffset.UtcNow;
telemetryClient.TrackAvailability(availability);
telemetryClient.Flush();
}
}

private async Task RunAvailabilityTestAsync()
{
using (var httpClient = new HttpClient())
{
// TODO: Replace with your business logic
await httpClient.GetStringAsync("https://www.bing.com/");
}
}

}
}

その後、当該関数が実行されると、テスト コードの結果として Application Insights に可用性テストの結果が記録されます。

Azure Portal から作成

Azure Portal からの作成は、.NET インプロセス モデルのみサポートしております。
また、インプロセス モデルはサポート終了予定で、今後は Visual Studio Code などを使って作成する必要がある点ご留意ください。

その 1 でご準備いただいた Functions リソースに対して、タイマー トリガーの関数を作成します。
関数名やスケジュールは、貴社のご要件に合うよう適宜ご指定くださいませ。

その後、当該 Functions リソースの左側ペインより「App Service Editor (プレビュー)」を開きます。

下図のように、当該関数に対して右クリックし新しいファイルを作成します。
作成するファイル名は、「function.proj」です。

function.proj に対して、下記のコードを貼り付けます。

1
2
3
4
5
6
7
8
<Project Sdk="Microsoft.NET.Sdk"> 
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.15.0" /> <!-- Ensure you’re using the latest version -->
</ItemGroup>
</Project>

次に runAvailabilityTest.csx というファイルを作成し、下記のコードを貼り付けます。

下記のコードは、監視対象の Web サイトに対して GET リクエストを送信するコードです。
こちらはサンプル コードのため、Bing のサイトにアクセスするよう実装しております。

1
2
3
4
5
6
7
8
9
10
using System.Net.Http;

public async static Task RunAvailabilityTestAsync( ILogger log )
{
using (var httpClient = new HttpClient())
{
// TODO: Replace with your business logic
await httpClient.GetStringAsync( "https://www.bing.com/" );
}
}

次のコードを run.csx にコピーします。
これによって既存のコードが全て置き換えられます。

下記のサンプル コードは、当該 Functions のアプリケーション設定に指定された接続文字列 (APPLICATIONINSIGHTS_CONNECTION_STRING) を使って、テスト結果の送信先である Application Insights を指定しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#load "runAvailabilityTest.csx" 
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.Logging;

private static TelemetryClient telemetryClient;

// =============================================================
// ****************** DO NOT MODIFY THIS FILE ******************
// Business logic must be implemented in RunAvailabilityTestAsync function in runAvailabilityTest.csx
// If this file does not exist, please add it first
// =============================================================
public async static Task Run( TimerInfo myTimer, ILogger log, Microsoft.Azure.WebJobs.ExecutionContext executionContext )
{
if (telemetryClient == null)
{
// Initializing a telemetry configuration for Application Insights based on connection string
var telemetryConfiguration = new TelemetryConfiguration();
telemetryConfiguration.ConnectionString = Environment.GetEnvironmentVariable( "APPLICATIONINSIGHTS_CONNECTION_STRING" );
telemetryConfiguration.TelemetryChannel = new InMemoryChannel();
telemetryClient = new TelemetryClient( telemetryConfiguration );
}

string testName = executionContext.FunctionName;
string location = Environment.GetEnvironmentVariable( "REGION_NAME" );
var availability = new AvailabilityTelemetry
{
Name = testName,
RunLocation = location,
Success = false,
};

availability.Context.Operation.ParentId = Activity.Current.SpanId.ToString();
availability.Context.Operation.Id = Activity.Current.RootId;
var stopwatch = new Stopwatch();
stopwatch.Start();

try
{
using (var activity = new Activity( "AvailabilityContext" ))
{
activity.Start();
availability.Id = Activity.Current.SpanId.ToString();
// Run business logic
await RunAvailabilityTestAsync( log );
}
availability.Success = true;
}

catch (Exception ex)
{
availability.Message = ex.Message;
throw;
}

finally
{
stopwatch.Stop();
availability.Duration = stopwatch.Elapsed;
availability.Timestamp = DateTimeOffset.UtcNow;
telemetryClient.TrackAvailability( availability );
telemetryClient.Flush();
}
}

その後、当該関数が実行されると、テスト コードの結果として Application Insights に可用性テストの結果が記録されます。

注意事項

上記サンプル コードは、あくまでサンプルです。
お客様の環境によっては GET リクエストではなく POST リクエストの実行結果を確認する事も想定されます。
そのため、お客様の監視要件に合わせてテスト コードを実装いただけますと幸いです。

まとめ

本記事では、パブリックからのアクセスを許可していない環境への可用性テストの方法ついてご案内いたしましたが、ご理解いただけましたでしょうか。

本記事が少しでもお役に立ちましたら幸いです。
最後までお読みいただきありがとうございました!

関連する記事

※本情報の内容(添付文書、リンク先などを含む)は、作成日時点でのものであり、予告なく変更される場合があります。