Apache2 MPM-ITK で WSGI を daemon モードで動かす

Apache2 MPM-ITK 環境下の情報が少なすぎて解決するのに丸一日かかってしまいました。もうこんな目に遭うのはご免なので記録します。前回の記事「Python CGI プログラマのための WSGI 移行記録」の続きから書きます。環境は同じく Apache2 MPM-ITK on Debian 9 Jessie です。


mod_wsgi には embedded mode と daemon mode があります。

embedded mode は呼び出される度にプロセスを起動して実行するのでほぼ CGI と同じ実行方式です。プロセス ID をしばらく観察していると、すぐには終了せずにしばらく残存してから終了するみたいです。CGI は都度終了するのでそこが違いでしょうか。

daemon mode は文字通り、デーモンとして起動したまま待機しています。プロセス ID を観察しているとずっと同じまま動作していることがわかります。

Apache2 の設定に AddHandler wsgi-script だけを設定すると embedded mode で実行されます。daemon mode で実行するには設定を加える必要があります。

Apache2 の VirtualHost に次の 2 行を加えます。

<VirtualHost *.443>
    ...
    WSGIDaemonProcess wsgi_app socket-user=USERNAME
    WSGIProcessGroup wsgi_app
    ...
</VirtualHost>

保存したら Apache2 の再読込を行います。

$ sudo systemctl reload apache2

これで完了です。気になる方は次のような WSGI スクリプトを実行してみて、PID が間を置いたリロードで PID が変化するかどうかを試してみてください。

# coding: utf-8

import os

def applicatoin(e, r):
    r('200 OK', [('Content-type', 'text/html; charset=utf-8')])
    yield '<p>PID: {}</p>'.format(os.getpid()).encode('utf-8')

MPM-ITK 環境では socket-user オプションを指定しないと次のようなエラーを吐きます。

503 Service Unavailable

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

このとき Apache2 の error ログには次のようなものが残ります。

[Thu Oct 11 15:04:38.059487 2018] [wsgi:error] [pid 15574] (13)Permission denied: [client 153.194.19.27:55658] mod_wsgi (pid=15574): Unable to connect to WSGI daemon process 'wsgi_app' on '/var/run/wsgi.15557.0.1.sock' as user with uid=1001.

これを見たら、パーミッションが不足していると思ってディレクトリのオーナーやパーミッションの変更ばかり試してハマりました。権限があるところもパーミッションが 777 のところを指定しても自体は解決しません。

socket-user ではなく user と group オプションもありますがこれまた何も解決してくれず、おかげで丸一日悩む羽目になりました。

以下はおまけ程度の内容です。

実際に embedded mode と daemon mode でどれくらいの差があるのかを、Chrome の開発者モードに表示されるページの読込時間で試してみました。上記の PID を表示するだけの WSGI スクリプトをひたすら再読込するだけで、単位は ms です。

embeddeddaemon
初回100100
直後3030
間欠10070

数回リロードして安定した数値を拾っているだけなのでかなり雑です。目安程度にしてください。直後というのは概ね数秒以内です。間欠は 10 秒以上間を空けています。

サーバを起動した最初の 1 回目は同じですが、それ以後は daemon mode の方が有利ですね。開発時ならともかく、運用時はまず再起動することはないので daemon mode を選ばない理由はありませんね。

複数ユーザへの対応

MPM-ITK を選んでいるのは複数ユーザのパーミッション分離を行うためで、Apache2 もユーザ別に設定ファイルを書いています。仮に foo さんと bar さんと hoge さんがいるとすると、設定ファイルは以下のようにしておけば、それぞれで WSGI スクリプトが daemon mode で動きます。

WSGIDaemonProcess 直後の名前は WSGIProcessGroup と揃えておけば好きな名前をつけて構いません。今回は WSGI であることがわかり、ユーザ別に分けたかったので wsgi_username の形にしました。

foo.conf

<VirtualHost *.443>
    ...
    ServerName foo.example.com
    ...
    WSGIDaemonProcess wsgi_foo socket-user=foo
    WSGIProcessGroup wsgi_foo
    ...
</VirtualHost>

bar.conf

<VirtualHost *.443>
    ...
    ServerName bar.example.com
    ...
    WSGIDaemonProcess wsgi_bar socket-user=bar
    WSGIProcessGroup wsgi_bar
    ...
</VirtualHost>

hoge.conf

<VirtualHost *.443>
    ...
    ServerName hoge.example.com
    ...
    WSGIDaemonProcess wsgi_hoge socket-user=hoge
    WSGIProcessGroup wsgi_hoge
    ...
</VirtualHost>

参考