[セッションレポート] Feature Flagで機能別にローンチをコントロールする (BOA305-R) #reinvent

Amazon CloudWatch EvidentlyとAWS AppConfigを利用したFeature Flagの実装例です。Amazon CloudWatch Evidentlyを利用することでフラグの詳細な設定やA/Bテストを簡単に行うことができます。
2022.12.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

セッション概要

動画

セッション動画も上がっていたのでこちらもご確認ください

ユーザーにコードを配布することはエキサイティングなことですが、リリースにはリスクも伴います。このリスクをコントロールするために、多くの場合、デプロイメントを機能が稼動するタイミングと切り離すことを選択します。新機能は if/then 式で隠蔽され、新機能の立ち上げは一元的に制御されるようになっています。これらは、一部のユーザーに限定することも、すべてのユーザーに限定することもできます。このテクニックは、フィーチャーフラグまたはダークローンチと呼ばれています。このセッションでは、AWS がどのように 10 年以上にわたってこの技術を使用してきたか、そしてどのようにあなたのアプリケーションに機能フラグを導入することができるかを学びます。このセッションの特徴は、最小限のスライドと最大限のコードです。

セッションレベル:300 - Advanced

スピーカー:

  • Sebastien Stormacq, Developer Advocate, AWS
  • Olivier Leplus, Developer Advocate, AWS

スピードを重視するかクオリティを重視するか

最近では main ブランチへのプッシュで自動的にデプロイが走る自動 CI/CD の利用が一般的ではありますが、マージされたものがそのまま本番環境へデプロイされてしまうことにはリスクが存在し、少々怖くもあります。

過去にあった例として、ある金融関係のアプリケーションは本番環境を壊したくないあまりデプロイを慎重に行う必要がありました。その結果本番環境の更新が年に 2 回しかできませんでした。

既存の環境を壊さないことはとても大切ですが、それでもアプリの更新に時間がかかりすぎてしまうことはビジネスにとって良いこととはいえません。

デプロイとリリースを切り離す

このような問題を解決するため、我々はデプロイとリリースを切り離して考えることにしました。

というのも、デプロイすることにはさまざまな目的があるからです。リリースが目的の場合はもちろん、テストや検証目的でデプロイを行うこともあります。

Feature Flag を用いたリリース戦略ではさまざまな用途で行われるコードのデプロイとユーザーへの影響を切り離すことが可能になり、カナリアリリースのように徐々に一部のユーザーへ機能を提供するようなことも比較的簡単に行えるようになります。

フラグを使った実装例

本セッションでは AWS がこれまで 25 年近く行ってきた Feature Flag を用いたリリースの実装例をご紹介します。

Feature Flag の仕組みは単純な if else の分岐です。フラグの ON /OFF で機能の表示と API の利用を制限します。

AWS AppConfig と CloudWatch Evidently

AWS Systems Manager の機能である AWS AppConfig は、機能フラグやその他のアプリケーション構成の作成、管理、デプロイに使用できることはすでに多くの方がご存知で、使用されていると思います。CloudWatch Evidently は、AWS Systems Manager と一緒に使うことでフィーチャ別に高度な機能管理と検証を提供します。

本セッションでは Flag の管理を AWS AppConfig で行い、対象のユーザーに対する挙動の設定を CloudWatch Evidently で行う方法をご紹介します。

アプリケーションコードは実行時に CloudWatch Evidently に問い合わせを行います。CloudWatch Evidently であらかじめ設定した新機能が適用されるユーザーの条件と割合を決定します。また、特定の顧客(例えば、ベータテスター)に対して、アプリケーションの動作を設定することもできます。

CloudWatch Evidently とは

昨年リリースされたばかりのダークローンチ(Feature Flag とも呼ばれる)の実施と A/B テストという、似ているが異なる 2 つのユースケースに使用することができるサービスです。

CloudWatch Evidently

プロジェクトの作成

まずは Evidently の設定から始めます。AWS Management Console を開き、Application Monitoring の下から CloudWatch Evidently のタブをクリックして Create a project を作成します。

Evidently では、オプションでイベントを CloudWatch のログや S3 に保存することができるのですが今回はイベントを保存しないを選択しました。完了したら、Create project を選択します。

Feature を追加する

次に、Add feature を選択してプロジェクトに Feature を作成します。Feature 名を入力して、バリエーションを定義します。

この例では、2 つのバリエーションを作成し、Boolean 型を使用します。true は新しい機能であるゲストブックが編集可能であることを示し、false は、読み取り専用であることを示します。バリエーションタイプには、boolean、double、long、string が使用できます。

Override を定義する(Optional)

オーバーライドを使用すると、選択したユーザーに対するバリエーションを事前に定義することができます。例えば、ベータテスターであるユーザー「seb」には、常に新しい機能であるゲストブックが編集可能なバリエーションを受け取ってもらいたいと思います。

以下のように設定することでバリエーションの設定を Override することができます。

サンプルコードを生成

Feature を作成するとサンプルコードを生成できるようになります。このコードをアプリケーション側利用して Feature の分岐を行います

このデモでは簡単なウェブアプリケーションを使用しています。このアプリケーションは、JavaScript を使ってコーディングしました。AWS SDK for JavaScript と Webpack を使ってコードをパッケージングしています。また、DOM を操作して要素を隠したり表示したりするために JQuery を使用しています。このアプリケーションは、標準的な JavaScript と最小限のフレームワークを使用するように設計されており、この例がすべての人に受け入れられるようになっています。実際のプロジェクトでは、React や Angular など、より高度なツールやフレームワークを自由に使ってください。

まず、Evidently クライアントを初期化します。他の AWS サービスと同じように、認証のためのアクセスキーとシークレットアクセスキーを用意する必要がありますが、この例では、Amazon Cognito Identity Pools を使って、一時的な認証情報を受け取っています。

// Initialize the Amazon CloudWatch Evidently client
const evidently = new AWS.Evidently({
  endpoint: EVIDENTLY_ENDPOINT,
  region: "us-east-1",
  credentials: fromCognitoIdentityPool({
    client: new CognitoIdentityClient({region: "us-west-2"}),
    identityPoolId: IDENTITY_POOL_ID,
  }),
});

このクライアントを使って EvaluateFeature API を呼び出し、顧客に表示するバリエーションを決定することができるようになります。entityId は、顧客をセグメント化するための任意の文字列ベースの属性です。セッション ID や顧客 ID、あるいはこれらのハッシュなどを利用することをイメージしてください。featureName パラメータには、評価する機能の名前を指定します。この例では、先ほど作成した EditableGuestBook という値を渡しています。

const evaluateFeature = async (entityId, featureName) => {
  // API request structure
  const evaluateFeatureRequest = {
    // entityId for calling evaluate feature API
    entityId: entityId,
    // Name of my feature
    feature: featureName,
    // Name of my project
    project: "reInvent22",
  };

  // Evaluate feature
  const response = await evidently
    .evaluateFeature(evaluateFeatureRequest)
    .promise();
  console.log(response);
  return response;
};

レスポンスにはサーバーサイドで定義されたトラフィックルールに基づく Evidently からの割り当て情報が含まれます。

 {
 details: {
 launch: "EditableGuestBook", group: "V2"},
 reason: "LAUNCH_RULE_MATCH",
 value: {boolValue: false},
 variation: "readonly"
 }}

上で受け取った値に基づいてユーザーインターフェースの一部を隠したり表示したりする実装例です。基本的な JQuery の DOM 操作を用いると、以下のような感じになります。

window.aws
  .evaluateFeature(entityId, "EditableGuestbook")
  .then((response, error) => {
    if (response.value.boolValue) {
      console.log("Feature Flag is on, showing guest book");
      $("div#guestbook-add").show();
    } else {
      console.log("Feature Flag is off, hiding guest book");
      $("div#guestbook-add").hide();
    }
  });

上記のユーザーは readonly の権限が割り当てられたため編集機能は画面に表示されないため利用できません。

ローンチの作成

サーバー側で機能が定義され、クライアントコードに Flag の分岐が追加されたのでコードをデプロイして顧客に公開し、後日その機能をリリースすることにします。

コンソールに戻り、プロジェクトを選択し、Create Launch を選択しローンチの名前と説明を選び起動したい機能を選択します。

Launch Configuration セクションでは、各バリエーションに送信するトラフィックの量を設定します。また、複数のステップで起動をスケジュールすることもできます。

これにより、スケジュールに基づいて異なるステップのルーティングを計画することができます。例えば、初日にトラフィックの 10%を新機能に送り、2 日目に 20%を送る、といった具合です。この例では、トラフィックを 50/50 に分けることにしています。

結果

50%の確率で編集機能にアクセスできるようになり、かつ myId パラメータに'seb'を渡した時は 100%editable の結果が返るようになりました。

*デモのスクショを取り損ねたので Youtube 動画が公開され次第記事を更新します

所感

実際の案件でも Feature Flag  を採用することが増えてきたのですが、Flag 情報をどこに保存するかのベストプラクティスを模索中でした。CloudWatch Evidently を利用した例と便利な機能を発見できてよかったです。次に Feature Flag の実装を行う際にはこのやり方を試してみようと思いました。

References