unoh.github.com

RackでWebアプリのWebサーバー依存を無くす

Mon May 07 08:50:25 -0700 2007

rack-logo
rack-logo posted by (C)komagata

komagataです。

仕事でも使う必要が出てきたのでRubyの勉強をしています。WebアプリケーションでRubyを使おうとしていきなり躓いたのがApache、WEBrick、Mongrel等、実行環境毎の設定やAPIの違いです。

Rubyを普段使っている人には常識過ぎるのか情報が少なく、FastCGIで単に「Hello, World」を表示させるのにも一苦労でした。(Railsでは簡単に動くのが悔しい)

そんな実行環境毎のAPIの差を吸収してくれるRackというライブラリを知ったので試してみました。

RackはRuby版WSGIと呼ばれているそうです。WSGIとはWeb Server Gateway Interfaceの略でWeb ServerとWeb Applicationの間のInterfaceを定めたPython界の仕様だそうです。

Web ApplicationはWSGIの仕様に沿ったAPIを使って作ればWeb Serverが変っても問題無いようになるはずです。

まずはこちらを参考に最低限のものを書いてみました。

インストール:

$ gem install rack


hello-rack.rb:

#!/usr/bin/env ruby
require 'rubygems'
require 'rack'
include Rack
 
class HelloRack
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
  end
end
 
Handler::WEBrick.run HelloRack.new, :Port => 3000


これを起動してみます。

$ ruby hello-rack.rb
[2007-05-07 18:43:31] INFO  WEBrick 1.3.1
[2007-05-07 18:43:31] INFO  ruby 1.8.5 (2006-08-25) [i486-linux]
[2007-05-07 18:43:31] INFO  WEBrick::HTTPServer#start: pid=19600 port=3000


ブラウザから3000番にアクセスしてみると・・・

hello_rack
hello_rack posted by (C)komagata

動きました!

HandlerにはWEBrickの他にCGI, FastCGI, Mongrelがあります。それぞれのHandlerを使うことで環境を切り替えることができます。

RequestやResponseはそれぞれRack::Request, Rack::Responseに分かりやすい感じで抽象化されているので環境差を気にせず使えそうです。

しかし、

「結局プログラムを変えなきゃいけないなら意味無いじゃないか、Railsならオプションや設定だけで変えられるのに!」

などと思われたかもしれません。

それを解決するためにRackではrackupというコマンドがあります。rackupとDSL風な設定ファイルを使ってWebサーバー依存をプログラムから追い出すことが出来ます。

rackupコマンド:

$ rackup --help
Usage: rackup [ruby options] [rack options] [rackup config]
 
Ruby options:
  -e, --eval LINE          evaluate a LINE of code
  -d, --debug              set debugging flags (set $DEBUG to true)
  -w, --warn               turn warnings on for your script
  -I, --include PATH       specify $LOAD_PATH (may be used more than once)
  -r, --require LIBRARY    require the library, before executing your script
 
Rack options:
  -s, --server SERVER      serve using SERVER (webrick/mongrel)
  -o, --host HOST          listen on HOST (default: 0.0.0.0)
  -p, --port PORT          use PORT (default: 9292)
  -E, --env ENVIRONMENT    use ENVIRONMENT for defaults (default: development)
 
Common options:
  -h, --help               Show this message
      --version            Show version


まずアプリケーションの方からサーバー依存を取ります。

hello-rackup.rb:

require 'rack/request'
require 'rack/response'
 
module Rack
  class HelloRackup
    def call(env)
      Response.new.finish do |res|
        res.write "Hello, Rackup"
      end
    end
  end
end


(hello-rack.rbの例もそうですが、要はcallできてResponseっぽいものを返すものならば何でもいいみたいです。)

Rackのexampleについているlobstericiousというふざけたアプリケーションを参考にして設定を書きます。(拡張子はrbではなくruです。rackupだからでしょうか?)

lobstericious:

lobstericious
lobstericious posted by (C)komagata

hello-rackup.ru:

require 'hello-rackup'
run Rack::HelloRackup.new


rackupコマンドをこの設定ファイルを指定して起動します。

$ rackup -s webrick -p 9292 hello-rackup.ru
[2007-05-07 19:27:29] INFO  WEBrick 1.3.1
[2007-05-07 19:27:29] INFO  ruby 1.8.5 (2006-08-25) [i486-linux]
[2007-05-07 19:27:29] INFO  WEBrick::HTTPServer#start: pid=22015 port=9292


これでブラウザから9292番にアクセスすると、

hello_rackup
hello_rackup posted by (C)komagata

動きました!

-sオプションにmongrelを指定すればmongrelで起動します。アプリケーション本体には手を入れずに済むようになりました。

更に設定ファイルでは下記のようにProxyのように透過的に動作するMiddlewareを簡単に追加することが出来ます。(このuseというメソッド、Rubyに慣れていないのでPerlみたいに普通にあるのかと思ってKernelモジュールや予約語一覧を探し回ってしまいました・・・)

require 'hello-rackup'
 
use Rack::ShowExceptions
use Rack::CommonLogger
use Rack::Lint
 
run Rack::HelloRackup.new


例えば、Rack::ShowExceptionsを追加するとはエラーをPythonのフレームワークDjango風に表示にし、Rack::CommonLoggerを追加するとApache風のログを出力します。Rack(棚)にMiddlewareを積んでいって機能を追加していくイメージなのかもしれません。

Djangoにそっくり:

traceback_ruby
traceback_ruby posted by (C)komagata

ところでCGIやFastCGIはどうするのかというと、正しいやり方がわかってません・・・。今は苦肉の策でこんなファイルを置いています・・・。

hello-rackup.fcgi:

#!/bin/sh
rackup hello-rackup.ru -s fastcgi


これはかなりヘボいのでどなたか正しいやり方を教えていただければ幸いです・・・。

このようにRackを使うことでRailsのようにWebサーバごとのエントリポイントやrackup用の設定ファイルを用意しておくだけで本体のアプリには手を入れずに様々な環境で動作するWebアプリケーションが書けます。オープンソースで配布するアプリケーションを作る場合などには役に立つのではないでしょうか。