2018年9月20日木曜日

FreeBSD で nginx に挑戦してみた(その3:location コンテキストと CGI)

 前回,FreeBSD で nginx に挑戦してみた(その2)として,FreeBSD 上の nginx の主な設定と起動の方法について書いた。 今回は,location コンテキストと CGI について書いていこう。

location コンテキスト


まず location コンテキストの公式の説明は下記を見て欲しい。
 ・location コンテキスト:Module ngx_http_core_modulelocation 項目

location コンテキストは,nginx.conf(それ自体が main コンテキストとなっている)の中の http コンテキストの中の server コンテキストの中に置かれるコンテキストである(言葉で書くとややこしいなぁ…)。前回を見て欲しいが,nginx.conf では,http コンテキストで web サーバーとしての設定を記載する。その中には,複数の(仮想)サーバー(バーチャルホスト)を列挙することができ,(仮想)サーバー1個ごとに1つの server コンテキストを記載する。 server コンテキストの中では,どのポート宛のものに応答するかや,どのサーバー名宛のリクエストに応答するか,などを記載するが,その server に対するリクエスト URI( server 中の特定のディレクトリや特定の拡張子を持つファイルへのアクセス等を記載)にどのように応答するか,を記載するのが location コンテキストである。 そのため location コンテキストは通常は server コンテキスト中に複数(多数)存在することになる。

 以下に location コンテキストのパターンを書こう(公式ドキュメントの location 項目の記載をそのまま手コピーしただけだが…)
location = / {
    [ configuration A ]
}

location / {
    [ configuration B ]
}

location /documents/ {
    [ configuration C ]
}

location ^~ /images/ {
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

 location コンテキストは,第1パラメーターとして「一致条件((修飾子+)URI)」を必要とする。 リクエスト URI がこの一致条件に合致すれば,その location コンテキストの内容が適用される。

 一致条件には大きく分けて2種類あり,「接頭辞文字列」(prefix string) と「正規表現」がある。 修飾子が「~」あるいは「~*」の場合は正規表現とみなされ,それ以外の修飾子(「=」,「^~」,(なし))の場合は接頭辞文字列とみなされる。

正規表現の場合,修飾子「~*」は大文字小文字を区別せず,「~」の場合は大文字小文字を区別する正規表現として,合致するかどうかが判断される。

「=」修飾子の接頭辞文字列の場合は,リクエスト URI と完全に一致した時に合致したとみなされる(完全一致)。
他の接頭辞文字列の場合,リクエスト URI の先頭から接頭辞文字列に一致していれば,後ろには何が続いても合致するとみなされる(前方一致)。

 nginx では,要求された URI に対して,複数ある location コンテキストの一致条件を検索する。
(1) 一番優先度が高いのが「=」修飾子を持つ接頭辞文字列の場合であり,
  記載順に検索して最初に一致した「=」接頭辞文字列の場合の location コンテキストの設定が適用される。

(2) 次にその他の接頭辞文字列条件を「すべて」検索し,「最も長く一致する」接頭辞文字列の場合を「記憶」しておく。
  ここで最も長く一致する接頭辞文字列が「^~」修飾子を持つ場合は,その location コンテキストの設定が適用される。

(3) その次に正規表現で合致するものを,記載順(上から順)に調べていき,
  最初に一致した正規表現を持つ location コンテキストの設定が適用される。

(4) 正規表現で合致するものがなければ,接頭辞文字列で
  一番長く一致した location コンテキストの設定が適用される。

 上記の表の例だと,「/」のみのリクエストなら,設定「A」が適用される。 「/index.html」なら設定「B」になる。 「/documents/document.html」なら設定「C」になり,「/images/1.gif」なら「D」,「/documents/1.jpg」なら「E」が適用される。

 この判定条件がわかっていないと,nginx の設定をしてもうまく動作しない,ということになってしまう。


nginx における CGI


 nginx で CGI を使うには,(1) nginx に FCGI モジュールを組み込み,(2) fcgiwrap アプリを用いればよい。 この場合,nginx と fcgiwrap の間のやりとりは unix domain socket を通じて行う。 ちまたでは,fcgiwrap の他に spawn-fcgi も必要と書いてあることがあるが,unix domain socket を使う場合にはいらないみたい。

 nginx 自体は CGI を使うためのモジュールはもっていない。 その代わり FastCGI (FCGI) のモジュールが公式にサポートされている。 nginx で fcgi を使うには,インストール時に fcgi モジュールを組み込んでおかないといけないが,FreeBSD の /usr/ports/www/nginx/ からインストールした場合には,make config の際に fcgi モジュールを選ぶ選択肢はなかったので,最初から組み込まれるようになっているような気がする。 そのため,今回は特に何かをしないといけない,ということはなかった。

 fcgiwrap は cgi を fcgi で使えるようにするものであり,ほとんどの場合,cgi のスクリプトをそのまま実行してくれる。 FreeBSD では /usr/ports/www/fcgiwrap/ からインストールできる。 インストール後,/etc/rc.conf に以下を追加した。
fcgiwrap_enable="YES"
fcgiwrap_user="www"
fcgiwrap_socket_owner="www"
fcgiwrap_socket="unix:/var/run/fcgiwrap/fcgiwrap.socket"
 ここで user と socket_owner を共に www にしている。 これは nginx と fcgiwrap の間で socket をやり取りする際に,同じ user,owner でなければうまくいかなかったから,である。 最後の行が unix domain socket を使う,と宣言してるようなもの,だと思っている(実はわかってない…)

 rc.conf の中に /var/run/fcgiwrap/ というディレクトリが出てくるので,念のために「/var/run/fcgiwrap/」を作り,permission を 777 にしておいた。

後は,以下のようにして起動すれば準備できあがり,である。
# service fcgiwrap start


CGI を使うための nginx の設定


 以下に CGI を使うための nginx.conf の簡単な例を載せよう。 前回書いた設定ファイルの例と違う部分に色を付けてみた。 赤い部分は,fcgi とは直接関係はなく,青い部分が fcgi に関連している。 説明は設定例の下で書こう。
(nginx 設定ファイル:CGI を使う例)
user www www;
worker_processes  2;
error_log  /var/log/nginx/error.log error;
pid        /var/run/nginx.pid;
# ==================================================================
events {
    worker_connections  500;
    use                 kqueue;
}
# ==================================================================
http {
    server_tokens      off;
    include            mime.types;
    default_type       application/octet-stream;
    charset            UTF-8;
    sendfile           on;
    tcp_nopush         on;
    keepalive_timeout  75;
    gzip               on;

    root   /usr/local/www/data;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log         /var/log/nginx/access.log  main;
    error_page  401              /401.html;
    error_page  403 404          /404.html;
    error_page  500 502 503 504  /50x.html;
    # ---------------------------------------------------------
    server {
        listen       80 default_server;
        server_name  www.xxxxx.com yyy.xxxxx.com zzz.xxxxx.com;
        location / {
             index  index.html /index.html;
        }
        location ~ \.cgi$ {
            include        fastcgi_params;
            fastcgi_pass   unix:/var/run/fcgiwrap/fcgiwrap.socket;
            fastcgi_index  index.cgi;
            fastcgi_param  SCRIPT_FILENAME  /usr/local/www/data$fastcgi_script_name;
        }
    }
}
まずは,fcgi に関連する青い部分について書こう。 と言っても,あまり説明するところはないが…。

最初に,location コンテキストの一致条件は「正規表現」で与えられ,リクエスト URI が「.cgi」で終わっている場合に適用される。 それ以外は一個上の「/」を「接頭辞文字列」として持つ location コンテキストの設定が適用される。

次に「fastcgi_params」を読み込んで (include) いる。 これは FreeBSD の場合 /usr/local/etc/nginx/ の下にインストール時に入れられていた fcgi の設定ファイルみたい。 今回はいじってないので,中身は書かないでおこう。

fastcgi_pass ディレクティブは,nginx と fcgi モジュール間の通信のルートを示していて,ここでは fcgiwrap の設定と同じ unix domain socket を指定している。unix domain socket 以外には IP + port 番号で指定することもできる。location のコンテキストと,location コンテキスト中の if ディレクティブに記載できる。

fastcgi_index ディレクティブは,cgi (fcgi) のリクエスト URI がスラッシュで終わっている場合のデフォルトのファイルのファイル名である。location のコンテキストと,location コンテキスト中の if ディレクティブに記載できる。

fastcgi_param ディレクティブは,第一パラメーターに指定したい「パラメーター名」を書き,第2パラメーターに「値」を書く。今回の例では「SCRIPT_FILENAME」を指定している。 具体的には,FCGI は必ず「/usr/local/www/data」の下にあり,リクエスト URI が,例えば「/info/」なら,「/usr/local/www/data/info/index.cgi」となる(直前の「fcgi_index」と組み合わせている)。 リクエスト URI が「/cgi/hello.cgi」なら「/usr/local/www/data/cgi/hello.cgi」が「SCRIPT_FILENAME」に入れられる。httpserverlocation のコンテキストに記載できる。

赤い部分前回書いた設定ファイルの例と違う部分であり,「root」は,全仮想サーバーに共通で「/usr/local/www/data」に置くという指示である。

error_page ディレクティブは,エラー番号ごとに表示するファイルを指定している。省略するとデフォルトのエラーメッセージが web ページ上に表示されるが,nginx のデフォルトの場合にエラーページに「nginx」と出るので,それが出ないようにページを作っている。また,403 エラーの時には 404 と同じエラーページを表示するようにしている。httpserverlocation のコンテキストと,location コンテキスト中の if ディレクティブに記載できる。

 私の場合は,これで cgi を動かすことができた。特にトラブルはなかったように思う。

次回は https 化と Digest 認証について記載しよう。
FreeBSD で nginx に挑戦してみた(その4)に続く

0 件のコメント: