リリース時の負荷対策


この記事は、ドリコム Advent Calendar 2014 - Adventar の22日目の記事です。

21日目は、ウッチーことtakao.uchikawaさんによる、「スマートフォンでの脱出ゲームの作り方」です。

自己紹介

シモーネと呼ばれています。
PHPとかJavaとかPerlとかで、ポータルサイトとか作ってました。
ドリコム来てからはRuby書いてましたが、今はPMやってます。
最近は、たまにサーバー入って、ちょっとコマンド叩く位。

ドリコム釣り部 裏部長やってます。
エリアトラウト、ヘラブナ、シーバス、投げ、ジギング、キャスティングなど、いろいろやってみて釣りを楽しんでます。

書くこと

アプリリリース時の負荷対策を、忘れっぽい自分の為に、覚えている範囲でまとめてみます。

余談ですが、同じリリース時の負荷対策として、釣った魚を写真撮ってからリリースする場合の、魚への負荷対策方法をご紹介します。

釣り上げた時に

魚への負荷を軽減する為に、この様にホースを魚の口に突っ込んで、海水をエラに届けます。酸素を供給して、写真撮影に備えます。

そして写真撮影

魚が落ち着いたら、写真撮影して、素早くリリースします。

さて本題です

状況としては、ネイティブアプリの開発で、大詰めを迎え、最後の仕上げ期間で、負荷テストによりサーバーサイドの問題点あぶりだしと、改善を行いました。

実際の作業は、何名かの方にやっていただいていますので、細かい部分は分からない事が多いです。

環境

  • Rails4.0(2014年1月頃)
  • Mysql
  • Amazon EC2(JMeter動かす用)

等々、一般的なrails環境です。

使用したツール

  • JMeter
  • NewRelic

手順

  1. 負荷テスト用環境を構築
  2. JMeterのテストシナリオを作成
  3. NewRelicの設定をrailsに追加
  4. テスト用データ生成スクリプトでデータ作成
  5. AmazonサーバーにJMeter設置
  6. 負荷テスト実行
  7. テスト結果確認
  8. ボトルネックの特定と修正

6~8の繰り返し。

負荷テスト用環境を構築

本番環境を事前に構築してしまうのが一番いいのかもしれませんが、お金かかるので、縮小バージョンで構築。

JMeterのテストシナリオを作成

自動で作成できる方法もある様ですが、サーバーにはAPIしか実装されていないので、一つづつ設定してもらいました。
負荷テストあれこれ-JMeterの使い方-
この辺が参考になりそう。結構大変だったそうです。

シナリオは、Getアクションをひたすら叩くもの、Updateメソッドをひたすら叩くもの、新規にユーザーデータを作成するものの3つの視点で行っています。

jmxフォルダー下

起動用スクリプトは、こんな感じです。


getter.sh
#! /bin/sh 

FILENAME=`basename $0`
BASENAME=${FILENAME%.*}

JMETER_PATH=../../../vendor/apache-jmeter-2.10/bin/jmeter

JMX_PATH=./jmx/${BASENAME}.jmx
JTL_PATH=./${BASENAME}.jtl

DOMAIN=localhost
PORT=3000
CLIENTS=20
LOOPS=10

${JMETER_PATH} -n -t ${JMX_PATH} -l ${JTL_PATH} -JDOMAIN=${DOMAIN} -JPORT=${PORT} -JCLIENTS=${CLIENTS} -JLOOPS=${LOOPS}

jmxは、GUIで操作です。ファイル自体は、xmlみたいですね。


NewRelicの設定をrailsに追加

NewRelicのサイトでsign upして、gem追加して、newrelic.ymlをconfig下に入れるだけの簡単設計。

テスト用データ生成スクリプトでデータ作成

ユーザーデータと関連するデータを100万ユーザー分作ってもらいました。

AmazonEC2にJMeter設置

アプリではデータセンターのクラウド環境を使っていますが、JMeterは一時的にしか使わないので、サービスとは別にEC2に設置。

負荷テスト実行

スレッド数が多すぎたのか、out of memoryで、何度か止まってしまいました。
スペックには、余裕を持っておいた方がよさそうです。

テスト結果確認

JMeterでも十分ですが、NewRelicでも統計レポート的なものが見れます。
気になる所があれば、NewRelicなら画像のぼかしを入れた部分をクリックすれば、細かく見ていけます。

ボトルネックの特定と修正

インデックス忘れなどは、上記の方法で十分発見できます。
修正したら、またテスト実行。
ものによっては、アプリのスクリプトに処理速度を計測するコードを各所に入れて、ボトルネックを探っていきます。
参考:Rubyで処理の時間計測方法

ハマった点

N+1問題
想像以上に、これでレスポンスが悪化していた。
NewRelicでは、クエリは高速に処理されていて、ビューの一部などとして計測される為、特定しずらい。
クエリが無駄に多く発行されている場合は、これを疑うといいかも。

始めっから、bullet入れといてもらえばよかった。。。

テストデータが、おかしかった
通常生成されるはずがない量のユーザー所持ユニットのデータが生成されてしまい、しばらく気付かずに、過剰に負荷対策していた。

最後は

どうしても、改善する事が難しい場合は、企画と相談して、仕様を変えたりしてしまうのがいい場合もあります。
例としては、1ユーザー当たりの所持ユニット数の上限が1000体だとしたら、300もあれば、いいんじゃないか?などです。

反省点

シナリオから漏れているAPIがいくつかあり、そんなに負荷が高くないのではないかとスルーしていたら、リリース後、危ない場面があった。弊社のスーパーDBAが一瞬で直してくれましたが。

この時は、リリースまでに間に合いましたが、負荷対策は、もっと早くやらせてくれ!と担当エンジニアの方に言われてしまったので、サーバー代かかっても、もう少し早く開始しておきたいです。

タイムリミットなので、今回はこんな所で!
この記事を書くに当たって、いろいろな方に、レビューなどご協力いただきました。ありがとうございました。

明日は、ドリコム技術の未来を切り開く男 tomofusaさんです。