CloudFrontとLambda@EdgeでA/Bテストをサクッと導入してみる

Amazon CloudFrontとLambda@Edgeを使い、アプリ改修なしにA/Bテストする方法を紹介します。
2020.06.29

ウェブサイトでA/Bテストする場合、以下の流れで作業します

  1. A/B 2パターンのサイト(機能)を用意
  2. ユーザーをどちらかに振り分ける
  3. A/Bのどちらが有効か評価

この2つ目のユーザー振り分けは実現方法が色々考えられます。

本ブログでは、Amazon CloudFront とエッジコンピューティングの Lambda@Edge(以下L@E) を使い、アプリ改修なしに、同じURLを利用しながら、ユーザーをA/Bに振り分ける方法を紹介します。

完成図

ポイント

ポイントは次の3点です

  1. アプリケーションは改修しない
  2. L@EでA/Bテスト用のCookieを発行し、A/B振り分ける
  3. CloudFrontではA/Bテスト用のCookieをキーにキャッシュ

前提として、A/Bを実装したアプリケーションに異なるホストを割り当て、URIパス部の仕様は同じとします。

1. アプリケーションは改修しない

アプリケーションでは A/Bそれぞれの機能を実装することにのみ専念します。

振り分け処理はアプリケーションの外側で実装します。

2. L@EでA/Bテスト用の振り分けを行う

Lambda@Edge(L@E)を利用すると、CloudFront(CDN)のエッジでLambda関数を実行できます。

この機能を利用し、A/B の振り分けを行います。

具体的には L@E で

  • ユーザーには A/B テスト用のCookieを発行
  • Cookieを利用してユーザーをA/Bのどちらかに一貫して振り分ける
  • URIパス部を維持したままA/B(異なるオリジン)間でホスト名だけを切り替える

という処理を行います。

L@E は CloudFront のキャッシュ前後のリクエスト・レスポンス、合計4箇所の処理に割り込めます。

図は公式ドキュメントから

A/B テスト向けに割り込む L@E 処理は以下の通りです。

ビューワーリクエスト

CloudFront がビューワーからのリクエストを受け取ると、リクエストされたオブジェクトが CloudFront キャッシュ内にあるかどうかを確認する前に、関数が実行されます。

初回リクエスト時などA/Bテスト用のCookieキーが存在しない場合、この関数内でリクエストヘッダーにCookieを設定します。

オリジンリクエスト

CloudFront がリクエストをオリジンに転送する場合にのみ関数が実行されます。

Cookieの値を見てリクエスト先ホストを動的に切り替えて、A/B のルーティングを行います。

オリジンレスポンス

CloudFront がオリジンからのレスポンスを受け取った後、レスポンス内のオブジェクトをキャッシュする前に関数が実行されます。

フェッチしたオリジンに対応する Set-Cookie するレスポンスを追加します。

ビューワーレスポンス

リクエストされたファイルがビューワーに返される前に関数が実行されます。

今回は何もしません。

3. CloudFront の設定

CloudFront の Behavior でL@Eを紐付けます。

さらに、A/Bテスト用Cookieキー(例ではpool)をホワイトリスト化し、このキーでキャッシュするように設定します。

cURL で動作を確認

初回アクセス時

A/B テスト導入後、CloudFront に初めてアクセスすると、オリジンをランダムに割り当て、次回以降もそのオリジンにリーティングされるようにCookieを発行します。

初回リクエストなので、CloudFrontでは当然キャッシュミスします。

$ curl -D - https://XXX.cloudfront.net/
...
Set-Cookie: pool=b
X-Cache: Miss from cloudfront
...

Origin B

レスポンスには、フェッチしたオリジンに対応する Set-Cookie が含まれています。

2回目のアクセス時

発行された Cookie をリクエスト時に渡すと、キャッシュ済みコンテンツが返却されます。

$ curl -D - --cookie "pool=b" https://XXX.cloudfront.net/
...
Set-Cookie: pool=b
X-Cache: Hit from cloudfront
...
Age: 2

Origin B

オリジンへの問い合わせは発生しません。

ハンズオンで手を動かしてみよう!

実装方針がわかったら、ハンズオンで具体的に動くコードを実装してみましょう。

今年のオンライン版AWS Summit向けに作成された次のワークショップがおすすめです。

GitHub - justasitsounds/lambda-edge-lab: A demo lab showing how to execute A/B testing using lambda@edge functions

まず CloudFront と S3 を連携するだけのサイトをデプロイし、次のステップで A/B テスト用の L@E 関数をデプロイします。

AWS Serverless Application Model(SAM) でシュッとデプロイできるように作られているので、簡単に完遂できます。

ハマリポイントをあげるとすれば、オリジンリクエスト時のLambda@Edge関数のホストの処理です。

S3オリジンの場合のイベント構造は、以下のようにホスト情報が 2箇所 に散在しています。

両方を正しく書き換えないと SignatureDoesNotMatch エラーが発生するので、ご注意ください。

{
  "method": "GET",
  "headers":
   {
     ...
     "host":  [ {
       "key": "Host",
       "value": "site-a.s3.amazonaws.com" } ]
   },
  "origin":
   { "s3":
      { 
        "domainName": "site-a.s3.amazonaws.com",
        ...
      } },
      ...
}

カスタムオリジンの場合も同じです。

最後に

Amazon CloudFront と Lambda@Edge を使い、アプリ改修なしにユーザー振り分けを実現する方法と実際に実装するワークショップを紹介しました。

  • CloudFront を利用している
  • 異なるホスト、同じURIパスで A/B の2種類の機能を提供する

という前提を満たしていれば、ワークショップと同じ発想を別環境に転用できるかと思います。

リクエストにinterceptできるエッジコンピューティングは、あれこれできて面白いですが、くれぐれもやりすぎにはご注意ください。

それでは。

参考