はじめまして。
enzaのプラットフォーム開発のサーバサイドを担当している西丸です。

 

enzaというサービスを聞いた事が無い方もいらっしゃるかと思いますので、少し紹介させて頂ければと思います。

enzaは、バンダイナムコエンターテインメント社とドリコムの共同出資による新会社BXD社からリリースされたサービスでして、スマートフォン向けのブラウザゲームを配信するプラットフォームとなります。

そしてenzaのプラットフォームは、ドリコムにて開発を行っています。

 

enzaは、アプリのインストールが不要で、スマホの標準ブラウザですぐに遊ぶ事ができます。

現在、「アイドルマスター シャイニーカラーズ」と「ドラゴンボールZ ブッチギリマッチ」を提供しており、アプリを新たにインストールすることなく、スマートフォンの標準ブラウザでページを開くだけですぐにゲームをプレイできます。

そしてenzaのプラットフォームでは、ゲームをより楽しむ事ができるよう、フレンド・コミュニティ・チャットなどの機能を提供しており、友だちとその場ですぐにワイワイ楽しむ事ができます。

 

本記事では、私が所属しているenzaのプラットフォーム開発を行っているチームでの負荷試験について、紹介させていただきたいと思います。

enzaは複数の外部システムと連携している為、外部のシステムには影響を与えないような負荷試験環境を持っています。

本記事では、主にその辺りの知見についてご紹介ができればと思います。

enzaでの負荷試験の方針

複数の外部サービス連携を考慮した負荷試験方針

enza全体の構成図

 

まず、enza全体での大まかな構成についてです。

上の図のように、enza APIは、enzaで利用している外部サービスと各ゲームに連携しております。

enzaへの1つのAPIリクエストの中には、複数の外部サービスの処理が含まれているので、負荷試験の対象となる外部サービス以外はモック化するようにしています。

例えば、enzaと課金システム間の負荷試験を行う際は、ゲームとの通信部分と、その他enzaが利用している外部サービスとの通信部分をモック化して、負荷試験を実施しています。

 

しかし、外部サービスの通信部分をモック化して計測すると、モック化した箇所のレイテンシはモックサーバーの性能に依存するので、本来外部サービスを通した場合よりもレイテンシやスループットの性能が高く評価されてしまう場合があります。 これは、モックサーバーが非常にレイテンシが低くスループットが高い為です。

詳しくは後述しますが、このような場合はモック化している特定のリクエストを、本番環境で利用した場合と同程度のレイテンシになるように遅延処理をいれて動作するようにしています。

このようにすることで、enzaでの負荷試験方針である本番環境に近い形での負荷試験を実施しています。

 

新機能開発からリリースまでのチームの役割

我々enzaプラットフォーム開発チームでは、エンジニアリングチームを3チームに分けています。
チーム構成は、新機能開発等を行うスクラムチームが2チームと、サービスのインフラ面から運用に必要なエンジニアリングを担当するSREチームで構成されています。

enzaのプロジェクトでは、スクラムチームで新規機能開発を行った際、SREチームでの設計レビューを必須とする方針で開発を行なっています。

そして、全てのAPIに対してリリース前に負荷試験を実施し、問題がない事を確認してリリースするようにしています。

enzaは、機能開発毎に負荷試験を実施し、ボトルネックとなる箇所の洗い出しとチューニングを適宜行う事で、ユーザがストレス無くサービスを利用出来る事を担保しています。

 

新機能開発から本番反映までのフロー

新機能開発から本番リリースまでの各チームで役割を明確にして、下記のフローで本番リリースを行っています。

 

  1. 企画・設計
    まず新機能の仕様を決めた後、スクラムチームのエンジニアで設計を行っています。
    設計の際に、新機能の利用頻度を想定して、秒間ピークのリクエスト数の試算を行い、リソース最適化も合わせて行っています。
    下の図はawsリソースの試算の例になります。
  2. 新機能開発
    新機能開発では、1の設計を元に、チーム全員で分担して開発を進めていきます。
  3. 負荷試験のシナリオ作成
    負荷試験を実施するにあたって、まずはスクラムチームからSREチームに追加するAPI、試算の結果等の説明を行います。
    それを元に、SREチームにてシナリオの作成を進めていきます。
  4. 負荷試験の実施
    負荷試験実施では、後に詳しく説明させていただきますが、1台に対しての負荷試験でボトルネックとなる箇所の洗い出しを行い、複数台サーバを並べて、想定するピークのリクエスト数を捌くことが出来るかの試験を行っています。
    負荷試験結果から、SREチームにてチューニング対象と上げたAPIに関しては、スクラムチームにてチューニングを行い、再度負荷試験を行うようにしています。
  5. 本番リリース
    負荷試験で問題ないことが確認できると、本番へのリリースを進めていきます。

 

enzaでの負荷試験環境

次に、enzaが負荷試験を実施する際のシステム構成についてです。

 

負荷をかける側は、enzaではGatlingを採用しております。

Gatlingはスケールがしやすく、Gatlingクライアントを複数台立てて負荷をかける事ができます。

そしてGatlingでの負荷試験結果は、とても見やすいグラフがHTMLで出力されるようになっています。

 

負荷を受ける側は、enzaのAPIサーバで、enza APIサーバからenzaと連携する外部サービスへのリクエストは、モックサーバが受けるようにしています。

 

■ 負荷を受ける側(enza API)

■ 負荷をかける側

■ 負荷の監視ツール

 外部の連携システムを考慮した負荷試験

enzaは、複数の外部サービスと連携している為、負荷試験を実施する際には、外部のサービスに対して影響が出ないよう、外部サービスへの通信が発生しない構成にしています。

外部サービスに接続している箇所のコードを差し替えるのは、複数の外部サービスとの接続部分のコードの量が多い為、モックサーバを立てて、外部のシステムへの全てのリクエストをモックサーバに向くようにして、ダミーのレスポンスを返すようにしました。

 

1. 負荷試験用モックサーバの構成

# ダミーログイン用のnginxのconfファイル例

server {
  server_name enza-auth.example.com;
  root /var/www/response/auth;

  default_type "application/json; charset=UTF-8";
  error_page 405 =200 $uri;

  rewrite ^/login /dummy-login.json;
}

 

上のサンプルコードのように、モックサーバはnginxで、指定のjson等をレスポンスするようにnginxのconfファイルを用意しています。

 

2. hostsを書き換えるスクリプト

enzaの負荷試験で、連携している外部サービスにリクエストが飛ばないようにする為、enza APIサーバから外部サービスへのリクエストの向き先がモックサーバになるように、enza APIサーバのhostsを書き換えるようにしています。

ただ、負荷試験環境では、複数台のwebサーバが対象なので、全webサーバに対して、/etc/hostsを自動で書き換えるスクリプトを用意しています。

スクリプトの詳細は省略させていただきますが、各webサーバに対して、下記のようなコマンドで/etc/hostsに追加を行っています。

 

# /etc/hostsにモックサーバのドメイン追加
echo "#{external_system_ip} #{dummy_domain}" | sudo tee -a /etc/hosts

 

3. 負荷試験用の環境変数

我々の負荷試験では、ユーザのログイン処理を省略するなど、負荷試験独自の処理を行うケースがあります。
enzaでは負荷試験用のソースコードは、通常の開発ブランチでマージしており、本番にも負荷試験用のソースコードを入れるようにしています。

当初は、負荷試験用のブランチを切って、負荷試験のたびにブランチを切り替えて、開発ブランチから差分を取り込む運用をおこなっていました。

しかし、リリース前のenzaは新機能開発がメインで、機能追従が大変なので、継続的に負荷試験を行える環境ができるように、Rails/Phoenixの環境変数に負荷試験用の環境変数(stress)を用意するようにしました。

負荷試験用の環境変数を用意する事で、負荷試験環境のみで負荷試験用の処理が利用できるようにしています。

 

負荷試験用の環境変数を用いて、負荷試験用のルーティングを設定

# config/routes/stress.rb(負荷試験環境用のルーティング)
if Rails.env.stress?
  Rails.application.routes.draw do
    # stress用のAPIを定義
    get "stress_login", to: "stresses#stress_login"
  end
end

 

本番のデプロイ設定ファイルの一部

# 本番用の環境変数の設定
set :rails_env, "production"

負荷試験のデプロイ設定ファイルの一部

# 負荷試験用の環境変数の設定
set :rails_env, "stress"

4. 負荷の掛け方

enzaで行う負荷試験では、1台のサーバに対して負荷を掛けるのと、複数サーバに対して負荷を掛ける2パターンで行っています。

1台のenzaサーバに対しての負荷試験

1台のサーバに対して負荷試験を行う際は、ボトルネックとなる箇所を明確にする事を目的として、徐々に同時接続数を増やしていきます。

その中で、後述する監視ツールを用いて、ボトルネックとなっている箇所を明確にします。

ボトルネックとなっている箇所に関しては、スクラムチームとSREチームで改善の方針を検討し改修を行います。

 

複数台のenzaサーバに対しての負荷試験

そして複数台サーバを立てての負荷試験では、本番構成と同じスペックと台数で構築を行い、事前に用意している想定リクエスト数で負荷試験を行います。

本番構成と同じ状態での負荷試験を行うことで、enzaでの目標スループットを想定のサーバ構成で捌けるかのチェックと、目標のレスポンスタイムのチェックの性能テストを行います。

複数の外部サービスを連携する事で考慮しないといけないのは、外部のサービス毎にスループット・レイテンシが変わってくるということです。

enzaとしての目標スループット・レイテンシが出せていない場合は、外部サービス側に改善の依頼を行いつつ、enza側でもキャッシュ化や非同期化などスループット・レイテンシの改善を行うようにしています。

 

5. 本番環境に近い形での負荷試験の実施

enzaの負荷試験は、本番環境に出来る限り近い状態で負荷試験を実施しています。

先程の「4. 負荷の掛け方」の項目でも記載しましたが、外部サービスの性能によってスループット・レイテンシは変わります。

一部の外部サービスでレイテンシが悪い場合は、その外部サービスをモック化する際に、レイテンシが悪いことをシミュレートされたモックサーバになるようにしています。

例えば、enzaが基準としているスループットで、平均のレスポンスタイムが5000mscの外部サービスであれば、下記のようにecho_sleep 5にして、レイテンシが悪いことも考慮した上で負荷試験を実施しています。

location /external-service/dummy_api {
 echo_sleep 5;
 echo_exec /dummy_api.json;
 }
}

enzaで利用している監視ツール

enzaでは負荷試験で利用する監視ツールとして、NewRelic, AppSignal, Prometheus, Grafana, CloudWatch を採用しています。

これらの監視ツールを見ながら、ボトルネックの検出を行っています。

1. Gatlingレポート

スループットやレイテンシ等、基本的な負荷試験結果は、Gatlingのレポート結果から見るようにしています。

例えば、RailsのAPIサーバーの場合は、同時接続数が、全体のunicornワーカープロセス数に収まっている状態で、レイテンシが95%タイルで200msを超えるようなAPIは、チューニング対象としてチケットを発行してスクラムチームにて対応を行っています。

レスポンスタイムの確認

Gatlingの負荷試験を行った後、上のような負荷試験結果のレポートが出力されます。

GatlingレポートTOPの右側に簡易的なレスポンスタイム等が載っているので、まずは、enzaの目標である95%tileでレスポンスタイムが200mscを抑える事が出来ているかの確認します。

それとResponse Time Distributionの項目にて、リクエスト全体のレイテンシをこちらのグラフから確認するようにしています。

 

スループットの確認

次に、スループットの確認についてです。

上のグラフは限界負荷試験を行った時に出力されたもので、同時接続数を徐々に増やしていき、システムの限界を確認しています。

グラフを見るとある一定の同時接続数を超えると、レイテンシが不安定になっているので、システムの限界スループットが目標スループットを超えているかの確認を行います。

 

2. NewRelic, AppSignal

enzaでは、Phoenixも利用しているので、RailsのサービスではNewRelic, PhoenixのサービスではAppSignalでパフォーマンス分析を行っています。

NewRelicelixir_agentを公式サポートされるようになったので、現在PhoenixのサービスもNewRelicへの移行を進めています。
「1. Gatlingレポート」の結果から、レイテンシの悪いAPIに関しては、NewRelicAppSignalから、問題のあるクエリなどの調査を行っています。

 

上のグラフはNewRelicのグラフになりますが、レイテンシをミドルウェア単位で確認する事が出来るので、ボトルネックを探す際はこちらを用いて調査しています。

外部サービスへのリクエストのレイテンシも確認する事が出来るので、外部サービスに通信をかけて負荷検証をする場合は、enza本体だけではなく、外部サービスとの通信も含めてボトルネックを洗い出すようにしています

 

3. Prometheus, Grafana, CloudWatch

enzaでは、awsリソース, Rails, sidekiq, PhoenixなどのメトリクスをGrafana/CloudWatchでモニタリングしています。
負荷試験を行った際に、スループットが悪い場合は、利用している各リソース(EC2, RDS, DynamoDBなど)のCPU、メモリ、キャパシティユニットなどのメトリクスを確認して、どのリソースで詰まっているのかの調査を行います。

上の図はenzaで利用しているGrafanaの画面でして、リソースのデータをPrometheusで取得して、Grafanaで表示しているので、リソース全体でのボトルネックになっている箇所はGrafanaから確認を行えるようにしています。

 

 まとめ

今回は、普段enzaで行っている負荷試験について、外部の連携システムを考慮した負荷試験の実施の仕方から、ボトルネックの調査について紹介させていただきました。
今回の記事が、少しでも負荷試験を行う際の参考になれば幸いです。

最後まで読んでくださり、ありがとうございました。