読者です 読者をやめる 読者になる 読者になる

mod_wsgiでDjangoを動かすまでを淡々と記録する

Python Apache

kuma8さんが簡潔かつ的確なまとめを書いてくださっているが、とりあえず記録しておく。

今回試した環境は、

必要なライブラリをインストールする

Server版のCentOSを普通にインストールしたらApache2は入っているはずなので、この点は省略。今回はCentOS標準のPythonではなく、独自にインストールしたPythonを使うという前提で話を進めるので、python-develは必要ではない。

$ sudo yum install apr-devel apr-util-devel

mod_wsgiをビルドする

mod_wsgiの最新版2.3をダウンロードしてビルドする。使用するPythonのパスは、/usr/local/bin/python-2.6であるとする。

$ http://modwsgi.googlecode.com/files/mod_wsgi-2.3.tar.gz
$ tar xzf mod_wsgi-2.3.tar.gz
$ cd mod_wsgi-2.3
$ ./configure --prefix=/usr --with-python=/usr/local/bin/python-2.6

動作テスト

kuma8さんのコードをコピって動作確認。Pythonのパスを標準のものから変更したから、公式Wikiのドキュメントにしたがって、WSGIPythonHomeを設定する。

#/etc/httpd/conf.d/mod_wsgi_test.conf
LoadModule wsgi_module modules/mod_wsgi.so
WSGIPythonHome  /usr/local
WSGIScriptAlias /test /var/www/cgi-bin/hello.py

テスト用のスクリプト。

#!/usr/local/bin/python-2.6
def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ["It's works!"]

PYTHON_EGG_CACHEの問題

エラーログ(/var/log/httpd/error_log)を確認して、PYTHON_EGG_CACHEを変えろと言われた場合、設定ファイルにWSGIPythonEggsを追加して、Apacheの実行ユーザが書き込み可能なディレクトリを指定する。

#/etc/httpd/conf.d/mod_wsgi_test.conf
LoadModule wsgi_module modules/mod_wsgi.so
WSGIPythonEggs /tmp/apache/.pythons-eggs
WSGIPythonHome  /usr/local
WSGIScriptAlias /test /var/www/cgi-bin/hello.py

ただ、CentOSの場合apacheユーザのディレクトリが/var/wwwなので、

$ sudo mkdir /var/www/.python-eggs
$ sudo chown apache:apache /var/www/.python-eggs

とするのが、正しい対処法であろう。

Segmentation faultの問題

特に問題がありそうなライブラリを使用しているわけではないのに、エラーログに、

[Tue Nov 04 02:27:32 2008] [notice] child pid 11567 exit signal Segmentation fault (11)

のような出力が出て、mod_wsgiのサイトがInternal Server Errorになる場合は、そのサーバでmod_wsgiと同時にmod_pythonを動かしていないかを疑ってみる。

CentOSの場合、mod_pythonが自動的にインストールされている?

mod_wsgimod_pythonを同時に動かす必要あるとは思えないし、個人的には、Grahamが言うようにmod_pythonの時代は終わったと思うので、僕の場合は、単純にmod_pythonを削除して問題を解決した。

$ sudo yum remove mod_python

Python Pathを設定する

Python Pathが通っていないライブラリ・アプリケーションを使う場合、WSGIアプリケーション内で、

import sys
sys.path.insert(0, '/path/to/my/application')

のようにしてパスを指定する方法も動作するが、mod_wsgiの設定の中でPathを設定する方がスマートであろう。mod_wsgiPython Pathを設定するには、WSGIPythonPathを使う。

LoadModule wsgi_module modules/mod_wsgi.so
WSGIPythonHome  /usr/local
WSGIPythonPath  /path/to/my/application
WSGIScriptAlias /test /path/to/my/application.py

Djangoを動かす

WSGIScriptAlias内で指定したスクリプトで、DJANGO_SETTINGS_MODULEを設定し、django.core.handlers.wsig.WSGIHandlerをWSGIアプリケーションとして指定。mod_wsgi内で環境変数を設定することはできない?ようだ。

#!/usr/local/bin/python-2.6
import os

os.environ['DJANGO_SETTINGS_MODULE'] = 'yourapp.settings'
import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

See also http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango

nginxの背後で使う

上述のようにWSGIScriptAliasを/testに設定してあるまま、nginxの背後で使う場合は次のようにする。ホスト名はwsgi.example.comであり、apache(mod_wsgi)は127.0.0.1:7080で動作しているものとする。

http {
  proxy_set_header X-Real-IP        $remote_addr;
  proxy_set_header X-Forwarded-Host $http_host;
  proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
  proxy_set_header Host             $http_host;

  upstream wsgi {
    server 127.0.0.1:7080;
  }

  server {
    listen 80;
    server_name wsgi.example.com;

    location / {
       proxy_pass http://wsgi/test/
    }
  }
}

上記のようにして、wsgi.exmaple.comへのアクセスをすべて127.0.0.0:7080で動いているmod_wsgiに流す。本来ならば、/img, /js, /cssのようなディレクトリに配置される画像/CSS/JavaScript等の静的ファイルは上流のmod_wsgiサーバに流す必要はないが、ここでは省略している。

Daemon modeで使う

mod_wsgiには上記のようなコンフィギュレーションで使うembedモードのほかに、daemonモードというものがある。

先に設定ファイルの例を出すならば次の通り。

LoadModule wsgi_module modules/mod_wsgi.so

WSGISocketPrefix run/wsgi

<VirtualHost wsgi.example.com:*>
  ServerName  wsgi.example.com
  ServerAlias wsgi.example.com

  WSGIScriptAlias   / /path/to/your/wsgi/script.py
  WSGIDaemonProcess wsgi.example.com user=apache group=apache processes=2 threads=15 home=/ python-path=/path/to/your/wsgi python-eggs=/var/www/.python-eggs
  WSGIProcessGroup  wsgi.example.com
</VirtualHost>

Daemon modeで動かすにはちょっと苦労したので、少し詳しく書いておく。

ソケットの場所をWSGISocketPrefixで指定する。

何も指定しないと、ここに書かれているように、

(13)Permission denied: mod_wsgi (pid=26962): Unable to connect to WSGI daemon process '<process-name>' on '/etc/httpd/logs/wsgi.26957.0.1.sock' after multiple attempts. 

というエラーが発生する。解決するには、ドキュメントにあるとおり、WSGISocketPrefixでソケットの場所を指定する。

ドキュメントの通りrun/wsgiに指定すれば、WSGIプロセスのソケットは/var/run/wsgi.26658.0.1.sockのような場所に置かれ、上述のようなエラーは発生しなくなる。

WSGIDaemonProcessのuser, groupは明示的に設定しなくてはならない

WSGIDaemonProcessのuser/groupは設定しなくても、自動的にapacheのユーザ/グループになるのだと思っていたが、そうでもないみたい?

上記のコンフィギュレーションのようにuser=apache, group=apache、もしくは他にWSGIプロセスの実行ユーザ/グループがあるならば、それを指定する。

WSGIDaemonProcessで設定可能はオプションについては、ここを参照。

設定時にはLogLevelをdebug/infoにしよう

設定時には、Apacheの(/etc/httpd/conf/httpd.confの)LogLevelコンフィギュレーションをdebugかinfoにする。正常に動かない場合は、Apacheのエラーログ(/var/log/httpd/error_log)を確認しよう。こうしても問題の判別は依然として難しいが、mod_wsgi内でのPythonインタプリタ起動前の問題か、そうでないかの判別はできる。

多くの場合は、mod_wsgiのWikiを探せば、原因は判別できるが。