Apache と mod_python で Django を動かす

revision-up-to:17812 (1.4)

Warning

mod_python のサポートは非推奨となっており Django 1.5 で取り除かれる予定 です。新しいデプロイ設定をするなら mod_wsgi または他の サポートされている サーバ を使うことを強くお勧めします。

Apachemod_python モジュールを使えば、本番環境で Django をデプロイで きます。ただし、この方法は、よりシンプルな mod_wsgi による運用 を使う方法に取 って代わられつつあります。

mod_python は mod_perl とよく似ていて (影響を受けていて)、 Apache の中に Python を埋め込み、サーバ起動時に Python のコードをメモリ上に読み込みます。 読み込まれたコードは Apache のプロセスが生きている間ずっと残るので、他のや りかたに比べて明確なパフォーマンスの向上につながります。

Django を mod_python で動かす場合、 Apache 2.x と mod_python 3.x が必要です。 また、 Apache を使う場合、 worker MPM ではなく prefork MPM を使うよう 勧めます。

See also

基本的な設定

Django を mod_python で動かすようにするには、まず Apache がインストールされ、 mod_python モジュールが有効になっていることを確かめます。

次に、 httpd.conf ファイルを編集し、以下のような設定を追加します:

<Location "/mysite/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonOption django.root /mysite
    PythonDebug On
</Location>

mysite.settings は、 Django プロジェクトの設定ファイルを指す Python import パスになるよう、自分の環境に合わせて置き換えて下さい。

この設定は、 Apache に 「/mysite/ の下の全ての URL は mod_python で処理し、 処理には Django mod_python ハンドラを使うように」命令します。また、 DJANGO_SETTINGS_MODULE を渡して、 mod_python にどこに設定ファイルがあるのか教えます。

mod_python は、サイトを /mysite/ プレフィクスの下で提供していることを自 動的に検出できないので、 PythonOption django.root ... を使ってハンドラ に通知してやる必要があります。この行に設定する値は、 <Location ...> の引数と一致していなければなりません。 django.root を設定すると、 Django はリクエストの URL から django.root に設定されたプレフィクスを除 去した上で URLConf のパターンマッチングを行います。このため、後でサイト を /mysite2 に動かした際に、 djnago.root だけを変更すればよくなりま す。

django.root を使う場合、必ず、プレフィクスを除去した後の URL がスラッシュ で開始するようにしてください。 そうでないと、先頭にスラッシュの入る URLConf の設定があった場合、パターンマッチングがうまくいきません。上の例では、 /mysite/admin//admin/ に変えたいので、先頭から除去すべきなのは /mysite です。従って、 django.root の値を /mysite にしているの です。この場合、 /mysite/ (末尾にスラッシュがついている) にしてしまうと、 エラーをひき起こすでしょう。

上の例では <Directory> ではなく <Location> ディレクティブを使って います。前者はファイルシステム上の場所を指定するのに使い、後者はウェブサイ トのURL 構造上の場所を指定するのに使います。従って、 <Directory> を使っ た指定は意味をなさないのです。

また、 Django プロジェクトがデフォルトの PYTHONPATH 上になければ、以下 のような設定を mod_python に教えておく必要があります:

<Location "/mysite/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonOption django.root /mysite
    PythonDebug On
    PythonPath "['/path/to/project'] + sys.path"
</Location>

PythonPath には、アプリケーションから import したいモジュールの各々の 親ディレクトリを入れなければなりません。また、 DJANGO_SETTINGS_MODULE の親ディレクトリも入れねばなりません。対話シェル を使う場合に Python パスを設定するのと同じです。何かモジュールを import す る際には、Python は必ず sys.path の各ディレクトリを順に調べ、その下から 該当モジュールを import しようと試み、 import に成功するまで探索を続けます。

ところで、Python のソースファイルのパーミッションが、 Apache のプロセスを駆 動しているユーザ (たいていのシステムでは apache とか httpd というユー ザ名がついている) から読み出せるように設定されているか確かめておいてください。

さて、分かりやすくするために例を挙げましょう。アプリケーションを /usr/local/django-apps/ の下に配置しているとします (例えば、 /usr/local/django-apps/weblog/ のようにです) 。そして、設定ファイルが /var/www/mysite/settings.py だったとします。上の例のように DJANGO_SETTINGS_MODULE を設定しているなら、 PythonPath は以下のように書かねばなりません:

PythonPath "['/usr/local/django-apps/', '/var/www'] + sys.path"

これで、 import weblogimport mysite.settings は両方とも正しく実 行できます。コード中で import blogroll していて、 blogrollweblog/ ディレクトリの下にあるのなら、 /usr/local/django-apps/weblog/PythonPath に加えねばなりません。 import したいモジュールの 親ディレクトリ を Python パスに入れねばならな いことに注意してください。

Note

Windows を使っているなら、パスの表記に注意してください。 Windows は通常 バックスラッシュをネイティブのパス区切り文字に使いますが、それでもスラッ シュを使ってパスを表記するよう進めます。 Apache はスラッシュをプラット フォームネイティブのパス区切り文字に変換する方法を知っているので、スラッ シュを使った方が、可搬性や視認性を持たせられるのです。 (それに、バックス ラッシュの二重エスケープのようなトリッキーな問題を回避できます)。

Windows システムでも、以下のパス表記を利用できます:

PythonPath "['c:/path/to/project'] + sys.path"

また、 PythonAutoReload Off のようなディレクティブを設定して、パフォー マンスを向上させられます。使えるオプションのリストは mod_python のドキュメント を参照してください。

注意しなければならないのは、実運用のためのサーバでは、 PythonDebug Off を設定すべきであるということです。 PythonDebugOn に設定されたま まだと、 mod_python に不具合が生じたときに、恰好の悪い (そして赤裸々な) Python トレースバックを表示してしまいます。

Apache を再起動しましょう。 /mysite/ やその下の URL へのリクエストが Django によって提供されているはずです。 Django の URLconf は /mysite/ を切り取りらず、完全な URL を渡すことに注意して下さい。

Django で作られたサイトを mod_python 上で運営している場合、 Python コードに 対して変更を加える度に Apache を再起動する必要があります。

ひとつの Apache に複数の Django をインストールする

ひとつの Apache に複数の Django をインストールするのは簡単です。 VirtualHost を使って、以下のようにするだけです:

NameVirtualHost *

<VirtualHost *>
    ServerName www.example.com
    # ...
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
</VirtualHost>

<VirtualHost *>
    ServerName www2.example.com
    # ...
    SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings
</VirtualHost>

一つの VirtualHost 設定の中に複数の Django をインストールしたい場合、 mod_python のキャッシュが Django の動作を台無しにしないように特に気を配る必 要があります。 PythonInterpreter ディレクティブで、 <Location> ディ レクティブごとに別のインタプリタが使われるよう設定してください:

<VirtualHost *>
    ServerName www.example.com
    # ...
    <Location "/something">
        SetEnv DJANGO_SETTINGS_MODULE mysite.settings
        PythonInterpreter mysite
    </Location>

    <Location "/otherthing">
        SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings
        PythonInterpreter othersite
    </Location>
</VirtualHost>

PythonInterpreter の値は、二つの Location ブロックの間で違う値にさ えなっていれば何でもかまいません。

mod_python 上で開発用サーバを動かす

開発用サーバに mod_python をえば、コードを変更する度にサーバを再起動するよ うなやんちゃは避けられます。 MaxRequestsPerChild 1 を Apache の httpd.conf ファイルに指定し、Apache にリクエストごとに全てをリロードさ せてください。とはいえ、これを実運用のサーバでやらないでください。さもない と、 Django ならではの恩恵が失われてしまいます。

あなたが print 文を散りばめてデバッグするタイプのプログラマなら、 stdout への出力は Apache のログには何も出力されず、 レスポンスエラーの原因になる ことさえある点に注意しましょう。

mod_python のセットアップでデバッグ情報を出力する必要があるなら、いくつかの方 法があります。次のように stderr に明示的に print することができます:

print >> sys.stderr, 'debug text'
sys.stderr.flush()

(stderr はバッファされるので、即座にデバッグ情報を表示されたいなら flush の呼び出しが必要なことに注意してください)

もっと簡潔なアプローチはアサーション (assertion) を使うことです:

assert False, 'debug text'

もう一つの方法は、ページのテンプレートにデバッグ情報を追加することです。

メディアファイルの提供

Django は、自分ではメディアファイルの提供を行わず、ユーザの選んだ Web サー バにその仕事を任せます。

メディアの提供には、別のウェブサーバ、すなわち Django を動かしていないサー バを使うよう勧めます。お勧めはいくつかあります:

とはいえ、Django を動作させているのと同じ Apache の VirtualHost でメディ アファイルや静的ファイルを提供せざるを得ない場合には、以下のようにして特定の 場所でだけ mod_python を切ります:

<Location "/media">
    SetHandler None
</Location>

Location ディレクティブの引数を、メディアファイルを置いている場所のルー ト URL に置き換えて下さい。 <LocationMatch> を使って、正規表現に一致さ せるようにもできます。

以下の例では、 Django をサイトのルートで設定し、 mediastatic サ ブディレクトリの下と、 .jpg.gif 、および .png で終わる URL だけでは明示的に Django を無効にしています:

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
</Location>

<Location "/media">
    SetHandler None
</Location>

<Location "/static">
    SetHandler None
</Location>

<LocationMatch "\.(jpg|gif|png)$">
    SetHandler None
</LocationMatch>

管理サイト用メディアファイルの提供

Django の開発サーバは自動的に管理サイトの静的ファイルを提供しますが、他 の構成では提供しません。管理サイトのメディアファイルを提供するには、 Apache なりその他のメディアサーバなりで設定を行う必要があります。

管理サイトのメディアファイルは Django 配布物の (django/contrib/admin/static/admin) ディレクトリ下に入っています。

django.contrib.staticfiles を使って admin のファイルを扱うことを 強く お勧めします。 (これは collectstatic 管理コマンドで STATIC_ROOT にファイルを集め、 STATIC_ROOT を Web サーバに STATIC_URL で提供させるよう に設定するということです) しかし、ここでは別の方法を 2 つ述べておきます:

  1. ドキュメントルートから admin メディアファイルへのシンボリックリンク を作ります。
  2. または、 admin メディアファイルをコピーして Apache のドキュメントルー トに入れます。

mod_python 上で “egg” パッケージを使う

Django を Python egg でインストールしたり、 Django から egg 化されたモジュー ルを使っている場合、いくつか追加の設定が必要です。プロジェクト中 (か、どこ か import できる場所) にファイルを作り、以下の内容を入れておきます:

import os
os.environ['PYTHON_EGG_CACHE'] = '/some/directory'

/some/directory は Apache httpd を動作させているプロセスが読み書き可能 なディレクトリにします。このディレクトリは、 egg のコードを展開する必要があ るときに使われます。

次に、 mod_python の PythonImport ディレクティブを使って、 mod_python が一 番最初にこのファイルを import するよう指定します。まず、前述の 上の節 で説明した PythonInterpreter ディレクティブを設定しておきます (複数の Django をイ ンストールしない場合でも必要です)。 次に、メインのサーバ設定 (LocationVirtualHost セクションの外) に、以下のように PythonImport 行を追加します:

PythonInterpreter my_django
PythonImport /path/to/my/project/file.py my_django

mod_python マニュアル で定義されている通り、モジュールの指定は絶対パス (またはドット区切りの import パス表記) で行えます。ここでは絶対パスを使って いますが、これは、 PythonImport を処理する時点では、プロジェクトへの Python パスを変更する操作がまだ完了していないからです。

エラー処理

Apache/mod_python を使っている場合、エラーは Django によって捕捉されます。 別の言い方をすれば、 エラーは Apache レベルまで伝播せず、 Apache の error_log には出力されないということです。

例外は Django のセットアップが全くうまくいっていない場合で、この場合には “Internal Server Error” がブラウザに表示され、 error_log ファイルには完 全な Python のトレースバックが出力されます。 (error_log トレースバック は複数のログエントリ行に展開されます (これは見苦しくて読みづらいのですが、 mod_python のやり方なので仕方ありません)。

セグメンテーション違反を起こす場合

Apache がセグメンテーション違反 (segumentation fault) を引き起こす場合、 2 種類の原因が考えられます。いずれも Django 自体とは関係のない原因です。

  1. Python コードが “pyexpat” モジュールを import している場合。 “pyexpat” は Apache に埋め込まれているバージョンと衝突する場合があり ます。詳しくは、 Expat Causing Apache Crash を参照してください。
  2. mod_python と mod_php を同じ Apache のインスタンスで動作させ、バック エンドに MySQL を使っている場合。PHP と Python MySQL バックエンドと のバージョン衝突で引き起こされる既知の問題かもしれません。これについ ては mod_python FAQ entry に詳しく書かれています。

mod_python を使った設定で問題を解決できない場合、まずは Django フレームワー クなしの素の mod_python サイトを動かすようにしてみるとよいでしょう。そうす れば、mod_python 固有の問題を簡単に切り分けられます。この手順については Getting mod_python Working で詳しく説明しています。

次のステップでは、テストコードを編集して、自分が使っている全ての Django 関 係のコード – ビュー、モデル、 URLconf, RSS の設定など – を import してみ ます。これらのモジュールを import する文をテストハンドラ関数に入れ、ブラウ ザでテストハンドラの URL にアクセスしてください。クラッシュするようなら、 Django コードの import が問題の原因だということになります。クラッシュしなく なるまで段階的に import するモジュールを減らしてゆき、原因のモジュールを特 定してください。原因のモジュールを調べて、中で import しているモジュールを 必要に応じて調べて下さい。

UnicodeEncodeError が出る場合

Django の国際化機能 (国際化とローカライズ 参照) を使っていて、ユーザにファイ ルアップロードをさせたいなら、 Apache が非 ASCII のファイルを受け入れるよ うに、 Apache を起動する環境変数をしかるべく設定してください。環境変数が正 しく設定されていないと、非 ASCII のファイル名を os.path() に渡したとき に UnicodeEncodeError 例外を引き起こすからです。

UnicodeEncodeError の問題を回避するには、 Apache を起動するときの環境変 数に、以下のような設定を入れておきます:

export LANG='en_US.UTF-8'
export LC_ALL='en_US.UTF-8'

正しい環境変数の設定方法や設定場所は、オペレーティングシステムごとのドキュ メントを参照してください。 Unix プラットフォームでは、 /etc/apache2/envvars を使うのが一般的です。設定を行ったら、 Apache を再 起動してください。