tDiary を ruby1.9 + nginx + fcgi で動かしてみた
tDiary + ruby1.9 + fcgi で動かそうとして挫折した記録 - HsbtDiaryより。 そういえばうちのサーバも VPS なので、 FastCGI を使えるんだった。
環境
- CentOS 5.2
- nginx 0.6.39
- ruby 1.9.1-p378
- tdiary 2.3.3.20100326
Nginx のインストール
NginxはWebサーバの一種。Apacheより軽いらしい。 EPELに用意されているCentOS用のRPMを使ってインストール。
$ wget http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm $ sudo rpm -Uvh epel-release-5-3.noarch.rpm $ sudo yum install nginx
設定ファイルは /etc/nginx/nginx.conf にインストールされる。 起動と停止は init.d で。
sudo /etc/init.d/nginx start sudo /etc/init.d/nginx stop
spawn-fcgi のインストール
公式ドキュメントを読んでFastCGIの使い方を理解。 Nginx は FastCGI 経由でプロセスを呼び出すことはできるけど、プロセスを起動することはできないらしい。 そのため、 FastCGI で呼び出されるプロセスは、事前に起動しておく必要がある。 このプロセス起動に使うコマンドが spawn-fcgi。
sudo yum install spawn-fcgi
/usr/bin/spawn-fcgi にインストールされる。使い方は以下の通り。
Usage: spawn-fcgi [options] -- <fcgiapp> [fcgi app arguments] spawn-fcgi v1.4.22 - spawns fastcgi processes Options: -f <fcgiapp> filename of the fcgi-application -a <addr> bind to ip address -p <port> bind to tcp-port -s <path> bind to unix-domain socket -C <childs> (PHP only) numbers of childs to spawn (default 5) -F <childs> numbers of childs to fork (default 1) -P <path> name of PID-file for spawed process -n no fork (for daemontools) -v show version -h show this help (root only) -c <dir> chroot to directory -u <user> change to user-id -g <group> change to group-id
通信にポートを使うなら、-p コマンドでポート番号を指定する。UNIXソケットなら -s 。
spawn-fcgi -p <ポート番号> -F <起動プロセス数> -f <コマンド名>
Ruby 側の環境を整える
Ruby側にもFastCGI用のモジュールが必要。 ここでは、すでに ruby 1.9.1 を /usr/local/bin にインストール済みで、PATHにも通っているという前提。
sudo yum install fcgi-devel sudo gem install fcgi
これで require 'fcgi' が可能になる。
Pure Ruby のモジュールもある模様。どっちを使うのがいいのかは分からない。
sudo /usr/local/bin/gem install ruby-fcgi
Hello World
Ubuntu+Apache+Rack+FastCGIな環境でRubyを動かしてみるに載っていたサンプルをお借りして動作テスト。
#!/usr/bin/env ruby require 'rack' app = Proc.new do |env| Rack::Response.new.finish do |res| res.write "Hello, Rack!" end end Rack::Handler::FastCGI.run app
spawn-cgiを使って起動してみると、子プロセスがエラーコード127を返して終了してしまった。
spawn-fcgi -p 10003 -f hello.fcgi spawn-fcgi.c.230: child exited with: 127
ファイル名だけで、パスを指定していなかったのが原因。 UNIXのコマンドと同じく、パスを明記しないといけない。内部で exec しているのかな。
spawn-fcgi -p 10003 -f ~/work/fcgi/hello.fcgi spawn-fcgi.c.208: child spawned successfully: PID: 17931
起動に成功すると、プロセスが起動しているのが分かる。 プロセスの終了には kill コマンドを使うしかないのかな。
$ ps -ef | grep 17931 machu 17931 1 0 04:03 ? 00:00:00 ruby /home/machu/work/fcgi/hello.fcgi $ kill -TERM 17931
-Fオプションを指定すれば、複数のプロセスが起動する。
$ spawn-fcgi -p 10003 -F3 -f ~/work/fcgi/hello.fcgi spawn-fcgi.c.208: child spawned successfully: PID: 18061 spawn-fcgi.c.208: child spawned successfully: PID: 18065 spawn-fcgi.c.208: child spawned successfully: PID: 18066
Nginx側の設定
とりあえず最低限の設定をする。.fcgiのファイルが呼ばれた場合に、10003ポートで待ち受けているFastCGIプロセスと通信するように設定している。 実際に使うにはもう少し設定が必要。(.html -> ?date=$1への変換など)
server { listen 8080; server_name _; root /var/www/www.machu.jp/html; location /diary/ { index index.fcgi; } location ~ \.fcgi$ { fastcgi_index index.fcgi; fastcgi_pass 127.0.0.1:10003; include fastcgi_params; }
このとき、FastCGIプロセス側へCGIの環境変数を渡してあげないといけない。 渡す値は /etc/nginx/fastcgi_params に記述されているけど、1つだけ足りなかったので追記した。
--- /etc/nginx/fastcgi_params.default 2010-02-16 14:35:27.000000000 +0900 +++ /etc/nginx/fastcgi_params 2010-04-30 02:51:15.000000000 +0900 @@ -5,6 +5,7 @@ fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root;
SCRIPT_FILENAMEはtDiaryのindex.fcgiで使われているので、適切に渡してあげないと tDiary 側でエラーになる。
dir = File::dirname( cgi.env_table["SCRIPT_FILENAME"] )
この状態でHello Worldが表示されることを確認しておく。 大丈夫だったら、代わりにtDiaryのindex.fcgiを起動する。-F や -p オプションの値はお好みで。
spawn-fcgi -F4 -p 10003 -f /var/www/www.machu.jp/html/diary/index.fcgi
これで Ruby 1.9 + FastCGI + Ningx で tDiary が動いた。
速度比較
abを使って比較してみた。
Apache + CGI
Requests per second: 0.81 [#/sec] (mean) Time per request: 1239.777 [ms] (mean) Connection Times (ms) min mean[+/-sd] median max Connect: 16 16 0.4 16 17 Processing: 916 1224 625.1 974 2339 Waiting: 606 911 618.0 665 2014 Total: 932 1240 625.5 989 2356
Nginx + FastCGI
Requests per second: 1.18 [#/sec] (mean) Time per request: 849.381 [ms] (mean) Connection Times (ms) min mean[+/-sd] median max Connect: 16 24 15.7 18 52 Processing: 733 825 112.0 819 1008 Waiting: 407 455 54.1 450 546 Total: 749 849 108.7 836 1028
3割くらい速くなった。
課題
日記の表示画面はFastCGI化できた。あとは更新画面 (update.rb) をどうするか。 自分しか使わないからFastCGIを使わずに通常のCGIでいいんだけど、Nginxは直接CGIを起動できないという制約がある(FastCGI経由で起動することは可能)。
所感
速度面では FastCGI のほうが有利。 ただし、NginxでのFastCGI はプロセスの起動を自分で管理しないといけない。 Nginx+FastCGIを使う場合は、
- プロセスが常駐するため、メモリを消費する
- プロセスが異常終了した場合に、自動で再起動する仕組みが必要
あたりがデメリットか。
Apache付属のmod_fastcgiやfcgidはプロセスを自動生成してくれる…のかな? Railsが流行りだした頃にApacheとFastCGIの組み合わせは推奨されないと言われていたけど、今ではそうでもないんだろうか。
いずれにしても、こうやって別プロセスで管理するのなら、FastCGIを使うよりもリバースプロキシ + HTTPサーバ (Thin, Mongrel) + Rackを使った方がデバッグが楽だろうなぁ。