Rails

Railsのアプリの実行環境について調べてみました

そういえば前回のRails勉強会でRailsアプリを公開する際のノウハウを聞けると思って参加してみたものの詳しい人がおらず分からずじまい。ネットでいろいろと調べたので晒しておきます。間違いを見つけたら教えてください。

予備知識は以下の通り。

  • アプリケーションディレクトリでscript/serverで3000番ポートでWEBrickという定番サーバーが起動する
  • Railsではデフォルトで3つの環境(development, test, production)が作られるので、公開する場合はRAILS_ENV=productionをセットして起動
  • 中でもLIGHTtpdやMongrelというサーバーがポピュラー
  • でもでもMongrelの作者がキレて最近開発をやめそう

調べているうちに最初に気づいたことは「1つのRailsインスタンス(1つのRubyプロセス)が同時に処理できるHTTPリクエストは1つだけ」ということである。びっくりした。念のため2つのブラウザーを立ち上げて、長い間sleepするコントローラーにリクエストを投げると、もうひとつのブラウザーでは簡単なリクエストにもかかわらず1つめのブラウザーのsleepが終わるまで待たされたので間違いないようだ。The Futures of Ruby Threadingのささださんコメントにもあるが、RubyのVMではGlobal Interpreter Lock (GIL)を取得するので同時に動けるスレッドは1つだけだ。この制限は現行のRuby1.8からRuby1.9(YARV)になってユーザーレベルスレッド(グリーンスレッド)からカーネルスレッドになっても同じである。

As you know, YARV support native thread. It means that you can run each Ruby thread on each native thread concurrently.

It doesn’t mean that every Ruby thread runs in parallel. YARV has global VM lock (global interpreter lock) which only one running Ruby thread has. This decision maybe makes us happy because we can run most of the extensions written in C without any modifications.

では複数のリクエストを同時に処理したい場合はどうするのだろうか?これには複数のRubyプロセスをあらかじめ起動しておき複数のプロセスで処理することで対応する。1プロセス=1スレッドというわけだ。このあたりは1プロセス=複数スレッドというJavaの世界とは異なる。最初はいまどきプロセス単位で並列かよとも思ったが、よくよく考えてみたらWebアプリは並列して動かしてもお互いの処理は独立していてお互いに協調する必要はほとんどないのでプロセス単位の並列化でもいいのかも知れない。Rails2.0からセッションもCookieStoreになったし。で、複数プロセスを起動するための方法としては大きく分けて2つあり、仮にFastCGI方式とMongrel方式と呼ぶことにする。

まず比較的トラディショナルな方式がFastCGI方式だ。FastCGIはPerlなど別のLL言語でも使われる方式で、あらかじめ複数のRubyプロセスを常駐しておいてそれらをフロントのWebサーバーから呼び出す。Webサーバーとしては、Apacheだといろいろと問題があるらしくRailsの世界ではLIGHTtpd(通称Lightly)という軽量Webサーバーが使われるのが一般的なようだ。Webサーバーと常駐しているRubyプロセス間の通信はソケット通信で行う。LIGHTtpdは静的コンテンツも高速に処理できるためLIGHTtpd+FastCGIで完結し割とすっきりな構成となる。

もうひとつの方式が最近メジャーになりつつあるMongrel方式だ。これは、MongrelというWebサーバーを使って1ポート=1Mongrelサーバー=1Rubyプロセスで複数のMongrelサーバーを起動する。例えば5並列で処理したい場合、5つのMongrelサーバー(5つのRubyプロセス)を起動してポート番号50,000から50,004で待ち受けて処理する。ただMongrelサーバーの作者はRuby on Railsはゲットーだにあるように開発をやめようとしているので、ポストMongrel時代のWebサーバにあるように最近ではThinなど別のサーバーが注目されている。

Mongrel方式の場合、複数のポートで処理を待つので外部に公開する場合はMongrelだけでは不十分で、1つのポートで待ち受けてリクエストを複数のMongrelに分配するリバースプロキシー+ロードバランサーが必要になってくる。このロードバランサーにはいろいろとあって一番お手軽なのはApacheのモジュールのmod_proxy_balancerだ。他にもこの種のソフトにはPoundSwiftiplynginxなどお腹いっぱいの充実ぶりだ。

結局どの方式にしてもそれなりに複雑であり、フロントとバックエンドの組み合わせとなると無限にある。この状況に対して、No True “mod_ruby” Is Damaging Ruby’s Viability On The Webなどで「だからなかなかRubyのブログや掲示板が流行らないんだ!」と問題提起されている。ちなみにこのページはRubyのWebアプリのトポロジーに複雑さにとどまらずスレッドの話などの議論も行われており興味深い。またこのページの作者による簡単にRubyアプリをデプロイをめざしてSwitchPipeなる試みもある。

さて気になるパフォーマンスはくまくまーの人のブログにクアッドコアでの測定結果がのっているが、これによると意外にLIGHTtpd+FastCGIが一番のようだ。またすでにApacheを動かしている場合はApache2 + mod_proxy_balancer + Mongrelが一番素直だと思った。

またApacheなどからバックエンドに飛ばす場合のURLのパスの書き換えはどうするの?問題についてはこれまたくまくまーの人のブログにある。このエントリーは面白いので最初から最後までじっくりと読むことをおすすめします。

でもこれら以外にまったく違う形式としてwarにしてJRubyアプリとしてJavaのアプリケーションサーバーにデプロイというのもあるそうですが、それはまだ調べ中なのでまたの機会に…。

similar posts

comment

よろしければ、コメントをどうぞ。トラックバックはこちら

このエントリーのコメントの購読

次のHTMLタグが使えます。: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

*Required Fields