実録・間違いだらけの負荷分散(『斉藤さん』編)
- 特に記録を残さず、記憶に頼って書いているので一部曖昧だったり誤っている部分もありますがご了承下さい。。。
- 基本的に細かいこと考えずに試行錯誤しているので、あんまり参考にしないほうがいいと思います。
- というかインストール方法とかコマンドラインが一切出てこないので参考にならないと思います。
- see also
そもそも『斉藤さん』ってなに?
ざっくり言ってしまうと『POMPA』というVoIPアプリで使った音声・動画チャットの技術を流用して、お手軽にもう一個アプリ作ろう!というところから始まった企画(確か)。学生層に人気・・・らしい。
サーバー側で主に担当しているのは「ユーザーのプロフィール管理」と「ショートメッセージ機能」と「Twitter認証(OAuth関係の処理)」と「マッチングプログラム」*1の部分。
マッチングプログラム以外の部分はApache+MySQL+PHP(とその他キャッシュなど)によって動いていた。アプリ側とはJSONで会話している。いわゆる普通のWebAPIですね。
予想外のアクセス増加
サービスイン当初は「まあこんなもんか」という感じでピーク時でも秒間20アクセス超えればいい方だったので、ニフティクラウドの一番下のプラン*2一台で余裕余裕、と思っていたところ、11月入った頃から急激にアプリダウンロード数が増加。12月入った頃には、とうとうピーク時に秒間100アクセス行くくらいの増加ペースを示し、こりゃやばいということで、インスタンスのグレードをひとまず1段階くらいアップさせる*3。年末年始は一旦アクセスが落ち着くものの、1月2週目くらいからまた元のペースに戻る。
CPUが1コアのインスタンスなので、Load averageが2とか3とかになっていれば*4待ちが発生して反応が悪くなるはずだが、「まあ、なんとか捌ききれているのでいいかな・・・」ぐらいに考えていた2月上旬、ショートメッセージ機能をリリースしたことによりアクセスがおよそ3倍に増加し、ついにピーク時のアクセスが秒間300アクセスを突破。日を跨ぐ時間帯にサーバーが全く反応しなくなる事態となる。
またまたこりゃやばい、ということで急遽インスタンスの数を増やし、それを『斉藤さん』のWebアクセス専用に割り当て、グレードももう一段階上げた*5。
ここからが深い闇の始まりだった。
ここまでのサーバー構成まとめ
リバースプロキシ&ロードバランサーとやらに手を出してみるの巻
新しく作ったインスタンスも、特に考えることなくApache+PHPの構成にしてみたものの、今後もこの一台で捌ききれるとは限らず、既に課金が絡んだサービスも始まっている。ということは、今までみたいに「よし、一旦落としてサーバーのグレードあげよう!」みたいな方策がいつまでもできるわけではないので、今のうちに負荷分散の仕組みも入れちゃうか!みたいな軽いノリで手を出したのです。
Nginx編その1
ちょっと前に個人的に借りているVPSで試していたので余裕余裕、とリバースプロキシとして動かす設定をし、ついでにもともとあったサーバーにもアクセスを流すようにしてみたところ、iPhone版では上手く行ったのだが、Android版では謎の「HTTP 411」というエラーが出てしまう。
軽くググってみると、どうやらNginxは通常では「Transfer-Encoding: chunked」とやらに対応してないらしく、モジュールを組み込んでビルドした後、設定をいれないとダメらしい、ということが分かった。
ということで、もう一度ビルドしなおしてインストールし直すと、Androidからのアクセスもちゃんと返すようになった。
のだが・・・レスポンスが異常に遅い!Apacheより速いと言われているはず*6なのに・・・アクセスが集中しても大丈夫!と言われてるはずなのに・・・。同時接続数の設定もデカくした、ファイルディスクリプタの数も上げた、どうしてこうなった。
もうこの時点で、就業時間中の半分位を消費してしまったため、諦めてApacheの「mod_proxy_balancer」を使う方向で設定して帰宅する。
ロードバランス周りの設定は「旧サーバー:新サーバー=1:3」とかそんな感じだったと思う。
mod_proxy_balancer編
帰宅後、しばらく様子を見ていたが23:30ころ*7、突如としてLoad averageが急上昇し、サーバーに入ってもレスポンスがほとんど入ってこないという状態になってしまう。
冷静になって考えてみたところ、新サーバーのApache(httpd)でロードバランスしてなおかつ、そのアクセスのほとんどを同じサーバーのhttpdプロセスにパスするわけなので、httpdプロセスが想定以上に増えてしまい結局リバースプロキシ&ロードバランサーの分だけプロセスが増えて負荷が増大してる・・・という考えに至った。まさに本末転倒。
設定を変えて色々試してみたりしたが、結局リバースプロキシ&ロードバランス部分を外し、アクセスをそのまま新サーバーのApacheで処理するようにしたところ、とりあえずは落ち着く。
Nginx編その2
ロードバランスを諦められないので、もう一度Nginxの設定を見直し、別ポートでプロセスを立ち上げ、ApacheBenchを掛けてみる。するとどうだろう、前とは違ってレスポンスがスムーズになり、秒間500〜1000くらいのリクエストを捌けるようになった(かのように見えた)。
「うっひょー!これで勝つる!」と思い、ApacheとNginxのポート設定を入れ替え、そのまま帰宅。
しかし、ピーク時間帯を迎えた頃、突如TLにLoad averageが49とかそういうありえない数値になっているアラートが飛んでくる*8。
慌ててリモートログインするものの、思いっきりI/O待ちが発生していてとてもじゃないがサーバー周りの操作ができない。
というわけで、サーバーを強制的に再起動し、元の状態(=Apacheだけで捌く)に戻す。
翌日ApacheBenchで調べてみたところ、例えば100アクセス同時に来るのが10秒程度だったらどうってことないが、30秒とか60秒にしてみるとだんだんアクセスごとの処理速度が遅くなっていき、プロセスが溜まって、サーバー全体が応答不能な状態になってしまう、というような感じであった。リバースプロキシとバックエンドの間でなんか起きてるっぽいけど、これ以上深追いする気力が・・・。
Pound編
一昔前は「リバースプロキシとロードバランスさせるならこれ!」的な定番ソフトだったらしいので、とりあえず設定して置いてみる。ApacheBenchで入念にベンチを取ってみるが、特に問題はなさそう。これで行ってみるか、と思いポートの設定を切り替えてAndroid版で動作確認してみると・・・なぜか接続エラー。
アクセスログを見てみると、Nginxのときとは違って200で返してはいるが、全てのレスポンスサイズが5byteになっていることに気づく。リクエストパラメーターが空の時に単に「error」と返しているはずなので、POSTデータが全部破棄されてるのか???
どうググっても解決策が見つからなかったのでApacheだけの状態に戻す。
この辺から「やっぱApacheさん最強!Nginx?誰それ?Pound?なに?ケーキ?」とか思いこみはじめる。
まとめというか教訓
*1:ちなみに、マッチングプログラムに関してはiPhone版の『POMPA』『斉藤さん』を開発したエンジニアがC++で書いたものをサーバーでコンパイルし、デーモンとして動かしている
*2:CPUが1GHz相当1コアでメモリ512MBというプラン
*3:CPUが3GHz相当1コアでメモリ2GBのプランだったと思う
*4:23時〜1時台にはLAが4以上になることも
*5:といってもCPUコアを2つにしただけだったりしますが
*6:まあでも「Webサーバーとして」と「リバースプロキシとして」の比較するのはおかしいですよね
*7:『斉藤さん』ではこの時間帯が一番混むようです
*8:sendmailの設定が面倒なので、お手軽にTwitterAPIを使って鍵垢にサーバーアラートを飛ばすようにしている