2010年11月30日火曜日

さくらVPSで一日6万PVを処理するためにしたこと


さくらVPSで6万PV程度のサイトを運用することになったので、その際の記録を残しておきます。



さくらレンタルサーバ⇒さくらVPS


さくらレンタルサーバで運用している時は、ちょくちょく503が発生しておりこれを解決するためにさくらVPSへの移行を行いました。



レンタルサーバの時は、ログ解析や監視ツールなどを導入していなかったので503の頻度やパターンは不明です。(安易な判断でVPSに移行したので、この点は反省です)



本来は原因を追及し、プログラムの改修やサーバ負荷の分散などをすべきですが時間の都合で省いてしまいました。



構成


運用するサイトはpukiwikiで構成されたサイトです。



pukiwikiは、PHPで書かれており大量のデータをRDBを利用せずに構築できる点が大きなメリットです。



今回は1サイトですが、複数のサイトを運用する可能性を考慮してVirtualHostも利用します。



さくらVPSの性能


  1. OS:CentOS

  2. CPU:XeonQuadを共有利用(2コア)

  3. メモリ:512M



Apacheのインストール


CentOSにはyumがあるのですが、不要なモジュールやパッケージをインストールを避け最新版のApcheを利用したかたったのでソースからインストールしました。


Apacheの最新版


configure

pukiwikiに関わらず基本的なサイトであれば以下のオプションで動作するはずです。



"./configure" \


"--enable-so" \


"--enable-rewrite" \


"--enable-expires" \


"--disable-proxy" \


"--disable-deflate" \


"--disable-cache" \


"--disable-disk-cache" \


"--disable-auth-basic" \


"--disable-authn-file" \


"--disable-authn-default" \


"--disable-authz-groupfile" \


"--disable-authz-user" \


"--disable-filter" \


"--disable-autoindex" \


"--disable-negotiation" \


"--disable-actions" \


"--disable-asis" \


"--disable-cgi" \


"--disable-env" \


"--disable-include" \


"--disable-userdir" \


"--disable-imap" \


"--disable-cgid" \


"--disable-charset-lite" \


"--disable-imagemap" \


"--disable-setenvif" \



httpd.conf

次の設定ファイルのみ有効にします。



#プロセスやスレッドをコントロールするための設定


Include conf/extra/httpd-mpm.conf


#バーチャルホストの設定


Include conf/extra/httpd-vhosts.conf


#Apacheの基本的な設定セキュリティ設定


Include conf/extra/httpd-default.conf




httpd-mpm.conf

mpm_prefork_moduleのみに注目してください。


mpm_worker_moduleはマルチスレッド対応のmpmですが不安定な一面があるのでpreforkに移行しました。




StartServers 10 # Apache起動時に起動するプロセス数


MinSpareServers 50 #最低待機プロセス数


MaxSpareServers 70 #最大待機プロセス数


MaxClients 110 #最大起動するプロセス数(これ以上はプロセスを起動しない)


MaxRequestsPerChild 1000


MaxMemFree 2048



MaxRequestsPerChildとMaxMemFreeについては後ほど説明したいと思います。




Apacheは起動するとデフォルトで5個ぐらいのプロセスを起動するのですが6万PVを処理するには平均で70プロセス程度、ピーク時で100プロセス程度起動します。



1プロセス1リクエストを処理した後にプロセスが終了するので、5⇔70プロセスの起動と終了を何度も繰り返すので非常にオーバーヘッドが大きくなってしまいます。



Apacheプロセス起動と終了に生じるオーバーヘッドを下げるためにmpmで予めプロセスを起動しておきます。


それがStartServers、MinSpareServers、MaxSpareServersの設定になります。




<IfModule mpm_prefork_module>


StartServers 10


MinSpareServers 50


MaxSpareServers 70


MaxClients 110


MaxRequestsPerChild 1000


MaxMemFree 2048


</IfModule>




httpd-default.conf

KeepAliveは諸刃の刃なので、ONにするときはよく検証しましょう。


できるかぎりサーバの情報は伏せておきたいので情報表示はOffにします。



Timeout 300


KeepAlive Off


MaxKeepAliveRequests 100


KeepAliveTimeout 5


UseCanonicalName Off


AccessFileName .htaccess


ServerTokens Prod


ServerSignature Off


HostnameLookups Off


TraceEnable Off




PHPのインストール


PHPもソースからインストールします。


バージョン:5.2.14


PHPダウンロード


PHPも同様に必要最低限の機能のみインストールします。


configure



'./configure' \


'--with-apxs2=/usr/local/apache2/bin/apxs' \


'--enable-zend-multibyte' \


'--with-zlib' \


'--with-xmlrpc' \


'--with-gd' \


'--with-jpeg-dir=/usr/local' \


'--with-png-dir=/usr/local' \


'--enable-mbstring' \




cp php.ini-recommended /usr/local/lib/php.ini



zlibがないとエラーがでますが、zlibなどはyumからインストールしていただいて結構です。


やっておくと便利



ln -s /usr/local/lib/php.ini /etc/


ln -s /usr/local/lib/php/extensions/no-debug-non-zts-20060613/ ./extensions





サーバ停止


上記の設定後、3日にロードアベレージが60程度まで上昇しサーバが停止しました。



サーバ停止の原因を探るため、topを見たところ一部のApacheプロセスがメモリを30%程度占有していることが判明しました。



同時にSWAPもどんどん増え、メモリ不足に陥りサーバが停止している事がわかりました。



PHPのメモリを制限する


php.iniのmemory_limit = 128M と恐ろしいことになっていたのでApacheが平均的に使用しているメモリ+αのサイズを設定しました。




memory_limit = 128M ⇒ memory_limit = 16M




APCを入れてみる


PHPの中間キャッシュを行うAPCを導入しました。



その結果、極端にメモリを消費するPHPファイルを除き高速に処理されるようになりました。



約、2倍ぐらい処理速度が向上しました。



導入後、キャッシュ利用サイズが7M程度なのでAPCに8Mのメモリを割り当てる設定にしました。


php.iniの変更

追記



extension=apc.so



追記後、Apacheを再起動してください。


副作用

PHPが利用できるメモリを制限したところ、予想通りですが“Fatal error: Allowed memory size”のエラーが一部のPHPファイルで発生しているのが確認できました。



問題のファイル名を調べてみると、Wiki内にテーブルや内部リンクが多くある場合メモリリークが発生しやすいようです。



この副作用で、どのプログラムが問題を起こしているのかを発見することができました。



このプログラムに関しては、Pukiwikiに情報がありメモリリークを軽減させるパッチがあったので適用させることで、比較的テーブルや内部リンクが多いページでもメモリリークが発生することはなくなりました。



Apacheのメモリを制限する


PHP側のメモリリークはなくなったものの、Wikiのテーブルや内部リンクの多いページにアクセスされ続けるとApacheがどんどんメモリを利用して解放しない現象が発生しました。



メモリ利用と解放の動き



Apacheがメモリ要求


OS→lib→Apache



Apcheのメモリ使用終了


OS lib←Apache



OSがメモリを要求 Apacheがメモリ解放


OS←lib Apache





このような動作をしているようでOSがメモリを要求しない限りlib(メインメモリーアロケータ)がメモリを解放しないので、OSがメモリを要求してこない限りApacheが使用するメモリはどんどん増えていきます。



物理メモリに余裕があれば殆どの場合問題にはなりにくい問題なのですが512MしかないのでOSのメモリ要求とApacheのメモリ要求で使い果たしてしまうようです。



ApacheにはMaxMemFreeという機能があります。




MaxMemFree ディレクティブは free() が呼ばれない限り、 主アロケータが保持できる空のメモリの最大値をキロバイト単位で設定します。 設定されていないか、零に設定されているときは、無制限になります。




なんとも難解な日本語で書かれているのですが、つまりlib(メインアロケータ)が保持できるメモリを制限し設定値以上のメモリはlib(メインアロケータ)はOSに返すのでApacheがメモリを食い続けるという現象がなくなりロードアベレージがかなり下がりました。



メモリ使用量は多いがCPU使用量は低いプロセスが多くあったので、定期的にプロセスのメモリを解放することにしました。




MaxRequestsPerChild 0 ⇒ MaxRequestsPerChild 5000




MaxRequestsPerChildは、プロセスが最大処理できるリクエスト数を設定するための物です。



設定値以上のリクエストを処理するとプロセスを再起動させるので、効率よく負荷の高いプロセスにメモリを割り振ることが可能です。




結果


平均ロードアベレージ:1.07


平均CPU使用率:22.1%


ブラウザがページを表示するまでの平均時間:0.87


平均未使用メモリ:42M


SWAP:30K


HTTP 503発生回数:0回



このような感じで現在も安定して動作しています。


WikiはRDBを利用しないためメモリ使用量が抑えられましたが、MySQLを利用する場合は13%~17%程度メモリ使用量が増加するのでmpmの設定でプロセス数を減らしメモリをどこかで浮かせる必要があります。


SWAP


SWAPが少し発生していたので、vmstatを見ていたところSWAPインが1日数十回発生するだけでSWAPアウトが発生していないのでメモリに置いていても利用頻度の低いデータはSWAPに移動されるのでそれが原因だと思われます。



監視


レンタルサーバでは、ログ解析や障害検知を行えていなかったのでZabbixとVISITORS Web Log Analyzerを導入しました。


Zabbixサーバは別のサーバに導入し、クライアントのみをインストールしています。




0 件のコメント:

コメントを投稿