なぜ Forkwell はリリース初日にサーバダウンを繰り返したのか

こんにちは。Forkwell の中の人、大岡(おおか)です。

本日は、お恥ずかしながら Forkwell リリース初日の失態について、詳細をお話しようと思います。
当初からのユーザーの方はご存知かもしれませんが、先週4月3日(火)に Forkwell がリリースされた際、殺到するアクセスの負荷に耐え切れず、深夜までサーバダウンを繰り返しました。

なぜそんなことになったのか。
端的に言えば、ロクに負荷テストを行ってなかったからというのが真相という間抜けなオチなのですが。

初めての慣れないディレクター業務で連日サービスリリースのことで頭が一杯になっていた大岡は、負荷テストのことがすっぽり頭から抜けていました。
私も過去、Apache Bench や JMeter で負荷テストを行った経験はもちろん何度もあったのですが、今回はウソのようにきれいさっぱり忘れてしまっていました。
ディレクター業務に加え、システム設計やサーバ構成等も大岡の担当でしたので、そのあたりの責任は免れません。

システム構成の詳細を説明すると、Forkwell のサーバは全てAWSを使用しています。
当初、Amazon EC2 のラージインスタンスをWebサーバ用に3台、DB用に1台を用意。
Webサーバは Nginx、アプリケーションサーバに Unicorn、DBは MySQL

3台のWebサーバは、Amazon Elastic Load Balancingで分散され、始まったばかりのサービスなら誰もが十分過ぎるスペックだと思ったでしょう。私もそう思ってました。

インターネット上にサービスを公開したのはだいたい午前9:30ごろ。そして10:30をもって正式リリースの告知を行いました。
当初は大量に画像を読み込むトップページこそ多少重かったものの、サービスは順調に稼働していました。

ところがお昼前に立て続けにTechCrunchの記事@ITの記事が公開されるやいなや、アクセスが殺到。そして500エラーが頻発。血の気が引きました。
Webサーバ、アプリケーションサーバ、DBサーバの再起動を行うことで一時的に改善するのですが、すぐにまた同じ現象が起きてしまいます。

殺到するアラートメール

どうもアプリケーションサーバに問題があるらしく、Unicorn のログを見ると「ActiveModel::MissingAttributeError」という謎の例外が大量に発生しています。

こんなエラーは、チームの誰も見たことがありません。

しかしDBサーバのCPU使用率は全く上がっていなかったため、まず行ったことは MySQL 設定のチューニング。
Unicorn のDBとの接続のところに問題があるために、ActiveRecord のインスタンスが不完全になっているのではとの推測のもとに、MySQL の同時接続数を増やすことで対応できるのではないかと思ったのです。
しかし状況はあまり変わらず。その後も色々と試行錯誤を重ねましたが、Nginx の外部コネクション数を制限することで、束の間の小康状態を得ることができました。

しかしそれでも、アクセス3〜4回に1回は500エラーが発生する状態でしたので、ゆっくりしているわけにもいきません。
この時点で午後5時。このままではサービスを一時閉鎖してごめんなさいするしかないのでは、という空気が社内に広がっていました。

でもその前に、大岡には試してみたいことがありました。
アプリケーションサーバを Unicorn から Phusion Passenger に変更することです。

Passenger for Nginx ロゴ

前々から Nginx + Passenger の組み合わせは最強という話は聞いていましたし、自分のブログでも2年以上使っていてその安定性に驚愕していました。
当初はこの組み合わせでいくつもりだったのですが、担当エンジニアのリソース不足で慣れないものを入れる余裕がなく、開発環境と同じ Unicorn のままリリースしてしまっていたのでした。

ここは当初の予定通り、多少の時間をかけてでも Passenger に変更すべきだと思いました。
馬場くんにNginxでアプリケーションが動くか検証してもらい、その環境をEC2に展開、それを AMI (Amazon Machine Image) に保存し、本番のサーバを1台ずつ落として入れ替えるという作業に、およそ4〜5時間を要しました。

希望が見えたのは、Webサーバの1台目を Passenger に入れ替えたとき。
Unicorn 環境では Load Average がすぐに5とかに上がってどうしようもなかったWebサーバが、Load Average 0.1〜0.2 付近に収まり、しかもそのサーバに振られたリクエストでは一切500エラーが発生しなかったのです。

大急ぎで全てのサーバを Passenger に入れ替え。するとウソのように500エラーは影を潜め、ブラウザからのレスポンス速度も目に見えて改善しました。
これには開発チーム全員が、声を上げて喜びました。

私も人生の中でこれほど安堵した瞬間はなかったほど。Passenger のおかげで、その日はチーム全員、終電までに帰宅することができました。
(しかし実は、Nginx + Passenger の SSL のリダイレクト設定に問題があり、Passenger に変更してからユーザー登録ができない状態になっていたらしく、そこから翌日の午前10:30くらいに対応するまでそれが続いてしまっていました。申し訳ありません)


ただ、Unicorn も Rails でアクセス数トップランキングに入るようなサービスで普通に使われていますし、携帯ソーシャルゲームでも Unicorn を使用している会社が多いようです。
ですので、ちゃんと設定を細かく詰められるインフラエンジニアがいて、それなりの台数を用意できるのであれば問題ないのでしょう。

しかしそうでない場合は、Passenger にしておいたほうが無難のようです。
Passenger に変えてからは、アプリケーションサーバがネックになることは全くなくなりました。むしろたまにDBサーバのほうが足を引っ張ることがあるくらい。

Forkwell のシステムはトップページが一番重く、コンテンツを組み立てるために尋常ではないクエリー数が発生しています。
これについてはクエリーの最適化が行われていないためであり、現在に至るも対応中です。
PVそのものは数万という、Webサービスとしては大した規模ではなかったのですが、この1アクセスで大量のクエリーが発生するという特性が、Unicorn と相性が悪かったのではないかというのが、開発チームの見解です。

あと「どうして Heroku にしなかったのか。Heroku ならそんな心配はなかったのに」というご意見を多くいただいたのですが、それについては以下のような理由です。

まず、サービスを日本で見てもアメリカで見ても、レイテンシのストレスが極力少なくなるようにしたかった。
世界展開を見据え、シリコンバレー周辺で見たときに最も快適に使えるようにしたいと考えていました。
ですので、サーバの物理的な設置場所は米国西海岸リージョン以外に考えられませんでした。

次に、日本語全文検索のソリューションを必要としていたこと。
将来的に Forkwell では、スキルタグのページにそのスキルに関連する情報を外部のものも含めて集約したいと考えているため、今はそれほどでなくてもその内に日本語全文検索が必要になってきます。

Heroku にしなかった主な理由はその2つ。
あと、私は古いタイプのエンジニアなので、中で何が起こってるかよくわからない PaaS を本番のサービスに使うのはまだ何となく不安があるというのもありました。


現在、Forkwell はいちおう安定運用できているのですが、「まだ重い」という声もちらほら聞こえます。
計測するとHTMLページ自体は400ms〜500msで返しており、速いとは決して言えませんが「重い」と言及されるほどのレベルではないようです。

何が重く感じさせているのかというと、トップページの各スキルのアクティビティに付随するユーザーの Facebook プロフィール画像。
これが読み込みにかなりの時間を食っています。リソースURLに毎回リダイレクトがかかるため、いちいち時間がかかる。またリダイレクトのせいで、ページに同じユーザーの画像が10回出てきても、キャッシュされず10回リクエストをかけてしまう。

Forkwellトップページのプロフィール画像

これがページのレンダリングを非常に遅くしている原因です。
ですので本日、トップページの Facebook プロフィール画像を読み込む箇所に Lazy Loading 対応を行いました。
ページのレンダリングを先に行い、画像は適宜読み込むようにしましたので、これにより体感的にはいくらか改善されているはずです。

また先に挙げたクエリーの最適化、プロフィール画像のキャッシュ、静的ファイルの置き場所をCDN対応する、等の施策を行っていく予定であり、今後さらに改善されていくものと思います。


以上が、Forkwell リリース当日の不始末の概要です。
これをご覧の優秀なエンジニアの方々はこんなミスをすることはまずないと思いますが、他山の石とでもしていただけましたら。


【2012/4/16 追記】
続編を書きました。
お寄せいただいた意見や質問に対してお答えしています。

ライタープロフィール
おおかゆか(oukayuka)
Forkwell の発案者でプロダクトマネージャー。
エンジニアと企業が幸せな関係を結べるようなしくみ作りとそれを世の中に広めるのがお仕事。
Publisher onGoogle+ 
We Forkwell
Forkwell
キーワード検索
Powered by Lokka