ひげろぐ

技術者として仕事人としての思うところや覚え書きやらです
Home      Profile      Works     
2009-08-05

Passengerがメモリを食いすぎるとき

Passengerを動かしているサーバのメモリ使用量が突然跳ね上がってスワップをガリガリ発生させることしばしばだったので最近いろいろ調整していた。
結論としては二つ原因があった。

Railsインスタンスプロセスの立ち上がりすぎ

PassengerMaxPoolSizeを適切に設定してないとそうなることがある。

PassengerMaxPoolSizeのデフォルトは6なのでRailsインスタンスが一個につき400MBのメモリを食っていたら最大で2.4GBのメモリを食うことになる。
というわけでメモリが2GBのサーバでも撃沈する。(まあ400MB消費すること自体がおかしいけど)

インスタンスひとつあたりのメモリ使用量を把握するにはしばらく動かしてみるしかないと思うので(何か方法あるかな?)最初は小さめに設定しておくのが無難かもしれない。
この値が1とか2くらいでも小さなサービスでは全く問題ないし。
今回のケースでもこれを4以下にしておけばスワップ発生はなかっただろう。

とはいえ2GBのメモリを搭載しているサーバでPassengerMaxPoolSizeが6で破綻するというのは残念すぎる。
今回の場合はこれは主原因ではなく副原因と言うべきだろう。

一気に大量のレコードを取得しすぎ

そこでもうひとつの問題。

たまにPassengerのRailsインスタンスプロセスのサイズが300MBとか400MBくらいに肥大化しているときがあるので何でかと思って調べてみた。
何となくカンでアタリをつけて動作を確認していると、案の定サイトマップのために数千レコード取得してるのが原因だった。

ビューテンプレートで以下のようにして一度に取得するレコード数を制限したら90MB台をキープするようになった。

Item.paginated_each(:per_page => 100) do |item|
  xml.url do
    xml.loc(url_for(:controller => 'item', :action => 'show', :id => item.id, :only_path => false))
  end
end

善哉。
本当はなるべくビュー側でモデルを直接取得しない方がいいのだろうけど、背に腹は代えられない。
なお、こういう場合はカンじゃなくてログとか調べて時間のかかってる処理からアタリをつけるのがほんとはいいと思う。

ちなみにPassengerじゃなくてthinとかでも同じ問題は起きる。
アプリの実装次第でメモリ使用量が大分変わるという例でもあるということで。

2008-06-01

PHPとPassengerのこととか

KoshigoeBLOG: Passengerのドキュメント読んだ

コメント欄にレスを書こうとしたけどTypeKeyに行く手を阻まれたのでここで。

比較的新しいソフトウェアなので、実績面で不安がありますが、(中略)
対応プラットホームも割と多く検証している気がしますが、そうでもないんでしょうかね。

最近のコミットログを見るに64bit LinuxとMac OS Xあたりの対応でいろいろとやっている模様。
実際に自分の手元でも64bitのCentOS 5.1で動かなかったりしたので、実績や検証はまだまだ足りないレベルかと。

Passengerを使う場合、mod_phpとか有効にすると邪魔になったりするのだろうか。Railsアプリが動くプロセスにmod_php分が上乗せされるのが気持ち悪いけど、気持ち悪いだけで、システムへの悪影響は全くないんだろうか。

通常のhttpdプロセスとは別にRailsアプリ専用のプロセスが立ち上がるのでmod_phpとPassengerが干渉しあうことはないと言ってよさそう。
そもそもPassengerはそうならないように注意深く設計されているとのこと。
ここらへんはPassengerアーキテクチャ概要に書いてありますな。

リクエストが来たらmod_passengerがRailsアプリに関係のあるリクエストがどうかを判断する。
関係のないリクエストはhttpdによってそのまま処理される。この時httpdにmod_phpが乗っていればPHPが動く。
関係のあるリクエストはRailsアプリケーションのインスタンスによって処理される。

といったかんじみたい。

ところでふと思いついて試しにRailsのViewの中にPHPスクリプトを書いて動かしてみたが、PHPは動かなかった。
一方でRAILS_ROOT/public以下のRailsアプリケーションが関与しないファイルは通常のhttpdプロセスによって処理されるので、ここにPHPスクリプトを置いておくと普通にPHPとして動作する。
ページキャッシュを使うとViewの中のPHPスクリプトもキャッシュ後に限って動くなぁ、とか思ったけどこのネタはあんまり役に立たなそうだ。

ところで

Passengerと呼ぶかmod_railsと呼ぶかどっちがいいのか。

前者が正式名称っぽいが、後者の方がわかりやすいよなー。
Rails以外のアプリも動くようにするかどうか検討中とかあるからPassengerと呼んでおいた方がいいのかな。

とりあえずPassengerで統一するか。

てか

ドキュメントの訳とかスゲー。→ KoshigoeBLOG: Passengerのドキュメント読んだ

2008-05-28

mod_rails安定稼働中

先週くらいからアウトドア用品価格比較をmod_railsで動かしているけど、問題なく動作している。
mongrelをクラスターで動かすよりいいかんじ。

メモリ消費量を心配してたけどそれほど大変でもなかった。


  USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
  root     16483  0.0  0.6 123616  6572 ?        Ssl  14:34   0:00 /usr/sbin/httpd
  root     16485  0.0  0.8  14320  8952 ?        S    14:34   0:00 Passenger spawn server
  apache   16490  0.0  1.6  62644 17140 ?        S    14:34   0:26 /usr/sbin/httpd
  apache   16493  0.1  1.6  62600 17188 ?        S    14:34   0:30 /usr/sbin/httpd
  apache   16950  0.0  1.6 103580 17220 ?        S    14:38   0:23 /usr/sbin/httpd
  apache   16491  0.1  1.6  62668 17288 ?        S    14:34   0:30 /usr/sbin/httpd
  apache   16488  0.0  1.6  52420 17300 ?        S    14:34   0:23 /usr/sbin/httpd
  apache   16492  0.1  1.6  62612 17304 ?        S    14:34   0:30 /usr/sbin/httpd
  apache   16954  0.1  1.6 113880 17308 ?        S    14:38   0:32 /usr/sbin/httpd
  apache   17609  0.1  1.6 124116 17432 ?        S    14:45   0:29 /usr/sbin/httpd
  apache   14929  1.0  2.0  27408 21340 ?        S    22:27   0:00 Passenger ApplicationSpawner: /var/www/outdooritem
  root     16602  0.0  2.0  27148 21520 ?        S    14:34   0:01 Passenger FrameworkSpawner: 2.0.2
  apache   13317  2.0  3.6  43168 37308 ?        S    22:02   0:27 Rails: /var/www/outdooritem

稼働してしばらく置いておくと「/usr/sbi/httpd」たちのVSZがものによっては100MB以上とか肥大化しているのでちょっとびびるけど、RSSは安定して20MB以下だしhttpdのプロセスも定期的に生まれ変わるようになっているので問題ないかなと。
Railsと直接関係しない(public以下の静的ファイルの面倒は見てくれるが)httpdプロセスの状況は、VSZが増えて行くところ以外はmod_rails導入前と大して変わらない。
前のエントリではVSZが増えたのをメモリ消費量増えたと表現してた。けどちゃんと見たら実メモリ使用量は大して変わってなかった。もちろんパフォーマンス的にも問題はない。

「Rails: /var/www/outdooritem」は普段はひとつだけど、連続したアクセスが増えてくると現状で数個くらいにまで増える。(デフォルトの状態で最大20個までのようだ)
このプロセスはひとつにつきRSSが35MB~40MB程度なので、プロセスひとつ当たりのメモリ消費はmongrelと比べて少ないくらい。
「Passenger*」たちを加えると1プロセス動作時の実メモリ使用量は若干増えるけど、プロセスが増えるほどmod_railsの方が有利になる。

またmongrelは時間のかかるリクエストを処理している状態で次のリクエストが来ると、前のリクエストが終わるまで次のリクエストを待たせるけど、mod_railsは適宜プロセスを増やして柔軟に対応してくれる。
このあたりはmongrelでクラスター組むよりすぐれている。

Apacheのmod_proxy_balancerと複数のmongrelで組んだクラスターでは、クラスター内のbusyなプロセスにもリクエストを割り振るので時々クライアントを待たせる状態が発生していた。
なのでmongrelを5個くらい立ち上げてごまかしてたんだけど、その状況に比べるとmod_railsを使っている現在の方が断然健全な状態になった。

参考までに実メモリ消費量でざっくり比較すると以下のようなかんじになっていて、メモリ1GBで他にもいろいろとやらせていることのあるマシンではけっこう助かっている。

  • mongrel 50MB/process * 5 = 250MB
  • Passenger*の合計 50MB + Rails:* 40MB/process * 3 = 170MB

プロセス数が1個の状態で比較するとmongrelの方が40MBくらいお得だけど、mongrelを複数立ち上げてクラスターを組むようならmod_railsの方が楽だし負荷にも柔軟だなぁ。
クラスター内のmongrelに処理を割り振るためにリバースプロキシとか使う面倒もないし。

ちなみに現時点で1日当たりのアクセスは6000~9000リクエスト程度。
Googlebotがスゲーお得意様です。

近況

MarkeZine:◎少年ジャンプの歴代人気キャラが駅貼りポスターで競演、缶コーヒー「ルーツ」キャンペーン

今日たまたまJR町田駅で見かけたが、悟空の言ってることがあまりにローカルネタすぎて吹いたw
あなどれねぇぜ町田。

2008-05-18

Passengerことmod_railsを使ってみた

httpd.confでDocumentRootのディレクトリ名を間違えてパーミッションではまった以外は簡単に導入できた。
Apacheモジュールのインストーラ周りはかなりしっかりしていて、足りないライブラリがあると明示してくれる上、それをインストールするためのyumのコマンドまで教えてくれたりする。
親切だ。
インストールでつまずく人はほとんどいないだろうな。

しかし自作のアプリを動かしてみると以下の問題が出た。

いずれもちょっと特殊ケースになるのかもしれないが、商用レベルで動かしているアプリケーションを移行する場合はしっかりテストはした方がよいと思われる。
こういう時のテストはSeleniumがよさげだなぁ。

あとメモリの消費量はけっこうでかい。
PHP5込みのApache2で1プロセス当たり30MB程度だったのが50MBにふくれあがった。
mongrelは1プロセス当たり5MBから6MB程度なのでだいぶ違う。 50MBから60MB。桁間違ってた。
これはメモリの貧弱なマシンにはけっこうきついなー。と、間違い分で認識を改めつつもやはりApacheのプロセスがいくつも立ち上がってしまうときついかなぁ。抑制するオプションはあるっぽいけど。

とは言え、Apacheで簡単にRails運用できる意義はやっぱり大きい。
fast-cgiの設定もいらなくなるし、public以下の.htaccessとかいじる必要もなくなるし(.htaccessは明示的に読ませない限り読まれない。読まなくても勝手にmod_railsがよきに計らってくれる)
バーチャルホストを定義するだけでいいというのは本当にお手軽。

GithubのPassengerのプロジェクトを見ると毎日怒濤のコミットがなされているのでbetaが取れる日も遠くはなさそうだ。

参考

Overview — Phusion Passenger (a.k.a. mod_rails)

追記

メモリ関連はそんなに心配するほどでもなかった。

mod_rails安定稼働中 – ひげろぐ

copyright brass.to | powered by WordPress ME