Sixpackとfluentdを使ったA/Bテストの効果測定
APソリューショングループの相野谷(@ainoya)
こんにちは.リクルートテクノロジーズ APソリューショングループの相野谷(@ainoya)です.
このエントリでは,A/Bテストの効果測定を素早くモニタリングできるSixpackという A/Bテスティングフレームワーク,これとfluentdを組み合わせ,収集したコンバージョンログを元に Sixpackでの効果測定を行う例について紹介いたします.
皆さんはどうやってA/Bテストモニタリングを実施しているでしょうか?一般的に行われているのは, アクセス解析系のSaaSが提供するjsを,フロントエンドに埋め込んでテストパターンの出し分けをする方式かと思います. 今回紹介するSixpackは,集計サーバ・クライアントライブラリ一式を自前で用意する手間があるものの, その分拡張の自由がききます.これを活かすことで,
- 自分のサービスにサードパーティのjsを埋め込みたくない
- クライアントサイドjs以外のところでテストパターン制御をしたい
- 既に内製のA/Bテストモジュールがあるが,効果測定の作業が煩雑なので自動化したい
というようなケースにおいてSixpackはぴったりはまって価値を発揮してくれるツールかなと思っています.
Sixpackを使ったA/Bテストの効果測定
Sixpack はseatgeek社が開発しているA/Bテスティングフレームワークで, A/Bテストの実施と効果測定を行うことができます.名前の由来は多分A/B Testの"ab"からだと思いますが, こういったソフトウェアに対する向こうのネーミングセンスには感心させられます.
Sixpack自体はpythonで書かれており,以下の2つのサーバで構成されています.Sixpack APIサーバ 経由で蓄積されたA/Bテストの集計結果を,Webダッシュボードから即時確認できるのがいいところです.
- A/Bテストの実施・結果情報をクライアントと送受信するためのAPIサーバ
- 効果測定を可視化するダッシュボード表示サーバ
Sixpack APIサーバ
クライアントライブラリは多言語で提供されているものが利用可能ですが, 自前で実装したい場合はクライアント仕様定義が参考になります.といっても, API自体シンプルなので簡単に実装可能です.
APIのエンドポイントは以下の2種類しかなく,クライアントIDなどのパラメータを付与して GETリクエストで通信を行います.より詳細な使い方はREADMEをご覧ください.
participate
: A/Bテストの振り分けを情報取得する(登録)convert
: A/Bテストのコンバージョンを登録する
SixpackはデータストアとしてRedisを利用しており,これにA/Bテストの測定情報を保管します.
高負荷な環境で使用する場合は,APIサーバを複数構成にするほか,Redis Sentinelの利用を検討して みてもよいかもしれません.Sentinelの使用オプションはSixpackのconfig として提供されています.(動作未確認)
Sixpack Webダッシュボード
Sixpack API経由で保管されたA/Bテストの測定情報のサマリは,APIサーバとは別に用意された Webアプリのダッシュボードから閲覧できます. 表示画面のサンプルを下に示します.サマリではA/Bテストの振り分け発行状況の時系列データや, コンバージョン率を閲覧可能なほか,G検定 による尤度測定の結果も同時に確認することができます.
fluentdのログをSixpackに流してA/Bテスト効果測定を行う
ここまでSixpackの簡単な概要説明をしてきましたが,もし自分のところで使ってみたくなったとしても,
- 既に導入しているA/Bテストモジュールがあり,競合する
- Webサービスのリクエストを捌いている最中,アプリケーション内部で余計なAPIとやりとりする不安
などの諸々の心配事があることかと思います.そこで代案として,fluentdに処理を任せて,ログから SixpackへA/Bテスト情報を蓄積するのはいかがでしょう.ログデータをSixpackのAPIに投入する Outputプラグインfluent-plugin-sixpackを書いてみたので, これを使って実際にログ出力されたA/Bテスト情報をSixpackで集計する方法を紹介します.
fluent-plugin-sixpackの設定
このプラグインは単にログをSixpack APIの要求するパラメータに合わせるだけのものなので, 利用方法といっても別段難しいことはないです.td-agent.conf
への最低限の記述としては sixpackのAPIサーバのURLさえ記述しておけば使えるようにしています.
1 2 3 4 |
<match app.abtest.log> type sixpack sixpackapi_url http://localhost:5000/ #sixpack APIのURL </match> |
プラグインが期待するログ構造はSixpack APIの仕様にそのまま合わせる形で,2パターンあります.
1 2 3 4 5 6 7 8 9 10 11 |
# A/Bテストの振り分け参加データ {"record_type":"participate", # A/Bテスト情報の種別: "A/Bテストの振り分け参加情報" "experiment":"header-color", # A/Bテストのテスト名 "alternatives":"red,green,blue", # A/Bテストパターンの全選択肢 "alternative":"red", # A/Bテストで選択された値 "client_id":"ID-0000-0001"} # A/Bテスト参加者の識別ID # A/Bテストのコンバージョン達成データ {"record_type":"convert", # A/Bテスト情報の種別: "A/Bテストの振り分け参加情報" "experiment":"header-color", # A/Bテストのテスト名 "client_id":"ID-0000-0001"} # A/Bテスト参加者の識別ID |
利用の際はfluent-plugin-record-modifierや fluent-plugin-parserを使ってログ構造を上記仕様に合わせこんでいく 必要があります.プラグイン自体にもキー名を変更できる程度のconfigを用意しているので, READMEで使えるオプションを確認してみてください.
Sixpack/fluentdの組み合わせを試す
fluentdとSixpackを使ったA/Bテスト効果測定の仕組みを簡単に試せるよう,これらをセットにした Dockerfileを用意しました.ビルド済みイメージをDockerHubに 置いているので,下記使用例をご参考にぜひ使ってみていただければと思います.
Dockerコンテナの起動
1 2 3 4 |
docker pull ainoya/sixpack docker run -t --name sixpack-server \ -p 49022:49022 -p 24224:24224 -p 5000:5000 \ -p 5001:5001 sixpack-server |
ログの投入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
## パスワード"sixpac"でコンテナにSSHログイン ssh -o UserKnownHostsFile=/dev/null \ -o StrictHostKeyChecking=no root@localhost -p 49022 ## A/Bテスト参加ログ投入 /opt/td-agent/embedded/bin/fluent-post -t 'sixpack.test' \ -v 'record_type=participate' -v 'experiment=header-color' \ -v 'alternatives=red,green,blue' -v 'alternative=red' \ -v 'client_id=id' ## A/Bテストコンバージョンログ投入 /opt/td-agent/embedded/bin/fluent-post -t 'sixpack.test' \ -v 'record_type=convert' -v 'experiment=header-color' \ -v 'client_id=id0001' |
SixpackのWebダッシュボード画面でデータ更新を確認
1 |
open http://localhost:5001/ |
まとめ
A/BテスティングフレームワークSixpackとfluentdを使って,ログデータからA/Bテストの効果測定を行う 方法について紹介しました.クライアントライブラリから直接SixpackのAPIを叩くのをやめ,ログをfluentd に流してAPIリクエストを代理させることで,既存のWebアプリケーションの変更を最低限にしつつA/Bテスト 効果測定の仕組みを導入できます.この先の応用としては,ログデータさえあればA/Bテストの効果測定を行える という利点を活かして,
- WebUIの枠を超えたAPIのA/Bテスト実施
- さらにWebアプリの枠を超えたミドルウェア・インフラのA/Bテスト実施
など,ありとあらゆるインタフェースをA/Bテストできるようになったら面白いなぁ,と考えています.