tDiary を ruby1.9 + nginx + fcgi で動かしてみた
2010-04-29
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を使った方がデバッグが楽だろうなぁ。