/home/by-natures/dev*

データ界隈で働くエンジニアとしての技術的なメモと、たまに普通の日記。

サーバが Swap を使いきってハングアップしたので、Apache のチューニングを実施しました

3月15日に ReceReco(レシレコ)がテレビ掲載されたようで、分間200アクセスという大量のアクセスがこのブログのエントリー「家計簿アプリ ReceReco(レシレコ)のご紹介」に来てしまい、サーバがハングアップしてしまいました。

仕事中だったので4時間ほどサーバにアクセスができない状態が続いてしまいました。申し訳ありません。。昼休憩時にさくらインターネットのコンソールからサーバ再起動させましたが、ここでは原因と対策をご紹介します。

[toc]

概要

原因は ReceReco テレビ掲載による大量アクセスと、それに伴う Apache プロセス数の上昇により、サーバの Swap を使いきってしまったこと(さくらインターネットコンソールより確認)。

対応としては、Apache の prefork 周りの設定を見直して、大量アクセスが合った場合に Swap を使いきるのではなくアクセスを制限するようにしました。

詳細

原因1:テレビ掲載による大量アクセス

大変ありがたいことに、カタカナで「レシレコ」と検索すると、このブログのエントリーが Google 検索上位(以前は1位!)に出現します。しかしテレビ掲載されたことで「レシレコ」キーワード流入が突発的に増え、今回のサーバハングアップを引き起こしました。

といっても、ものの数分でハングアップしてしまったようでログがほとんど残っておらず…

[caption id="attachment_2581" align="aligncenter" width="377"]キーワード「レシレコ」で Google 検索をすると、第3位に表れます(2013/03/16 現在)。 キーワード「レシレコ」で Google 検索をすると、第3位に表れます(2013/03/16 現在)。[/caption]

アクセスログが残らない
アクセスログはリクエスト処理が終わってレスポンス応答を返す際に吐かれるので、リクエスト処理が間に合わなかった今回は該当時間にログが吐かれていませんでした。

Cacti も残らず
Cacti は Poller Interval をデフォルトの5分にしていたため、こちらもリソース状況を確認できませんでした。

sysstat もダメ
sysstat は10分に1回 cron で回しているので、該当時間のリソース状況を確認できませんでした。

大量の favicon エラーログを発見!
他にもエラー出力は無いかと /var/log 配下を漁ってみましたが、該当時間にすっぽりログが抜け落ちている以外はまともな出力は見当たらず。。Apache のエラーログも favicon.ico の溜まり場になってるしなぁ…と思いながら Apache のエラーログを眺めていたところ、この favicon エラーの集計でアクセス状況が分かるのでは?と思い立ちました。

favicon とはブラウザのタブの左側に出てくる、サイトごとのアイコンです。ブラウザは favicon を取得するためにサーバにアクセスしますが、明示的にアイコンを指定していないところだと、404 Not Found が返ってしまいます。

[Sat Mar 16 17:33:03 2013] [error] [client xx.xx.xx.xx] File does not exist: /var/www/html/favicon.ico

サーバにアイコンを置いておくかApache の設定でこのログを吐かないようにしなければ、これがリクエスト毎に吐かれます。本来の用途とは違いますが、アクセス数を測るために時間ごとに集計してみました。

[bynatures]# grep favicon /var/log/httpd/error_log | awk '{print $4}' | sed -e 's/([0-9]:[0-9]):[0-9]*/\1/g' | sort | uniq -c
      2 10:48
      5 10:49
      1 10:54
      2 10:57
      1 10:58
      3 10:59
      1 11:01
      8 11:03
    118 11:04
    224 11:05

放送時間もこの数分前でドンピシャ!このデータだけでも、サーバに集中的にアクセスがあったことが裏付けできました。

原因2:Apache が Swap を食いつくす

一時的に大量にアクセスがあったと同時に、Apache がプロセスを大量生成して Swap を食いつくしたことがハングアップの原因でした。

この「プロセスを大量生成して」という部分は(今から調べることは不可能なので)憶測なのですが、さくらインターネットのコンソールにログインした際、Swap を食いつくしてハングアップした旨のエラーが垂れ流しにされていたので、大方間違いないと思われます。

解決策:prefork の設定を見直す

そもそも Swap を食いつぶすほどプロセス生成できる Apache の設定が問題なので、デフォルトの値から見直しを行いました。

値の見直しはこちらの方のブログを参考に以下の値に設定しました:ビジネスアプリケーション部会Blog:「Apacheをデフォルト設定のままで使っていませんか?」

Apache で利用可能なメモリ容量を調べ、実際に利用しているメモリ容量を計算して、サーバが最大利用可能なプロセス数を弾き出すという、非常に分かりやすい解説になっていますので是非参考にしてください。私のサーバでは以下のように計算されました:

Apacheの最大子プロセス数 : P
= Apacheに割り当て可能なメモリ量 / Apacheの子プロセスあたりの平均メモリ使用量
= 850MB / 60MB
= 17

たった17プロセス\(^o^)/ という悲しみをグッと堪えて…、これを prefork の設定に反映させます。

この 60MB は多めに見積もって出していることと、Swap 領域が 2GB あることから、最大同時接続数を 30 として、下記設定変更を行いました。

MaxRequestsPerChild を抑えてあるのは、WordPress 中心の運用なので、PHPメモリリークを防ぐためです。


StartServers    8
MinSpareServers 5
MaxSpareServers 20
ServerLimit     256
MaxClients      256
MaxRequestsPerChild 4000


StartServers    5
MinSpareServers 5
MaxSpareServers 10
ServerLimit     30
MaxClients      30
MaxRequestsPerChild  50

他にも、Apache で不要な Module をロード対象から外したり必要のないアプリケーションを chkconfig から外したりしましたが、気休め程度なので割愛します(^_^;

負荷テスト
Web サイトに対する負荷テストは、Load Impact という ASP サービスを利用しました。月10回までなら無料らしく、個人ブログで負荷計測したい程度であれば、JMeter を用意せずとも Load Impact で簡単に負荷計測できます。

下記グラフが示した通り、MaxClients の 30 に達するまで平均応答時間が変わらないことから、正常にサービス提供できていることが分かります。サーバ上で vmstat や netstat などのコマンドを叩いていましたが、httpd 子プロセスが 30 になる辺りでメモリもちょうど使いきる程度でした。

平均応答時間が30秒と遅いのは、海外の AWS 上からリクエストを送っているためのようです。ログインすれば日本の AWS からもアクセスさせることも可能なようです。

[caption id="attachment_2580" align="aligncenter" width="400"]ASPサービス Load Impact による負荷計測 ASPサービス Load Impact による負荷計測を行いました。緑色の "Clients active" の山が折れている所は、サーバ上では Max Clients の30に達した地点です。[/caption]

[caption id="attachment_2587" align="aligncenter" width="400"]ASPサービス Load Impact の概要 ASPサービス Load Impact の負荷テスト概要です。ブラジル辺りからアクセスしていることが分かります。[/caption]

課題:prefork を見直しても今回のアクセスは捌けない

以上のように prefork の設定を見直し、設定範囲内であればサービスが正常に提供できることを確認しました。

しかし、今回のように短時間に大量のアクセスが合った場合、今回の設定見直しで防げるのは Swap 枯渇によるサーバのハングアップです。Max Clients 以上にアクセスが合った場合、サイトを訪れてくれた方すべてに Web ページを正常に提供することは出来ません。そもそも 1 プロセス立ち上げるのに数十 MB のメモリリソースが必要なのに対し、現在のさくらインターネットVPS では 1GB しか貰っていないので、数百プロセスを立ち上げるのは現在のリソースでは現実的ではありません。

Apache の動作モデルを prefork から worker にしてみるとか、Apache をやめて軽量 Web サーバの Nginx にしてみるなど解決策は考えられますが、今回のように短時間の集中アクセスに根本的に備えるには、AWS で Web サーバの数自体を動的に変えられるような仕組みが必要だと思います。

今のところ、そのような大量アクセスが起こりそうなのは ReceReco のエントリーぐらいなので、どこまで本腰を入れて対応するかも悩みどころですが… 「サーバリソースはピーク時に合わせなければいけない」という訓戒を味わえただけでもよかったです。