こんにちは。CD部の金井と申します。今回は大規模な負荷試験の準備に関する事を書いていこうと思います。
そもそも負荷試験って何の為にやるの?
リリース当初のゲームに良くありがちな事ですが。
ユーザーアクセスの集中によってサーバーが落ちてしまい、そのまま長期メンテナンスに入った、と言うような事柄は目にした事があると思います。
他にも新イベントが実装された時や期間限定のガチャが開催された時にユーザーが数多に戻ってきて、レスポンスが重くなったり。
端的に言うならば、負荷試験はそのようなサーバー不具合を防ぐ為に行うものです。
もう少し具体的に書くならば、
リリース前に行う負荷試験としては、
・初日に起こり得るであろうリセマラに対して
・実装したイベントに対して
・また全てのAPIに対して
・レスポンスは許容出来る時間内に返せるか?
・用意したAPIサーバー、データベースサーバー、キャッシュサーバー、リアルタイムサーバー等々は想定したユーザー数のプレイに耐え得るか?
・想定以上のユーザー数がプレイに興じた場合どのような影響が発生するか? サーバー自体は落ちずに、レスポンスが重くなる程度の影響に留められるか?
・万一どこかのサーバーが落ちた場合、障害によって如何なる影響が発生するか? ユーザー不利益は発生しないか? 自動復旧の仕組みは整っているか?
という事を確認する必要があります。
また、リリース後にも新機能の実装時などには負荷試験を行うべきですが、その時は新しく実装された、影響された範囲だけを追加で調査すれば基本的に大丈夫ですので、そこまで大変ではないです。
負荷試験の為に何を準備すれば良いの?
端的には以下の3つです。
- どのような負荷試験を行うか、調査する事を明確に定めた負荷試験計画書
- 実際にサーバーに対して負荷を掛ける、リクエストを投げる為のシナリオプログラム
- そして、負荷試験の為の環境
一つ一つ見てきましょう。
1. 負荷試験計画書
これが無いと負荷試験は始まりません。
当たり前な事ですが、明確な目標やゴールが見えないままに負荷試験を行ったところで、大した成果物は発生しません。
その為に、上記で書いたような事を明確な数値、試験内容として定める必要があります。
例えば、
- 想定DAU(Daily Active User……1日に遊ぶユーザー数)は30万人とする。
- サーバーのCPU、メモリはユーザーが最もプレイする時間帯でもそれぞれX%, Y%までに留まるようにする。
- 負荷試験中にユーザーデータを保存しているデータベースの1台を落とす。N分以内に自動復旧し、正常に遊べるようになる事を確認する。
等々とても割愛していますが、様々な状況を想定して、それらに対する目標値を定めなければいけません。
さて、ここでタイトルにもあるようにDAU30万を想定するならば、RPS(Request Per Second……秒間に飛んで来るリクエストの数)はどれ程を想定すれば良いのかを算出してみましょう。
計算式は以下です。
RPS = DAU × 最大でどれだけの割合の人数が同時に遊ぶか × リクエストは何秒に1回叩かれるか
まずDAUは、上の人……要するに製作しているゲームのターゲット層、想定プレイ人数を定めているお偉い人から聞き出しましょう。ここでは表題の通り、30万とします。
次に最大でどれだけの人数が同時に遊ぶかですが、これに関しては自分の経験にはなりますが、DAUの10%~30%で定めた事がありました。ここでは多めに30%としておきましょうか。
そして最後のリクエストは何秒に1回叩かれるかに関しては、これは実際に遊んでみて確かめてみましょう。ここでは10秒に1回サーバーにリクエストが飛ぶ、と仮定しましょう。
すると、上の式は以下となります。
RPS = 300000 × 0.3 × 0.1
RPS = 9000
要するに、秒間に9000のリクエストが捌ければ良い訳ですね! 大変だぁ。
また、負荷試験というのは最初からこんな9000RPSなどといったデカい値を数多のサーバーに対してぶん投げるのではなく、サーバー単体での性能から測っていくものですが、この単体の性能が貧弱だと想定人数を捌く為に必要なサーバー台数も必然的に爆上がりします。
例:
1サーバー辺りのスレッドが20あるとして、リクエストが平均200msecだった場合、そのサーバー1台が秒間で捌けるリクエスト数は 20 * 1000/200 = 100となる。
9000RPS…秒間リクエスト9000を捌く為に必要なサーバーの台数は9000/100 = 90台。
また、スレッド数20を実現出来るサーバーは、AWSだとオンデマンドで大体1時間に1\$の料金が発生するサーバーになる為、この性能をオートスケーリングとか何もなしでオンデマンドで常に立てておくと、一月辺りの料金は、
- 1日に1台で掛かる料金が24\$
- それが90台で30日の2700倍で64800\$
日本円に換算して大体674万円!
ひゃっほう!
因みに言うとこれ、APIサーバーだけで掛かる金額なので、データベースサーバーとかキャッシュサーバーとか鑑みるともっと増えます。
何が言いたいかと言うと、サーバーコストを削減する為にも綺麗で速いコードと言うのは常日頃から心がけておくべきですね。リファクタリングによって月100万単位でコストを削る事も可能になるかもしれませんよ。
まあ、上記の例だとリファクタリングより、スポットインスタンスやらオートスケーリングやらを活用した方が安くなると思いますけど。
2. シナリオプログラム
準備が一番大変なのは、計画書よりも環境よりも、こいつです。
結局、疑似的にクライアントを作るようなものですからね。
基本、全てのAPIに対して投げられるようにシナリオプログラムは作ります。そして、データベースのインデックスが効いているか等を確かめる為にも、ある程度ランダム性も持たせなければいけません。
場合によっては、暫く待たなければ結果が出来ない仕様のデータを早送りして即座に結果を受け取れるようにさせる、フレンドからの干渉によるデータを疑似的に作成する、等といった負荷試験専用のデバッグAPIなどをSV側に作ったりする必要も出てきます。
これに関しては開発中からコツコツ作っておくのが吉だと思います。負荷試験をすると決めてから作るとした場合、余裕で二週間とかは掛かると思います。
また、その負荷を掛ける為のツールは幾つかありますが、自分はJMeterとLocustに関して使用経験があります。
JMeterはシナリオに書き辛い部分があったり、実際に負荷試験本番で使う場合はCUIから結構複雑なコマンドを投げたりしなければいけませんが、出力結果を纏めたり比較したりするツールがGUIに内包されており、そこは便利です。
Locustはシナリオは書き易く、実際に負荷試験本番で使う場合もGUIを使って簡単に操作出来ますが、結果はCSV等に出力出来るだけで、そのCSVからまた色々比較検討する際にはExcelなどを使わなければいけません。
総合的に言えばLocustの方が良い印象です。
負荷試験はプロジェクトで一回切りのものでもありませんし、手間を掛ける煩雑さに関しても
負荷試験を実行する > 負荷試験の結果を纏める
な事には間違いありませんから。
3. 負荷試験の為の環境
リリース後にも負荷試験を行う必要性が出てくるであろう事も考慮すると、リリース前の本番環境を負荷試験環境にしたりするのはあんまりお勧めしません。
ただ、基本的に使う必要の無い負荷試験環境を立てっぱなしにしていては、勿論無駄なお金が掛かります。
また負荷試験の実行フェーズでも、本番環境と同等の環境にしていた場合、負荷試験をしていない時間には出来るだけサーバーは落としておくべきです。
上記の例だと、その本番環境と同等にしていた場合には、19時に帰宅して翌日の10時に出社するだけでも1350$掛かりますからね! もし金曜に立ち上げっぱなしにしてしまった場合、月曜に気付いた時にはお給料以上のお金が吹っ飛んでいますね! とっても怖いですね!
そんな事で、負荷試験環境はスクラップ&ビルドが簡単な状態にしておく事が望ましいです。……とても大変ですけど。
また、本番想定の為、DBの負荷を正常に測る為に想定ユーザー分のダミーデータをDBに突っ込んでおく必要もあります。
これに関しては、最初にリセマラを想定した負荷試験をすれば行う必要も無くなる事もありますが、スクリプト等を別途組んでおけば楽になるでしょう。
負荷試験スタート
さて。
ここまで整えて、やっと負荷試験はスタートを迎える事が出来ます。
上記の通り、スタートを迎える為に準備する事柄は数多にありますので、負荷試験フェーズに入ってからの一からの準備ではもう手遅れ、残業塗れになる事は想像に難くありません。
そんな事にならない為にも、開発フェーズから負荷試験の事は考えておく事をお勧めします。
*****
「よし! 単体サーバーで150RPSまで捌けるようにコードの改善も出来たぞ! 性能が1.5倍! コストは2/3! ダミーデータもたっぷり入れたし、これからサーバー台数を一気に増やして本番想定の負荷試験だ!」
「すまん、リリース前だが仕様変更だ」
「えっ、ちょっと、この仕様テーブル定義かなり変わるじゃん、土日掛けて突っ込んだ膨大なダミーデータ無駄じゃん」
「すまんが頼むね」
「…………」
頑張りましょう。