ブログトップ 記事一覧 ログイン 無料ブログ開設

Kazzzの日記 このページをアンテナに追加 RSSフィード

2009-01-22

[][][]Ruby on RailsをIIS7 FastCGIホストする


丸二日かかったが、なんとかRailsアプリケーションWindows VistaのIISからホストできたので、まとめてみた。

内容に関しては全面的に以下のサイトを参考にさせて頂いた。これらの情報が無かったら今回の成功は絶対に無かっただろう。
HowToConfigureIIS7 in Ruby on Rails - Ruby on Rails Wiki
Ruby on Rails in IIS 7.0 with URL Rewriter - RuslanY Blog
内容は似ているものの違いがあるが、基本的には日付が新しいRuslanY Blogの解説を参考にさせて頂き、あとは自分環境で上手くいった内容を使っている。必要無かった作業に関しては割愛しているが、あくまで私の環境であって、他の環境ではまた違うかもしれない。(RubyもRailsもマイナーバージョンが違うだけで結構違いがあるので、一つずつ動作を確認していくしかない)

Windows Vista Bussiness(Ultimate) + Service Pack 1
Ruby 1.8.7 [i386-mswin32]
Ruby On Rails 2.2.2
RubyForIIS
Microsoft URL Rewrite Module for IIS 7.0, Go Live

  • 必要なソフトウェアの導入

1. IIS7をFastCGI機能込みでインストール
先日のエントリにもあるが、RubyをIISからホストするにはFastCGIサポートが必要だ。FastCGIサポートはWindows Vista SP1のIISのCGI機能に含まれているので、単にCGI機能付きでIIS7をインストールすれば良い。

2. RubyForIISをインストール
RubyForIISはFastCGIのサポートをRubyに追加するライブラリィ。インストーラ付属しておりRubyディレクトリ内に必要なファイルコピーしてくれはずだが、必ずしも必要なスクリプト(fcgi.rb)がパスが通った場所に配置されるとは限らないらしいので同スクリプトがロードできるか、IRB等で確認しておく必要がある。

例) fcgiがロードできるか確認する

irb(main):002:0> require 'fcgi'
=> true
irb(main):003:0>

もしこれでロードが失敗する場合、RubyForIIS下のファイル全てを$RUBY_HOME下等にコピーして、再度チェックする。

実は、当初作業したときにこの確認を怠ったために、後でエラーとなってはまった。
"<handler> scriptProcessor could not be found in <fastCGI> application configuration"
f:id:Kazzz:20090120173526p:image
このエラーだけ見ても原因はまず分からないので、ゆめゆめ注意が必要だ。


3. Microsoft URL Rewrite Module for IIS 7.0をインストール
MicrosoftがIIS7のURL書き替えの為のモジュール。IIS7のマネジャのスナップインとしてURL書き替えのルール編集できる。
後述するが、Railsは自らにルーティングされる内部パスしか処理することができないため、アプリケーション外の静的なURLは処理できない。また、Railsは一部のリソースに対してキャッシュされるのを防止するために特殊な'アセット'サフィクスが付加されるが、アセットが追加されたURLはRailsのハンドラは適応できてもIISのファイルのハンドラは対応できずに正しいリソースを探し出せない。URL Rewrite Moduleはこれらの問題をFIXするために使用される。

必要なソフトウェアの導入が完了したならば、IISのマネジャを使用してサイトを作り、アプリケーションを対象のRailsアプリケーションにマッピングする。
IISのサイトとRailsのアプリケーションのマッピング方法はいろいろなやり方があると思うが、簡単なのはIISの管理上の'サイト'をRailsのアプリケーションのルートに直接マップすることだ。これだとWEBRickでテストする場合とURLは変わらないが、IISは同時に開けるサイトは一つだけなので、複数のアプリケーションを同時に使用する場合はポートを変える必要が出てくる。

もう一つはサイトの配下に複数のRailsアプリケーションを作成する方法だ。

e:
cd www
rails application1
:
rails application2
:
:

この方法だと幾つアプリケーションを増やそうが、サイトは一つなので管理は楽になる。
(この方法だとIISがマップするパスとRailsアプリケーションがルーティングするパスが一致しなくなる。とりあえずベタroutes.rbを変更して、アクセスできるようにしたのだが、結局これが後に問題とになる。)

例) application1のroutes.rb
ActionController::Routing::Routes.draw do |map|
  map.connect 'application1/:controller/:action/:id'
  map.connect 'application1/:controller/:action/:id.:format'
end


サイトとアプリケーションのマップが出来たら、FastCGIからrubyを起動するためにハンドラマピングを追加する。

IISマネジャ->サイトを選択->ハンドラマッピング->モジュールマップの追加
f:id:Kazzz:20090120173527p:image
このようにGUIで設定しても良いが、web.configのフラグメントを作成して対象アプリケーションのルートに配置することでハンドラマッピングをアプリケーション毎に設定できる。(というかこの方が簡単)
web.configの例 (addressbook)

<configuration>
    <system.webServer>
    <handlers>
        <clear />
        <add name="Addressbook" path="*" verb="*" modules="FastCgiModule" scriptProcessor="E:\ruby\bin\ruby.exe|E:\www\addressbook\public\dispatch.fcgi development"※ resourceType="Unspecified" />
        <add name="Static" path="*" verb="*" modules="StaticFileModule" resourceType="File" />
    </handlers>
  </system.webServer>
</configuration>

この例では、$RUBY_HOMEをe:\ruby、IISのサイトをe:\www、railsで作成したサンプルのアプリケーションとしてe:\www\addressbookとしている。(routes.rbも修正済み)
アプリケーションはとりあえず動作確認だけできれば良いので、以下のコマンドで必要最小限のアクションだけを追加した。
e:\www\addressbook>ruby ./script/generate controller test index about


test_controller.rb
class TestController < ApplicationController
  def index
    render :text=>"The index action"
  end
  def about
    render :text=>"The about action"
  end
end

以降、Webブラウザから以下のようにURLを指定することで上記のアクションが実行することを確認できれば成功だ。
http://locahost/addressbook/test/index
http://locahost/addressbook/test/about


ちなみにここは今でも解らないのだが、scriptProcessorc属性で設定する値としてはruby.exeへのパスとパイプで渡すスクリプトへのパスを書く、ここまでは良いのだが、その後のパラメタ"development"は必要なのか? ということ。
scriptProcessor="E:\ruby\bin\ruby.exe|E:\www\addressbook\public\dispatch.fcgi development"

wiki.rubyonrails.orgの記事では書くようには説明が無いが、ruslany.netの記事ではこのパラメタの記述があるのだ。パラメタの名前からして、環境変数 'ENV_RAILS'の規定値だと思うのだが、ここで説明通りパラメタを記述しておくと、以下のようにエラーになってしまうのだ。
f:id:Kazzz:20090120173525p:image

なお、このようにweb.configを作る場合、ルートの設定(applicationHost.config)の特定のセクションを上書き(override)することとなるが、IIS7のデフォルトの設定では各セクションの上書きは禁止("Deny")になっているので、そのままでは設定がエラーになってしまう。
従って、applicationHost.configの該当セクション、つまり"handlers"セクションの上書きを許可するように修正する。
<sectionGroup name="system.webServer">
    <section name="asp" overrideModeDefault="Deny" />
    <section name="caching" overrideModeDefault="Allow" />
    <section name="cgi" overrideModeDefault="Deny" />
    <section name="defaultDocument" overrideModeDefault="Allow" />
    <section name="directoryBrowse" overrideModeDefault="Allow" />
    <section name="fastCgi" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
    <section name="globalModules" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
    <section name="handlers" overrideModeDefault="Allow"/>
    :
    :

さて、実際にアクセスしてみよう。以下のようにテキストだけがレンダリングされれば成功だ
f:id:Kazzz:20090121172305p:image

Railsは「開発モード」「テストモード」「製品モード」を環境変数で切り換えることができるが、IISを使う場合、上記のapplicationHost.configにおけるfastCgi要素に環境変数'RAILS_ENV'を記述することでモードを設定できる。

<fastCgi>
<application 〜>
<environmentVariables>
<environmentVariable name="RAILS_ENV" value="production" />
</environmentVariables>
</application>
</fastCgi>

なお、これは既に紹介したが、"Administration Pack"を使うことでGUIで編集することも可能。

  • Microsoft URL Rewrite Moduleを使ってRailsだけではサポートできない静的なURLに対応する

Ruby(Rails)をリクエストハンドラにした場合、アプリケーションが動作する動的なURLは勿論のこと、イメージやスクリプトなどの静的なリソースも全て処理対象になる。この場合、アプリケーション以外のパス上にあるリソースはRailsのルーティングの対象外になるため、Railsのリクエストハンドラではルーティングを解決できずにエラーとなってしまうことがある。
このような問題はURLの書き換えを使って回避することができる。IIS7にも幸いURL書き換えを機能に追加することができるため、これを使って静的なリソースに関しては、IISの静的なファイルを処理するハンドラにリクエストを委譲してやれば良い。
具体的には、以下のURL書き換えルールをIIS URL Rewrite Moduleに対してインポートしてやるだけだ。

# Redirect all requests not available on the filesystem to Rails   
RewriteEngine On   
RewriteRule ^$ index.html [QSA]   
RewriteRule ^([^.]+)$ $1.html [QSA]   
RewriteCond %{REQUEST_FILENAME} !-f   
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]  

IISのURL RewriteModuleはmod_rewriteルールのインポートが可能であり、上記のルールをそのまま貼りつけることができる。素晴らしい。
f:id:Kazzz:20090121172306p:image

なお、上記のルールで書き換えたURLとそれに伴うリクエストはRailsで処理するためにdispatch.fcgiというスクリプトで実行されなければならない。それが最後のルールにもなっているのだが、このように修正した場合、アプリケーションにマップしたハンドラのパスも"*"ではなく、dispatch.fcgiにする必要がある。
<add name="Addressbook" path="dispatch.fcgi" verb="*" modules="FastCgiModule" scriptProcessor="E:\ruby\bin\ruby.exe|E:\www\addressbook\public\dispatch.fcgi" resourceType="Unspecified" />


インポートしたルールの内容は適用するとweb.configに反映される。以下、書換ルールが反映された、addressbookアプリケーションのための最終的なweb.configの内容だ。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
    <handlers>
        <clear />
        <add name="Addressbook" path="dispatch.fcgi" verb="*" modules="FastCgiModule" scriptProcessor="E:\ruby\bin\ruby.exe|E:\www\addressbook\public\dispatch.fcgi" resourceType="Unspecified" />
        <add name="Static" path="*" verb="*" modules="StaticFileModule" resourceType="File" />
    </handlers>
        <rewrite>
            <rules>
                <rule name="Imported Rule 1" enabled="true">
                    <match url="^$" ignoreCase="false" />
                    <action type="Rewrite" url="index.html" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 2" enabled="true">
                    <match url="^([^.]+)$" ignoreCase="false" />
                    <action type="Rewrite" url="{R:1}.html" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 3" enabled="true" stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" pattern="" ignoreCase="false" />
                    </conditions>
                    <action type="Rewrite" url="dispatch.fcgi" appendQueryString="true" />
                </rule>
            </rules>
        </rewrite>
  </system.webServer>
</configuration>

IIS7-FastCGIから実行したRuby on Railsは当然だがWEBRickを使った場合よりも、一度目のロードに時間がかかるが、きびきびと動く印象である。
ただ、このような変則的?な実行環境の場合、なにかしら制限があるので、暫く慎重に見ていく必要があるだろう。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/Kazzz/20090122/p1