実録・間違いだらけの負荷分散(『斉藤さん』編)

  • 特に記録を残さず、記憶に頼って書いているので一部曖昧だったり誤っている部分もありますがご了承下さい。。。
  • 基本的に細かいこと考えずに試行錯誤しているので、あんまり参考にしないほうがいいと思います。
    • というかインストール方法とかコマンドラインが一切出てこないので参考にならないと思います。
  • see also

そもそも『斉藤さん』ってなに?

ざっくり言ってしまうと『POMPA』というVoIPアプリで使った音声・動画チャットの技術を流用して、お手軽にもう一個アプリ作ろう!というところから始まった企画(確か)。学生層に人気・・・らしい。

サーバー側で主に担当しているのは「ユーザーのプロフィール管理」と「ショートメッセージ機能」と「Twitter認証(OAuth関係の処理)」と「マッチングプログラム」*1の部分。

マッチングプログラム以外の部分はApache+MySQL+PHP(とその他キャッシュなど)によって動いていた。アプリ側とはJSONで会話している。いわゆる普通のWebAPIですね。

現在、iPhone版Android版で絶賛展開中。

予想外のアクセス増加

サービスイン当初は「まあこんなもんか」という感じでピーク時でも秒間20アクセス超えればいい方だったので、ニフティクラウドの一番下のプラン*2一台で余裕余裕、と思っていたところ、11月入った頃から急激にアプリダウンロード数が増加。12月入った頃には、とうとうピーク時に秒間100アクセス行くくらいの増加ペースを示し、こりゃやばいということで、インスタンスのグレードをひとまず1段階くらいアップさせる*3。年末年始は一旦アクセスが落ち着くものの、1月2週目くらいからまた元のペースに戻る。

CPUが1コアのインスタンスなので、Load averageが2とか3とかになっていれば*4待ちが発生して反応が悪くなるはずだが、「まあ、なんとか捌ききれているのでいいかな・・・」ぐらいに考えていた2月上旬、ショートメッセージ機能をリリースしたことによりアクセスがおよそ3倍に増加し、ついにピーク時のアクセスが秒間300アクセスを突破。日を跨ぐ時間帯にサーバーが全く反応しなくなる事態となる。

またまたこりゃやばい、ということで急遽インスタンスの数を増やし、それを『斉藤さん』のWebアクセス専用に割り当て、グレードももう一段階上げた*5

ここからが深い闇の始まりだった。

ここまでのサーバー構成まとめ

  1. サービスイン当初
    • CPU1GHz*1,512MB(『POMPA』と『斎藤さん』というサービスが同居。Apache,MySQL,PHPが全部一台で動いている)
  2. グレードアップ
    • CPU3GHz*1,2GB
  3. サーバーを増やす
    • CPU3GHz*2,2GB(もともとあったインスタンス、DB周りと『POMPA』のWebアクセスとマッチングプログラム担当)
    • CPU3GHz*2,2GB(新しく作ったインスタンス、『斎藤さん』のWebアクセス専用)

リバースプロキシ&ロードバランサーとやらに手を出してみるの巻

新しく作ったインスタンスも、特に考えることなく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が急上昇し、サーバーに入ってもレスポンスがほとんど入ってこないという状態になってしまう。

冷静になって考えてみたところ、新サーバーのApachehttpd)でロードバランスしてなおかつ、そのアクセスのほとんどを同じサーバーの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?なに?ケーキ?」とか思いこみはじめる。

Nginx編その3

そもそもNginxをリバースプロキシとして動かしたから変な感じになっちゃったんじゃね?と思い、PHPをビルドし直し、php-fpmをインストール。つまり、Apacheそのものを置き換える方策に打って出る。その結果・・・

「すげー!メモリー消費減りすぎなんですけど!アクセスも問題なく捌けてるし、Nginxさんかっけー!!!」←いまここ

まとめというか教訓

  • Apacheさんもなんだかんだで結構使える
    • ただしWebサーバーとして。
  • Nginxさんを導入する勇気があるならリバースプロキシとしてじゃなくてWebサーバーとして使ったほうが良い
    • 結局バックエンドの負荷に引きずられて望むような性能を発揮できない予感。
  • L7ロードバランサーの負荷は予想以上にデカイ
    • パケット解析する処理とか、バックエンドからのレスポンスとかで引きずられる。
    • もし高負荷サイトでロードバランサー建てるなら素直にL4使うかサーバー独立させたほうがいいんじゃないかという「当たり前だろ」とか言われそうな話。
  • 結局負荷分散はどうしたんだよ
    • 負荷「対策」はしました。ハイ。

*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を使って鍵垢にサーバーアラートを飛ばすようにしている