Edited at
nginxDay 20

NginxでCGIを動かそうと頑張った話

More than 3 years have passed since last update.

この記事はnginxアドベントカレンダーの20日の記事です。

Nginx、いいですよね。速くて設定も楽で。

でもCGI使えません。今時CGIなんて使ってんじゃねーよ!という声もごもっともなのですが、用途が極めてニッチなアプリケーションの中には、未だにCGIのものも少なくないのです。その大きな大きな理由が、 Apacheで動いている旧態依然としたレンタルサーバ向けの用途を念頭に置いている というものです。


Why CGI

CGIが、FCGIやWSGI等の新しい技術と比較して段違いに優位な点は、FTPでレンタルサーバの然るべき場所に然るべきパーミッションで設置するだけで使えるところです。これは比較的ネットワーク関連の知識がなくても、身内だけで該当ツールを用いて遊べればそれでいい、という人に対しては、敷居を下げる点で優位に働きます。

一方で、SSHも叩けない軟弱なサーバへの設置を良しとしない私のような偏屈な人に対しては、敷居を下げたはずのCGIが設置の足枷になってしまいます。何せnginx単体で設置ができないのですから。


fcgiwrap

そこでnginxと協力して、完全にCGIとして書かれたプログラムをFCGIにラップしてくれるプログラム、fcgiwarpを使って、CGIを動かしてみる事にします。

素直にApache使えよ、という言葉は聞こえない

以下の作業はDebian jessieおよびnginx1.9.9でテスト済みです。


【注意】 FCGIにラップする ≠ FCGIの性能が出る

fcgiwrapは、あくまでFCGIのインターフェイスを提供してくれるだけで、裏ではforkしてCGIプログラムを実行しています。

100%FCGIで書かれたアプリケーションのパフォーマンスには遠く及びません。あくまでnginxの裏で動かせるようになるだけです。


インストール

Nginx1.9.9は、公式サイトのmainlineリポジトリから取得しておきます。

Debian jessieにおいては、fcgiwrapはdebパッケージが提供されているので、 apt-get install fcgiwrap するだけで入ってくれます。


nginxの設定

全ての .cgi 拡張子を持つファイルをCGIとして実行する設定が以下になります。

location ~ \.cgi$ {

root /path/to/root;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

これだけでOKです。PHPをPHP-FPMで動かす設定とあまり変わりませんね。違いはfastcgi_passの部分だけです。

fastcgi_passに指定している/var/run/fcgiwrap.socketですが、これはdebパッケージからfcgiwrapをインストールすると勝手に作成されています。


fcgiwarpの設定

fcgiwrapの設定は、initスクリプトを直接編集しなければなりません。

インストールしたての状態では、以下のような記述になっています。


/etc/init.d/fcgiwrap(抜粋)

# FCGI_APP Variables

FCGI_CHILDREN="1"
FCGI_SOCKET="/var/run/$NAME.socket"
FCGI_USER="www-data"
FCGI_USER="www-data"
# Socket owner/group (will default to FCGI_USER/FCGI_GROUP if not defined)
FCGI_SOCKET_OWNER="www-data"
FCGI_SOCKET_GROUP="www-data"

このうち変更しなければならないのは、 FCGI_SOCKET 以外の全てです。


FCGI_CHILDREN

リクエストを待つワーカーの数です。 このワーカー1つごとにCGIをforkして実行する ため、nginxのワーカー数と同じようにCPU数と同じにすれば良いという訳にはいきません。適切な値はサーバや実行するアプリケーションごとに違うはずなので、見極める必要があります。


FCGI_USER、FCGI_USER

CGIを実行するユーザ・グループ名です。これが適切に設定されていないとパーミッションのエラーが起きて4xx系のエラーになります。

デフォルトはwww-dataで、これはDebianのリポジトリからnginxをインストールした場合は変える必要がないのですが、今回は公式リポジトリから最新のnginxインストールしたため、「nginx」ユーザに変える必要があります。


FCGI_SOCKET_OWNER、FCGI_SOCKET_GROUP

/var/run/fcgiwrap.socket の持ち主になります。上記の通りwww-dataユーザは今回いないので書き換える必要がありますが、コメントアウトに書いてあるとおり設定しなければFCGI_USER、FCGI_USERと同じになるので、いちいち書き換えるのが面倒ならばこの2行はコメントアウトしても良いと思います。

全て設定が終わったら、 sudo systemctl restart fcgiwrap で再起動してあげましょう。


テストしてみる

nginxの方で設定したrootのディレクトリに適当なCGIスクリプトを 実行権限をつけて 置いてアクセスし、ちゃんと実行結果が表示されれば成功です。

私は実行権限をつけるのを忘れて5分くらいハマりました。


index.cgi

#!/usr/bin/perl

print "Content-Type: text/html\n\n";
print "<html>";
print "<head><title>Test</title></head>";
print "<body>";
print "<h1>Hello CGI World!</h1>";
print "</body>";
print "</html>";