チュートリアル: バリアント機能フラグを使用して実験を実行する (プレビュー)

アプリケーションで実験を実行すると、情報に基づいた意思決定を行い、アプリのパフォーマンスとユーザー エクスペリエンスを向上するのに役立ちます。 このガイドでは、App Configuration ストアで実験を設定して実行する方法について説明します。 App Configuration、Application Insights (プレビュー)、Split Experimentation Workspace (プレビュー) の機能を使用して、データを収集し、測定する方法について説明します。

そうすることで、データ ドリブンの意思決定を行ってアプリケーションを改善できます。

Note

実験を始める簡単な方法は、Quote of the Day AZD サンプルを実行することです。このリポジトリには、実験を実行するために Azure App Configuration とお使いの .NET アプリケーションを統合に関する包括的な例があり、Azure リソース プロビジョニングと最初の実験も付属します。

このチュートリアルでは、次の作業を行いました。

  • バリアント機能フラグを作成する
  • Application Insights リソースをストアに追加する
  • Split Experimentation Workspace をストアに追加する
  • 実験を実行するようにアプリを設定する
  • テレメトリを有効にし、バリアント機能フラグに実験を作成する
  • 実験メトリックを作成する
  • 実験結果を取得する

前提条件

バリアント機能フラグ (プレビュー) を作成する

機能フラグのクイックスタートの説明に従って、Greeting という名前のバリアント機能フラグを作成し、OffOn の 2 つのバリアントを追加します。

Application Insights (プレビュー) リソースを構成ストアに接続する

実験を実行するには、まず、ワークスペースベースの Application Insights リソースを App Configuration ストアに接続します。 このリソースに App Configuration ストアに接続すると、実験のテレメトリ ソースを使用して構成ストアが設定されます。

  1. App Configuration ストアで、[テレメトリ] > [Application Insights (プレビュー)] の順に選択します。

    ストアへの Application Insights の追加を示す Azure portal のスクリーンショット。

  2. バリアント機能フラグとアプリケーションのテレメトリ プロバイダーとして使用する Application Insights リソースを選択し、[保存] を選択します。 Application Insights リソースがない場合は、[新規作成] を選択して作成します。 作成する方法の詳細については、「ワークスペース ベースのリソースを作成する」を参照してください。 次に、[Application Insights (プレビュー)] に戻り、使用可能な Application Insights リソースの一覧を再度読み込み、新しい Application Insights リソースを選択します。

  3. 通知は、Application Insights リソースが App Configuration ストアで正常に更新されたことを示します。

Split Experimentation Workspace (プレビュー) をストアに接続する

Azure App Configuration で実験を実行するには、Split Experimentation Workspace を使用します。 次の手順に従って、Split Experimentation Workspace をストアに接続します。

  1. App Configuration ストアで、左側のメニューから [実験]>[Split Experimentation Workspace (プレビュー)] の順に選択します。

    App Configuration ストアへの Split Experimentation Workspace の追加を示す Azure portal のスクリーンショット。

  2. [Split Experimentation Workspace][保存] の順に選択します。 Split Experimentation Workspace がない場合は、Split Experimentation Workspace のクイックスタートに従って作成します。

    Note

    Split Experimentation Workspace で選択するデータ ソースは、前の手順で選択した Application Insights リソースと同じである必要があります。

  3. 通知は、操作が正常に終了したことを示します。

実験を実行するようにアプリを設定する

Application Insights (プレビュー) リソースを App Configuration ストアに接続したので、実験を実行するようにアプリを設定します (プレビュー)。

この例では、"今日の名言" という名前の ASP.NET Web アプリを作成します。 このアプリを読み込むと、名言が表示されます。 ユーザーは、ハート ボタンを押して "いいね!" を付けることができます。 ユーザー エンゲージメントを向上するために、パーソナル設定されたあいさつメッセージによって、名言に "いいね!" を付けるユーザーが増加するかどうかを調べる必要があります。 App Configuration で Greeting という名前のバリアント機能フラグを作成し、OffOn の 2 つのバリアントを追加します。 Off バリアントを受け取ったユーザーには、標準のタイトルが表示されます。 On バリアントを受け取ったユーザーには、あいさつメッセージが表示されます。 Application Insights で、ユーザーの操作のテレメトリを収集し、保存します。 Split Experimentation Workspace を使用すると、実験の有効性を分析できます。

アプリを作成し、ユーザー シークレットを追加する

  1. コマンド プロンプトを開き、次のコードを実行します。 これにより、個人アカウント認証を使用して、ASP.NET Core に新しい Razor Pages アプリケーションが作成され、QuoteOfTheDay という名前の出力フォルダーに配置されます。

    dotnet new razor --auth Individual -o QuoteOfTheDay
    
  2. コマンド プロンプトで、QuoteOfTheDay に移動し、次のコマンドを実行して、アプリケーションのユーザー シークレットを作成します。 このシークレットには、App Configuration の接続文字列が保持されます。

    dotnet user-secrets set ConnectionStrings:AppConfiguration "<App Configuration Connection string>"
    
  3. Application Insights の接続文字列を保持する別のユーザー シークレットを作成します。

    dotnet user-secrets set ConnectionStrings:AppInsights "<Application Insights Connection string>"
    

アプリケーション コードを更新する

  1. QuoteOfTheDay.csproj で、必要なパッケージとして、機能管理と App Configuration SDK の最新のプレビュー バージョンを追加します。

    <PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="8.0.0-preview.2" />
    <PackageReference Include="Microsoft.FeatureManagement.Telemetry.ApplicationInsights" Version="4.0.0-preview3" />
    <PackageReference Include="Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore" Version="4.0.0-preview3" />
    <PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="4.0.0-preview3" />
    
  2. Program.cs の行 var builder = WebApplication.CreateBuilder(args); の下に、App Configuration プロバイダーを追加します。これは、アプリケーションが起動されたときに Azure から構成をプルダウンします。 既定では、UseFeatureFlags メソッドに、ラベルのないすべての機能フラグが含まれ、キャッシュ有効期限が 30 秒に設定されます。

    builder.Configuration
        .AddAzureAppConfiguration(o =>
        {
            o.Connect(builder.Configuration.GetConnectionString("AppConfiguration"));
    
            o.UseFeatureFlags();
        });
    
  3. Program.cs で、次の using ステートメントを追加します。

    using Microsoft.ApplicationInsights.AspNetCore.Extensions;
    using Microsoft.ApplicationInsights.Extensibility;
    using Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore;
    
  4. builder.Configuration.AddAzureAppConfiguration が呼び出される場所の下に、次のスニペットを追加します。

    // Add Application Insights telemetry.
    builder.Services.AddApplicationInsightsTelemetry(
        new ApplicationInsightsServiceOptions
        {
            ConnectionString = builder.Configuration.GetConnectionString("AppInsights"),
            EnableAdaptiveSampling = false
        })
        .AddSingleton<ITelemetryInitializer, TargetingTelemetryInitializer>();
    

    このスニペットは、次のアクションを実行します。

    • Application Insights テレメトリ クライアントをアプリケーションに追加します。
    • 送信テレメトリにターゲット設定情報を追加するテレメトリ初期化子を追加します。
    • アダプティブ サンプリングを無効にします。 アダプティブ サンプリングの無効化の詳細については、「トラブルシューティング」を参照してください。
  5. ルート フォルダー QuoteOfTheDay に、ExampleTargetingContextAccessor.cs という名前の新しいファイルを作成します。 これは、ExampleTargetingContextAccessor という名前の新しいクラスを作成します。 次の内容をファイルに貼り付けます。

    using Microsoft.FeatureManagement.FeatureFilters;
    
    namespace QuoteOfTheDay
    {
        public class ExampleTargetingContextAccessor : ITargetingContextAccessor
        {
            private const string TargetingContextLookup = "ExampleTargetingContextAccessor.TargetingContext";
            private readonly IHttpContextAccessor _httpContextAccessor;
    
            public ExampleTargetingContextAccessor(IHttpContextAccessor httpContextAccessor)
            {
                _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
            }
    
            public ValueTask<TargetingContext> GetContextAsync()
            {
                HttpContext httpContext = _httpContextAccessor.HttpContext;
                if (httpContext.Items.TryGetValue(TargetingContextLookup, out object value))
                {
                    return new ValueTask<TargetingContext>((TargetingContext)value);
                }
                List<string> groups = new List<string>();
                if (httpContext.User.Identity.Name != null)
                {
                    groups.Add(httpContext.User.Identity.Name.Split("@", StringSplitOptions.None)[1]);
                }
                TargetingContext targetingContext = new TargetingContext
                {
                    UserId = httpContext.User.Identity.Name ?? "guest",
                    Groups = groups
                };
                httpContext.Items[TargetingContextLookup] = targetingContext;
                return new ValueTask<TargetingContext>(targetingContext);
            }
        }
    }
    

    このクラスは、FeatureManagement のターゲット設定でユーザーのコンテキストを取得する方法を宣言します。 この場合、UserIdhttpContext.User.Identity.Name を読み取り、メール アドレスのドメインを Group として扱います。

  6. Program.cs に戻り、次の using ステートメントを追加します。

    using Microsoft.FeatureManagement.Telemetry;
    using Microsoft.FeatureManagement;
    using QuoteOfTheDay;
    
  7. AddApplicationInsightsTelemetry が呼び出される場所の下に、App Configuration の更新を処理するサービスを追加し、機能管理を設定し、機能管理のターゲット設定を構成し、機能管理でテレメトリ イベントを発行できるようにします。

    builder.Services.AddHttpContextAccessor();
    
    // Add Azure App Configuration and feature management services to the container.
    builder.Services.AddAzureAppConfiguration()
        .AddFeatureManagement()
        .WithTargeting<ExampleTargetingContextAccessor>()
        .AddTelemetryPublisher<ApplicationInsightsTelemetryPublisher>();
    
  8. var app = builder.Build(); の下に、必要に応じて App Configuration の更新をトリガーするミドルウェアを追加します。

    // Use Azure App Configuration middleware for dynamic configuration refresh.
    app.UseAzureAppConfiguration();
    
  9. その下に、次のコードを追加して、ターゲット設定情報を HttpContext に格納して TargetingTelemetryInitializer がその情報にアクセスできるようにします。

    // Add TargetingId to HttpContext for telemetry
    app.UseMiddleware<TargetingHttpContextMiddleware>();
    
  10. [QuoteOfTheDay]>[ページ] >[共有]>[_Layout.cshtml] で、QuoteOfTheDay.styles.css が追加されている場所の下に次の行を追加して、font-awesome のバージョン 5.15.3 の css を追加します。

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
    
  11. [QuoteOfTheDay]>[ページ]>[Index.cshtml.cs] を開き、内容を名言アプリに上書きします。

    using Microsoft.ApplicationInsights;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using Microsoft.FeatureManagement;
    
    namespace QuoteOfTheDay.Pages;
    
    public class Quote
    {
        public string Message { get; set; }
    
        public string Author { get; set; }
    }
    
    public class IndexModel(IVariantFeatureManagerSnapshot featureManager, TelemetryClient telemetryClient) : PageModel
    {
        private readonly IVariantFeatureManagerSnapshot _featureManager = featureManager;
        private readonly TelemetryClient _telemetryClient = telemetryClient;
    
        private Quote[] _quotes = [
            new Quote()
            {
                Message = "You cannot change what you are, only what you do.",
                Author = "Philip Pullman"
            }];
    
        public Quote? Quote { get; set; }
    
        public bool ShowGreeting { get; set; }
    
        public async void OnGet()
        {
            Quote = _quotes[new Random().Next(_quotes.Length)];
    
            Variant variant = await _featureManager.GetVariantAsync("Greeting", HttpContext.RequestAborted);
    
            ShowGreeting = variant.Configuration.Get<bool>();
        }
    
        public IActionResult OnPostHeartQuoteAsync()
        {
            string? userId = User.Identity?.Name;
    
            if (!string.IsNullOrEmpty(userId))
            {
                // Send telemetry to Application Insights
                _telemetryClient.TrackEvent("Like");
    
                return new JsonResult(new { success = true });
            }
            else
            {
                return new JsonResult(new { success = false, error = "User not authenticated" });
            }
        }
    }
    

    この PageModel は、名言をランダムに選択し、GetVariantAsync を使用して、現在のユーザーのバリアントを取得し、"ShowGreeting" という名前の変数をバリアントの値に設定します。 PageModel はまた、Post 要求を処理、_telemetryClient.TrackEvent("Like"); を呼び出します。これは、イベントを Like という名前で Application Insights に送信します。 このイベントは、ユーザーとバリアントに自動的に関連付けられ、メトリックで追跡することができます。

  12. index.cshtml を開き、名言アプリの内容を上書きします。

    @page
    @model IndexModel
    @{
        ViewData["Title"] = "Home page";
        ViewData["Username"] = User.Identity.Name;
    }
    
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
        }
    
        .quote-container {
            background-color: #fff;
            margin: 2em auto;
            padding: 2em;
            border-radius: 8px;
            max-width: 750px;
            box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
            display: flex;
            justify-content: space-between;
            align-items: start;
            position: relative;
        }
    
        .vote-container {
            position: absolute;
            top: 10px;
            right: 10px;
            display: flex;
            gap: 0em;
        }
    
        .vote-container .btn {
            background-color: #ffffff; /* White background */
            border-color: #ffffff; /* Light blue border */
            color: #333
        }
    
        .vote-container .btn:focus {
            outline: none;
            box-shadow: none;
        }
    
        .vote-container .btn:hover {
            background-color: #F0F0F0; /* Light gray background */
        }
    
        .greeting-content {
            font-family: 'Georgia', serif; /* More artistic font */
        }
    
        .quote-content p.quote {
            font-size: 2em; /* Bigger font size */
            font-family: 'Georgia', serif; /* More artistic font */
            font-style: italic; /* Italic font */
            color: #4EC2F7; /* Medium-light blue color */
        }
    </style>
    
    <div class="quote-container">
        <div class="quote-content">
            @if (Model.ShowGreeting)
            {
                <h3 class="greeting-content">Hi <b>@User.Identity.Name</b>, hope this makes your day!</h3>
            }
            else
            {
                <h3 class="greeting-content">Quote of the day</h3>
            }
            <br />
            <p class="quote">“@Model.Quote.Message”</p>
            <p>- <b>@Model.Quote.Author</b></p>
        </div>
    
        <div class="vote-container">
            <button class="btn btn-primary" onclick="heartClicked(this)">
                <i class="far fa-heart"></i> <!-- Heart icon -->
            </button>
        </div>
    
        <form action="/" method="post">
            @Html.AntiForgeryToken()
        </form>
    </div>
    
    <script>
        function heartClicked(button) {
            var icon = button.querySelector('i');
            icon.classList.toggle('far');
            icon.classList.toggle('fas');
    
            // If the quote is hearted
            if (icon.classList.contains('fas')) {
                // Send a request to the server to save the vote
                fetch('/Index?handler=HeartQuote', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
                    }
                });
            }
        }
    </script>
    

    このコードは、名言に対するハート アクションを使用して、QuoteOfTheDay を表示して処理する UI に対応します。 これは、前述の Model.ShowGreeting の値を使用して、バリアントに応じてユーザーごとに異なる内容を表示します。

アプリをビルドして実行する

  1. コマンド プロンプトで、QuoteOfTheDay 内で dotnet build を実行します。

  2. dotnet run --launch-profile https を実行します。

  3. アプリケーションの出力で、Now listening on: https://localhost:{port} 形式のメッセージを探します。 ブラウザーで、含まれているリンクに移動します。

  4. 実行中のアプリケーションを表示したら、右上にある [登録] を選択して新しいユーザーを登録します。

    今日の名言アプリの [登録] を示すスクリーンショット。

  5. user@contoso.com という名前の新しいユーザーを登録します。 パスワードは 6 文字以上で、数字と特殊文字がそれぞれ 1 つ含まれている必要があります。

  6. ユーザー情報を入力した後、ここをクリックしてメールを検証してくださいと示されたリンクを選択します。

  7. userb@contoso.com という名前の 2 番目のユーザーを登録し、別のパスワードを入力して、この 2 番目のメールを検証します。

    Note

    このチュートリアルの目的上、これらの名前を正確に使用することが重要です。 機能が想定どおりに構成されている限り、この 2 人のユーザーには異なるバリアントが表示されます。

  8. 右上にある [ログイン] を選択し、userb (userb@contoso.com) としてサインインします。

    今日の名言アプリの [ログイン] を示すスクリーンショット。

  9. ログインすると、userb@contoso.com がアプリを表示したときに特別なメッセージが表示されることを確認できます。

    今日の名言アプリでユーザーに表示される特別なメッセージを示す今日の名言アプリのスクリーンショット。

    userb@contoso.com は、特別なメッセージが表示される唯一のユーザーです。

テレメトリを有効にし、バリアント機能フラグに実験を作成する

次の手順に従って、テレメトリを有効にし、バリアント機能フラグに実験を作成します。

  1. App Configuration ストアで、[操作]>[機能管理] の順に移動します。

  2. バリアント機能フラグ "Greeting" の右側にある [...] コンテキスト メニューを選択し、[履歴] を選択します。

    バリアント機能フラグの編集を示す Azure portal のスクリーンショット。

  3. [テレメトリ] タブに移動し、[テレメトリを有効にする] チェック ボックスをオンにします。

  4. [実験] タブに移動し、[実験の作成] チェック ボックスをオンにして、実験に名前を付けます。

  5. [確認と更新][更新] の順に選択します。

  6. 通知は、操作が正常に終了したことを示します。 [機能マネージャー] で、バリアント機能フラグの [実験] の下に [アクティブ] という語が表示されます。

実験メトリックを作成する

Split Experimentation Workspace の "メトリック" は、Application Insights に送信されるイベントの定量的測定値です。 このメトリックは、ユーザーの動作と結果に与える機能フラグの影響を評価するのに役立ちます。

以前にアプリを更新したときに、アプリケーション コードに _telemetryClient.TrackEvent("Like") を追加しました。 Like は、ユーザー アクション (この場合は、ハート ボタンの選択) を表すテレメトリ イベントです。 このイベントは、これから作成するメトリックに接続する Application Insights リソースに送信されます。 作成したアプリでは 1 つのイベントのみを指定しますが、複数のイベントを指定して、その後に複数のメトリックを作成することができます。 複数のメトリックが 1 つの Application Insight イベントに基づく場合もあります。

  1. Split Experimentation Workspace リソースに移動します。 [構成]>[実験メトリック] で、[作成] を選択します。

  2. [実験メトリックの作成] で、次の情報を選択または入力し、[作成] を選択して保存します。

    新しい実験メトリックの作成を示す Azure portal のスクリーンショット。

    設定 値の例 説明
    名前 "ハート投票" 実験メトリックの名前。
    説明 "特別なメッセージが表示された場合と表示されない場合のハート ボタンを選択するユーザーの数をカウントする。" メトリックの説明 (省略可能)。
    Application Insights イベント名 Like Application Insights イベントの名前。 この名前は大文字と小文字が区別され、コード内の _telemetryClient.TrackEvent("<Event-Name>") で指定された名前です。
    測定する値 Count 次のオプションを使用できます。
    • 回数: ユーザーによってイベントがトリガーされた回数をカウントします。
    • 平均: ユーザーのイベントの値を平均します。
    • 合計: ユーザーのイベントの値を合計します。 合計値の平均を示します。
    • 割合: イベントをトリガーしたユーザーの割合を計算します。
    期待される影響 増加 この設定は、作成したメトリックの測定の背後にある最終的な目標または目的を表します。

    このチュートリアルでは、"今日の名言" の横に特別なメッセージがある場合、より多くのユーザーがハートの形をした "いいね!" ボタンをクリックするという仮説を立てています。 アプリケーション コードでは、このクリックを Like という名前のイベントとして追跡します。 アプリケーションは、Like イベントをテレメトリとして Application Insights に送信します。この実験の [期待される影響] は、"ハート投票" でのユーザーのクリック数 ([回数] として測定) の増加を確認し、指定した仮説を検証することです。 割り当てられた対象ユーザーに特別なメッセージが表示されたにもかかわらず、ボタンのクリック数が減少した場合、この実験の仮説は間違っていることになります。

  3. 作成が完了したら、新しいメトリックがポータルに表示されます。 画面の右側にある ([...]) コンテキスト メニューを選択すると、このメトリックを編集または削除できます。

    実験メトリックを示す Azure portal のスクリーンショット。

実験結果を取得する

新しく設定した実験をテストに配置し、生成された結果を分析するには、いくつかのトラフィックをシミュレートし、10 から 15 分待ちます。

実験の結果を表示するには、[機能マネージャー] に移動し、バリアント機能フラグの一覧で、[...][実験] の順にクリックするか、[実験] 列で [アクティブ] リンクを選択します。 この列は既定では表示されません。 これを表示するには、[機能マネージャー] で、[列の編集][列の編集][列の追加][実験][追加] の順に選択します。

結果ページでは、実験の [バージョン]、結果を比較する [ベースライン][Comparison] バリアントが既定で選択されています。 必要に応じて、この既定を好みに合わせて変更し、[適用] を選択して、実験の結果を表示します。

実験結果を示す Azure portal のスクリーンショット。

上記のスクリーンショットは、ハート投票 でのハートの投票数は On バリアントの方が Off バリアントよりも 560.62% 多く、実験で期待どおりの結果が得られたことを示しています。

バリアント機能フラグを編集すると、実験の新しいバージョンが生成され、それを選択して結果を表示できます。

Note

実験結果を得るために最小限必要なイベント数はバリアントあたり 30 ですが、実験で信頼できる結果を確実に得るには、この最小サンプル サイズよりも大きいサンプル サイズを使用することをお勧めします。

Note

Application Insights のサンプリングは既定で有効になっており、実験結果に影響を与える可能性があります。 このチュートリアルでは、Application Insights でのサンプリングをオフにすることをお勧めします。 詳細については、「Application Insights におけるサンプリング」を参照してください。

次のステップ

実験の概念の詳細については、次のドキュメントを参照してください。

.NET 機能管理ライブラリの完全な機能の概要については、次のドキュメントに進んでください。