恐らく Build 2019 に合わせたと思ってますが、Azure App Configuration がアップデートされて Feature Management という新しい機能が追加されています。
公式にも A/B テストとか、新しい機能の段階的なリリースにも使えると紹介されています。App Service の Test in production ではなく、コードレベルでの対応の方が良いことも多いでしょう。
要するに bool 値を専用に扱うための機能です。Azure Portal からオンオフの管理が簡単なので、普通に Key を追加するよりも便利に扱えます。
同時にクライアントライブラリもアップデートされています。前に動かなかった Time-Based Access は簡単に試している範囲では直っているようでした。
基本的な使い方
ドキュメントでは ASP.NET Core ベースで書かれていますが、DI を使って基本となる IFeatureManager
を取得できれば良いので、Console App などでも使えます。
同様に Azure Functions v2 でも問題ないでしょうが、基本は Web アプリで使うかと思います。
単純に新しい Feature を追加してオンオフするだけで試しておきます。基本はドキュメントにあるサンプルコードと同じなので、気になった部分だけコードを出すことにします。
Azure Portal から App Configuration を作成すると、Feature Management という項目が増えているはずです。
適当にキー名を設定して追加すると、Portal にトグルスイッチ付きの項目が出てきます。
このオンオフが bool 値にそのまま対応しているので難しいことはないです。
そしてキーを参照する側の話ですが、ASP.NET Core の場合は FeatureAttribute
や feature
Tag Helper が用意されているので、組み込みも違和感なく行えます。
[Feature("Beta")] public class NewFeatureController : Controller { public IActionResult Index() { return View(); } }
<feature name="Beta"> Beta flag on </feature>
キーをオンにした時だけコントローラや表示が有効になるので、テストや新機能のリリース時の切り替えに使えるという話です。App Configuration よりも ASP.NET Core との統合の方が重要という感じがします。
ちなみにデフォルト設定では 30 秒間隔でポーリングするようになっているので、常時起動している Web アプリでも問題なく切り替えが可能です。
少し高度な使い方
Feature Management には ASP.NET Core 向けにいくつか組み込みで機能が用意されています。ドキュメントにちゃんと書いてあるので、特に説明は不要かと思います。
実装としては IFeatureManager
を DI で受け取って、フラグを見て処理を分岐しているだけなので、DI で解決される部分でならカスタム実装を入れたりと自由に使えます。
例えば独自の Service を用意した場合にも、コンストラクタで IFeatureManager
を受け取れば良いです。
public class DemoService { public DemoService(IFeatureManager featureManager) { _featureManager = featureManager; } private readonly IFeatureManager _featureManager; public string Hello(string name) { if (_featureManager.IsEnabled("Beta")) { return $"こんにちは, {name}"; } return $"Hello, {name}"; } }
ドキュメントでは一部 enum を使っていましたが、基本は string しか受け取ってくれないので Feature 用の文字列をいい感じに管理する必要があります。
この辺りは設定からコード自動生成したり、同期を上手くとる方法が必要になりそうです。
FeatureFilter を使う
ここまでの機能だと目新しさはないというか、これまでも出来てただろという感じですが、Feature Management ではフィルターを使って動作をカスタマイズできます。
分かりやすい部分だと、トラフィックの数 % だけ有効化するといった動作を簡単に追加できます。
組み込みで用意されているフィルターは以下の 2 種類です。名前からわかるように % 指定するものと、期間指定するものの 2 つです。
- PercentageFilter
- TimeWindowFilter
使う前には DI にフィルターを追加しておく必要があるので、忘れないようにします。組み込みのフィルターは自動で追加されていても良い気がしますが、今は手動で追加します。
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddFeatureManagement() .AddFeatureFilter<PercentageFilter>(); }
DI に追加してしまえば、後は Azure Portal から使いたいフィルターを追加するだけです。
名前がクラス名と異なっているので少し注意が必要です。フィルター毎にパラメータを設定可能なので、今回の PercentageFilter
の場合は 0-100 の数字を設定して、有効化する確率を指定します。
保存するとトグルスイッチ部分が Conditional 表示に変わります。
これで指定した % だけフラグが有効になるので、段階的なリリース向けに利用できます。
再デプロイは不要で、Azure Portal から設定を変えれば再起動無しで反映されるのは使い勝手が良いです。
FeatureFilter を自作する
組み込みの FeatureFilter はやはり機能が少ないので、必要な場合は自作してしまいましょう。基本は IFeatureFilter
を実装したクラスを用意すれば良いだけなので簡単です。
DI には Singleton として追加されますが、IHttpContextAccessor
が使えるので HTTP リクエストによって動作を変えるフィルターを作ることも出来るようです。実際に以下のコードで動作を確認しておきました。
[FilterAlias("MyCustomFilter")] public class MyCustomFilter : IFeatureFilter { public MyCustomFilter(IHttpContextAccessor httpContextAccessor, ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<MyCustomFilter>(); _httpContextAccessor = httpContextAccessor; } private readonly ILogger _logger; private readonly IHttpContextAccessor _httpContextAccessor; public bool Evaluate(FeatureFilterEvaluationContext context) { _logger.LogInformation(_httpContextAccessor.HttpContext.TraceIdentifier); return true; } }
フィルターとしての動作は持たず、単純にログにトレース ID を書き出すだけですが、ちゃんとリクエスト単位で ID が出力されています。
この結果を踏まえて、サンプルとして User-Agent の値で振り分けるフィルタを書いてみました。
実用性はあまりないですが、Azure Portal からパラメータの設定が可能なサンプルとしてみてください。
[FilterAlias("UserAgentFilter")] public class UserAgentFilter : IFeatureFilter { public UserAgentFilter(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } private readonly IHttpContextAccessor _httpContextAccessor; public bool Evaluate(FeatureFilterEvaluationContext context) { var userAgentSettings = context.Parameters.Get<UserAgentSettings>(); var userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].FirstOrDefault(); return userAgent != null && userAgent.Contains(userAgentSettings.Value); } } public class UserAgentSettings { public string Value { get; set; } }
Azure Portal で設定する名前は FilterAlias
で指定した名前になります。
これも DI に追加した後に Azure Portal からフィルターを設定しておきます。ちなみに複数フィルターを追加した場合は上から順に評価されるかつ OR 扱いになります。
パラメータとして Edge を設定して、Edge と Chrome でページを確認しておきました。ちゃんと Edge ではナビゲーションに項目が出ていますが、Chrome では出ていないので正しく動いています。
HTTP リクエストを見て判断できるので、例えばログイン中のユーザーが所属するロールを見て動作を変えるというフィルターも書けるはずです。
他にもアクセス元が社内の IP の場合だけ機能を有効にするといった処理も簡単に実現出来ます。セッションアフィニティ用にクッキーを吐いたりも出来そうな気がするので、割と応用範囲が広いです。
Feature Management を単独で使う
Azure Portal では Feature Management は App Configuration に強く統合されていますが、ライブラリ的には分離された設計になっているので、当然ながら単独で使うことが出来ます。
Feature Management がやっていることは Configuration を読み取って、いい感じの切り替えを提供しているだけなので、構造を分かってしまえばシンプルに扱えます。
単独で使う場合は Microsoft.FeatureManagement.AspNetCore
を追加して、AddFeatureManagement
で利用するセクションを指定するだけで済みます。
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); // Feature section の下を見る services.AddFeatureManagement(Configuration.GetSection("Feature")); }
Feature Management で使う値は appsettings.json
に追加しておきます。環境変数なども使えます。
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "Feature": { "Beta": true } }
これで App Configuration 無しでも動作します。Azure Portal のサポートは結構大きいので、組み合わせて使う方が便利だと思いますが、Feature Management だけ使いたいというニーズもあると思うので。