はじめに
Star It!のアクセス数が増えてきて、色々と支障がでてきたので、おもいきって友人の会社に置いているサーバへと移してみました。さすがに、さくらインターネットの共用サーバ上で、1日あたり10万クエリをCGI + sqlite3という構成でさばくのは無理があったようです。新天地では、データベースをMySQLへと入れ替えたうえで、PoundとMongrel、それにApache2.0系という構成にしてみましたので、ばっちり動いてくれることを期待しています。
というわけで、今回のエントリーは、PoundとMongrel、それにApache2.0系を利用して、Ruby on Railsウェブアプリケーション環境を構築してみよう、という話です。この構成のメリットは、十分なスケーラビリティを得たうえで、柔軟性も確保できるという点が挙げられます。私のサーバ環境はDebian GNU/Linuxですので、Debian特有の話題も少々でてきますが、基本的な点は、他の環境でも変わらないとおもいます。
友人のところに置いているサーバは、もともとプライベートなSubversionリポジトリをApacheモジュールとして提供するために設置していました。また、いくつかのJava EEアプリケーションサーバをテストする目的でも利用しています。ここにRailsアプリケーションを追加したいのですが、いままで稼働させていたものをあきらめるというのも、しょっぱい話です。Poundを利用すれば、こうしたものを同じポートで同居させることができるようになります。
さらに、Poundはロードバランサとして動作しますので、冗長性を確保するのにも役立ちます。サーバが一台しかありませんので、ハードウェア障害などには対応できませんが、長期運用した経験がないため信頼性に自信が持てないMongrelを冗長化できるというのは大きな強みです。
今回紹介するサーバアプリケーションについて
今回のエントリーでは、PoundとMongrel、それにApacheを取り上げます。ここでは、それぞれの役割について簡単に解説を加えます。
Pound
Poundとは、リバースプロキシとロードバランサとして動作するサーバアプリケーショです。
リバースプロキシとは、通常のプロキシサーバが内部のネットワークからのリクエストを外部のサーバへとを中継するのに対して、それとは逆に、外部のネットワークからのリクエストを内部のサーバへと中継するサーバのことをいいます。PoundはHTTPとHTTPSに対応しており、クライアントからのリクエストをあらかじめ設定しておいたルールに従って決められたサーバへと振り分けます。
Mongrel
Mongrelとは、RubyとCの拡張ライブラリで書かれたウェブサーバです。高速に動作するというのがウリで、Ruby on RailsではMongrelと組み合せて利用するのが一般的になりつつあるようです。
Mongrelはひとつのプロセスに、ひとつRubyインスタンスを持ち、ひとつのポートを占有します。MongrelのプラグインであるMongrelクラスターを利用すると、簡単に複数のMongrelプロセスを立ち上げたりすることができます。今回は、このMongrelクラスターを利用します。
Apache
Apacheとは、代表的なHTTPサーバです。今回は、静的ファイルの配信と、Subversionのフロントエンドとして活躍します。
Mongrelのドキュメントでは、Apache2.2系から利用できるmod_proxy_balancerについて解説があるのですが、今回は十分に運用経験があるApache2.0系を利用したいので、mod_proxy_balancerは利用しません。Apache2.2系をご利用の方は、mod_proxy_balancerに挑戦してみてもよいでしょう。
ネットワーク・システム構成
このエントリーのためのネットワークやシステムの環境について説明しておきます。今回登場する物理的なサーバは1台で、IPv4アドレスをひとつ(192.0.2.2)割り当てています。さらに、そのIPv4アドレスには、3つの名前を割り当てることにします。具体的には、このサーバの本名であるmyserv.example.jp、Subversionが動作しているようにみえるsvn.example.jp、Railsアプリケーションが動作しているようにみえるrails.example.jpです。DNSの設定は下記のようになります。
myserv.example.jp. 3600 IN A 192.0.2.2 svn.example.jp. 3600 IN A 192.0.2.2 rails.example.jp. 3600 IN A 192.0.2.2
設定などの詳細は後述しますが、このサーバ上で、Pound、Mongrel、Apacheを動作させます。myserv.example.jpの80番ポートにPoundを、ローカルホストの8000番ポートにApacheを、ローカルホストの8100番ポートから8102番ポートまでにMongrelを、それぞれ割り当てるようにします。次のnetstatコマンドの実行結果を確認してください。
user@myserv% netstat -tl 稼働中のインターネット接続 (サーバーのみ) Proto 受信-Q 送信-Q 内部アドレス 外部アドレス 状態 tcp 0 0 localhost:8000 *:* LISTEN tcp 0 0 localhost:8100 *:* LISTEN tcp 0 0 localhost:8101 *:* LISTEN tcp 0 0 localhost:8102 *:* LISTEN tcp 0 0 myserv.exmple.jp:www *:* LISTEN
このmyserv.example.jpの80番ポートに届いたリクエストを、バーチャルドメインや、リクエストの種類によって、実際のサーバアプリケーションへと振り分けるというのが、今回のゴールになります。
インストールと設定
Pound
インストール
http://www.apsis.ch/pound/から最新バージョンを取得し、下記の手順でインストールします。
user@myserv% sh configure user@myserv% make user@myserv% sudo make install
さらに、サーバ起動時にきちんとpoundが立ち上がるように、起動スクリプトを作成します。Debianの場合は、/etc/init.d/skeltonというファイルがありますので、これをpoundという名前でコピーしたうえ、利用します。コピーしたファイルを下記のように編集すればよいでしょう。
17 18 PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 19 DESC="pound - Reverse Proxy and Load Balancer" 20 NAME=pound 21 DAEMON=/usr/local/sbin/$NAME 22 PIDFILE=/var/run/$NAME.pid 23 SCRIPTNAME=/etc/init.d/$NAME 24
update-rc.dコマンドで適切なランレベルと起動順を設定するのを忘れないでください。
設定ファイルの構造
poundの設定ファイルは、グローバル定義、リスナー定義、サービス定義、バックエンド定義という部分から構成され、それぞれが入れ子の関係になっています。
グローバル定義は、サーバ全体の動作を設定する部分で、複数のリスナー定義を含みます。この部分には、Poundを動作させるときのユーザや、グループ、それにJail環境で動作させるか、ログをどれだけ詳細に記録するか、などが設定できます。
リスナー定義は、HTTP接続の受け付けを設定する部分で、複数のサービス定義を含みます。この部分には、リッスンするIPアドレス、ポート番号、最大接続数などが設定できます。
サービス定義は、HTTPリクエストがどのように処理されるかを定義する部分です。リクエストURLのパターンやヘッダのパターンを設定しておき、受け付けたリクエストが、このパターンに一致するかどうかによって、どのバックエンドに振り分けをするか決めるわけです。サービス定義はバックエンドを複数含むことができ、もしバックエンドを複数含んでいるならば、その複数のバックエンド間でロードバランスを行ないます。
バックエンド定義は、実際の振り分け先について設定します。アドレスとポート番号を設定します。
意図する動作
具体的な設定にはいる前に、意図する動作を確認しておきましょう。まず、このサーバには名前が3つ割り当ててありました。myserv.example.jp、rails.example.jp、svn.example.jpの3つです。
そのうち、myserv.example.jpとsvn.example.jpへと来たリクエストは、Apacheに担当させます。Apacheは、ローカルホストの8000番ポートに割り当てます。
rails.example.jpへと来たリクエストは、Mongrelに担当させます。Mongrelは3つプロセスを立ち上げ、それぞれローカルホストの8100番ポートから8102番ポートに割り当てます。ただし、rails.example.jpへと来たリクエストであっても静的ファイルへのアクセスは、MongrelよりもApacheの方が得意ですので、Apacheに担当させたいと思います。
ルールを整理すると、下記のようになります。
- rails.example.jpへのリクエスト
- 静的コンテンツならばApacheへ
- 動的コンテンツ(静的コンテンツ以外)ならばMongrelへ
- それ以外のリクエスト
- Apacheへ
実際の設定ファイル
それでは、実際の設定ファイルをみていきましょう。デフォルトの設定ファイルの位置は、/usr/local/etc/pound.cfgになります。ファイルが無い場合は、ファイルを新規作成してください。
1 User "www-data" 2 Group "www-data" 3 4 LogLevel 0 5 Alive 30 6 7 ListenHTTP 8 Address 192.0.2.2 # バインドするアドレス 9 Port 80 # ポート番号 10 xHTTP 2 # WebDAVリクエストを許可する 11 12 # 静的ファイルへのアクセスの場合 13 Service 14 HeadRequire "Host: rails.example.jp" 15 URL "/(images|javascripts|stylesheets)/.*" 16 BackEnd 17 Address 127.0.0.1 18 Port 8000 19 End 20 End 21 22 # Railsへのリクエスト 23 Service 24 HeadRequire "Host: rails.example.jp" 25 BackEnd 26 Address 127.0.0.1 27 Port 8100 28 End 29 BackEnd 30 Address 127.0.0.1 31 Port 8101 32 End 33 BackEnd 34 Address 127.0.0.1 35 Port 8102 36 End 37 End 38 39 # その他のリクエスト 40 Service 41 BackEnd 42 Address 127.0.0.1 43 Port 8000 44 End 45 End 46 End
1行目、2行目では、poundが動作するユーザ権限を設定しています。ここでは、Debianの流儀に合わせてユーザ、グループともwww-dataにします。
4行目の設定は、どの程度のログを記録するかという設定です。ここでは、ログをとらない0に設定してあります。なお、デフォルトのログの出力先はdaemonになっています。
5行目のAlive設定は、フェイルオーバ機能に関する設定で、バックエンドが動作しているかどうかをどの程度の期間ごとにチェックするかを秒数で設定する設定です。ここでは、デフォルトと同じ30秒ごとに生死確認をとるようにしています。
7行目から46行目までのListenHTTPとEndに挟まれた部分が、リスナー設定になります。8行目と9行目とで、192.0.2.2の80番ポートをpoundが利用するように設定をしています。
10行目の設定は、どのHTTPリクエストを受け付けるかを設定しています。poundは、デフォルトの動作として、GET、POST、HEADリクエスト以外を受け付けません。今回は、Subversionを動作させますので、WebDAVで利用されるリクエストを許可しておく必要がありますので、値は2に設定します。値と動作の対応は、下記を確認してください。
| 値 | 動作 | ||
|---|---|---|---|
| 0 | GET、POST、HEADのみを受け付ける(デフォルト) | ||
| 1 | 上記に加えてPUTとDELETEを受け付ける | ||
| 2 | WebDAVで利用されるリクエストを受け付ける | ||
| 3 | MSによって拡張されたWebDAVで利用されるリクエストを受け付ける | ||
| 4 | 上記に加え、MS RPCによって拡張されたリクエストを受け付ける |
13行目から20行目が静的ファイルへのリクエストについてのサービス、23行目から37行目がRailsへのリクエストについてのサービス、40行目から45行目がそれ以外のリクエストについてのサービスを定義している部分になります。Poundが受け付けたリクエストは、サービスの定義された順番に、それぞれのルールに一致するかどうかが確認され、最初に一致したサービスへと振り分けられます。
静的ファイルへのアクセスの場合をみていきましょう。この場合のリクエストは、
http://rails.example.jp/images/
http://rails.example.jp/javascripts/
http://rails.example.jp/stylesheets/
へのリクエストになります。
このリクエストへ一致するルールを定義しましょう。Poundでは、ルールを定義するために、次の3つの項目が用意されています。項目の値は、正規表現のパターンを指定します。正規表現は、Perl互換正規表現ライブラリ(PCRE)が利用できるならばそれを利用し、そうでない場合はPosix互換の正規表現ライブラリを利用するようです。また、それぞれの項目は、複数設定することもできます。
| 項目 | パターンの対象 | 複数設定したとき |
|---|---|---|
| URL | リクエストされたURL (絶対パス) |
いずれかに一致すること (省略時には全てのパターンに一致) |
| HeadRequire | リクエストヘッダ行 | 全てに一致すること |
| HeadDeny | リクエストヘッダ行 | 全てに一致しないこと |
14行目と15行目の意味は、リクエストヘッダ行に
Host: rails.example.jp
が含まれ、リクエストURLが
/images/.*
/javascripts/.*
/stylesheets/.*
のいずれかの場合にこのサービスを利用するという意味です。このリクエストは、16行目から18行目で定義されたバックエンドであるローカルホストの8000番ポート、つまりApacheに転送されます。
さて、次はRailsへのリクエストの部分です。
先述のように、どのサービスに振り分けるかというルールは先頭から評価され、最初に一致したものが適用されます。ですから、24行目でバーチャルホストがrails.example.jpの場合に一致するとだけ指定していますが、これは最初のサービスに一致しなかった場合にだけ評価されます。つまり、rails.example.jpへのリクエストで静的ファイルへのリクエストでないものということになります。
このサービスは3つのバックエンドを持ちます。このバックエンドに対応するのが、ローカルホストの8100番ポートから8102番ポートで待ち受けているMongrelです。poundは、このいずれかのMongrelへとリクエストを転送します。また、いずれかのバックエンドが死んでしまった場合、そのバックエンドにはリクエストを転送しません。
最後に残ったサービスでは特にルールを指定していません。つまり、ここでは上記のサービスのルールに一致しなかったもの全てに一致するということになります。これらのリクエストは、全てApacheへと転送されます。
Mongrel
インストール
今回は、Mongrelとそれをまとめて複数起動させることができるためのツールであるMongrelクラスターを利用します。インストールは、gemを利用すると簡単です。
user@myserv% sudo gem install mongrel -y user@myserv% sudo gem install mongrel_cluster -y
初回インストール時には、インストールするバージョンなどのいくつかの質問がされるかとおもいますが、環境にあった適切なものを選択してください。システムにインストールしているRubyのバージョンによっては、最新のMongrelを利用できない場合もあるかもしれません。
Mongrelクラスターの設定
インストールが無事に完了したら、RailsアプリケーションをMongrelクラスターで動作させる設定をしていくことにしましょう。Railsアプリケーションは、/var/www/rails.example.jpに配置してあるものとします。
まず、Railsアプリケーションルートである/var/www/rails.example.jpへと移動します。次に、Mongrelクラスターの設定コマンドを実行します。このコマンドの実行結果はconfig/mongrel_cluster.ymlへと保存されますので、最初に実行すれば以降この値が利用されます。
user@myserv% sudo mongrel_rails cluster::configure \ -e production -p 8100 -a 127.0.0.1 -l /var/log/mongrel.log \ -P /var/run/mongrel/mongrel.pid -c /var/www/rails.example.jp \ -r /var/www/rails.example.jp -N 3 --user www-data --password www-data
いくつかのオプションが指定してありますが、このオプションの意味は下記の通りです。
| オプション | 値の意味 |
|---|---|
| -e | 動作させるときのRAILS_ENV |
| -p | 割り当てるポート番号 |
| -a | バインドするアドレス |
| -l | Mongrelのログファイル |
| -P | pidファイルの位置 |
| -c | chdirするディレクトリ |
| -r | RAILS_ROOT |
| -N | 動作させるインスタンスの数 |
| --user | mongrelクラスターを動作させるユーザ権限 |
| --group | mongrelクラスターを動作させるグループ権限 |
今回は、mongrelをwww-dataユーザ・グループ権限で動作させます。logディレクトリtmpディレクトリがwww-dataによって書き込めるかどうかを確認しておきましょう。さらに、PIDファイルを保存するために、Debinaの流儀に従って/var/run/mongrelを作成し、www-dataが書き込めるようにします。
また、-eオプションで指定するRAILS_ENVは、必ず指定してください。config/environment.rbの値は無視されます。
設定が完了したら、無事に起動と終了ができることを確認します。
user@myserv% sudo mongrel_rails cluster::start user@myserv% sudo mongrel_rails cluster::stop
さらに、サーバ起動時と終了時にmongrel_clusterが自動的に起動できるようにしましょう。mongrelの配布ファイル中に起動スクリプトが含まれています。
user@myserv% sudo mkdir /etc/mongrel_cluster user@myserv% sudo ln -s /var/www/apps/testapp/config/mongrel_cluster.yml /etc/mongrel_cluster/ user@myserv% sudo cp /usr/local/lib/ruby/gems/1.8/gems/mongrel_cluster-0.2.1/resources/mongrel_cluster /etc/init.d user@myserv% sudo chmod +x /etc/init.d/mongrel_cluster user@myserv% sudo update-rc.d mongrel_cluster defaults
Apache
Apacheについてですが、様々なドキュメントがありますので、ここで詳細に説明することはしません。 名前ベースのバーチャルホストをrails.example.jp、svn.example.jp、myserv.example.jpでそれぞれ設定するだけです。ポートは8000番にしておきます。
まとめ
以上で、Pound、Mongrel、Aapacheという構成が完成します。ちょっとだけ書くつもりがいつの間にかに長くなってしまいしたが、ポイントとしてはシンプルで、Poundにちょっとしたルールを設定して、あとはなんとかしようということです。
最近の傾向として、なんでもHTTPかつ80番ポートという傾向があり、さまざまなサーバアプリケーションをひとつのポートで提供したいというニーズが高まっています。Poundはそうしたニーズにこたえるシンプルでいながら強力なアプリケーションですので、今後ますます活躍する機会が増えていくのではないでしょうか。今回紹介しきれなかったSSLの対応や、Poundで実現するHAなど面白い用途がいろいろありますので、興味をお持ちになった方はmanファイルを読んでみてください。

コメントする