【fcgi】解説 Lighttpd プラグイン

Lighttpdのプラグイン開発に苦戦しています。

  • 低レイヤプログラミングになる程, 特にメモリに注意しないといけない
  • システムコールの戻り値に気をつかう(エラーハンドリング)
  • 様々なコンテキストに対応する必要がある

I/O多重化の時の挙動をもっと理解しないといけないなと思ってます。
システムプログラミングという領域は, OSを理解することなしでは成り立たないですね。

息抜きということでLighttpdのインストールとLighttpd上で動くアプリについて書きます。

fcgi_pic

OSXにLighttpdをインストールする

OSXにLighttpdをインストールする手順です。

$ ./configure --without-pcre --prefix=/usr/local/lighttpd/lighttpd-1.4.32
$ make
$ make install

configure で –without-pcre はpcreがなかったから指定しました。
正規表現は大事なので入れましょう。prefixでインストール場所を指定します。

/usr/local/etc/lighttpd.confを開いて, ポート, ドキュメントルート, logの場所を以下のように設定します。

server.document-root        = "/usr/local/var"
server.errorlog             = "/usr/local/var/log/lighttpd.error.log"
server.port                = 80
## bind to localhost (default: all interfaces)
server.bind = "127.0.0.1"

lighttpdを起動します。index.htmlでも作って確認してください。

# start
$ sudo /usr/local/sbin/lighttpd -f /usr/local/etc/lighttpd.conf

CGIとFastCGIの違い

CGIは仕様自体が古くWEBサーバと通信するプロセスは, リクエスト毎に起動される必要があります。
そのためサーバが毎回インタプリタを起動しなければなりません。
一方で, FastCGIの場合WEBサーバにインタプリタを組み込む代わりに, バックグラウンドでプロセスを生成しておきソケット通信します。
プロセスは起動後ループしているため, 毎回起動するCGIに比べ速くなります。

CGIで動かす

lighttpd.confを変更して, CGIを使えるようにします。

server.modules += ("mod_cgi")

cgi.assign = (
".bin" => "",
".py" => "/usr/bin/python",
".pl" => "/usr/bin/perl",
".fcgi"=> "/usr/bin/python",
".rb" => "/usr/bin/ruby"
)

必要なpythonパッケージをインストールします。

$ sudo easy_install web.py

hello.pyを作成して, 定番のhello worldを書きます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import web

print "Content-Type: text/plain"
print
print "hello world."

CGIで使うためのおまじないをかけます。

$ chmod +x hello.py 

ブラウザを開いてlocalhostにアクセスします。

hello_py

lighttpd.confをparseしているのはconfigfile.cとconfigfile-glue.cです。
プラグインのフックエントリがjoblistの場合は、リクエストが確定する前にjoblistが呼ばれるので “.py” -> “/usr/bin/python” のように記述してもルーティングしてくれないようです。従って、プラグイン内でrequest.url->ptrで文字比較して判定する必要があります。

FastCGI

次はFastCGIです。まずは, lighttdの設定からです。

server.modules += ("mod_fastcgi")

fastcgi.debug = 1

fastcgi.server = (
  "/hello_wsgi.fcgi" =>
 (( 
 "host" => "127.0.0.1",
 "port" => "1111",
 "bin-path" => "/usr/local/var/hello_wsgi.fcgi",
 "max-procs" => 1
 ))
)

url.rewrite-once = (
"^/favicon.ico$" => "/static/favicon.ico",
"^/static/(.*)$" => "/static/$1",
"^/(.*)$" => "/hello.py/$1",
)

FastCGIはバックグラウンドでプロセスを生成しておくことでCGIと起動と終了のコストをなくせます。
一方で, リクエストが来なくても一定のメモリを消費する・このプロセスを監視する必要があるというデメリットもあります。

unix-domain socketで他のポートのプロセスと通信することが必要ですが, ここではspawn-fcgiを使って unix-domain socketの設定を肩代わりさせます。

$ tar zxvf spawn-fcgi-1.6.3.tar.gz
$ cd spawn-fcgi-1.6.3
$ ./configure
$ make
$ make install

spawn-fcgiは/usr/local/bin/spawn-fcgiにあります。

Web Server Gateway Interface (WSGI, ウィズギー) モジュールをインストールします。

$ sudo easy_install flup

サーバアプリケーションを書いてhello_wsgi.fcgiという名前で保存します。
[1] https://flask.pocoo.org/docs/deploying/fastcgi/

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

from cgi import escape
import sys, os
from flup.server.fcgi import WSGIServer

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])

    yield '<h1>This is FastCGI</h1>'

WSGIServer(app).run()

起動の手順はfcgiプロセス -> lighttpd の起動の順で行います。

$ sudo killall lighttpd
# if used fcgi_proccess , kill PID
$ spawn-fcgi -f /usr/local/var/hello_wsgi.fcgi -a 127.0.0.1 -p 1111 -u www-data -g www-data
$ spawn-fcgi: child spawned successfully: PID: 29955
$ sudo /usr/local/sbin/lighttpd -f /usr/local/etc/lighttpd.conf

ブラウザを開いてみましょう。

fastcgi


[2] mod_proxyのソースコードリーディング
[3] mod_websocketなるものを発見しました。シェア1%のサーバにwebsocketのプラグインを書く人がいることに感動です。