Windows Server の上で Django を動かしてほしい、と頼まれることがあります。
Linux と nginx で組めるならそのほうが早いのですが、社内の資産が IIS に寄っている現場では選択肢がありません。
そこで詰まりやすいのが、IIS と Python アプリをどうつなぐか、という一点。
答えは web.config に書く httpPlatform ハンドラーで、設定さえ通れば残りは普通の Django です。
なお、私の手元の検証環境は Linux なので IIS そのものは動かせません。
この記事は Microsoft 公式ドキュメントの記載に沿って、設定の組み立て方を整理したものです。
HttpPlatformHandler は何をしているのか
まず、IIS が Python を直接理解するわけではない、という前提を押さえておきます。
HttpPlatformHandler がやっているのは、別プロセスを起動して、受け取ったリクエストをそこへ丸ごと横流しすることの2点だけ。
公式の説明でも、役割は「HTTP リスナーのプロセス管理」と「管理しているプロセスへのリクエストのプロキシ」と書かれています。
つまり IIS は、Python が listen しているポートへ向けた薄いリバースプロキシとして振る舞うわけです。
対応バージョンは IIS 7.5 / 8 / 8.5 / 10 で、モジュール本体は IIS の公式サイトから x86 版と x64 版が配布されています。
wfastcgi ではなく httpPlatform を選ぶ理由
Python を IIS で動かす方法としては、FastCGI 経由の wfastcgi がもうひとつの選択肢です。
ただ Microsoft は、公式ドキュメントの中で wfastcgi はメンテナンスされていないので HttpPlatformHandler を推奨するとはっきり書きました。
新規に組むなら、素直に httpPlatform 側へ寄せておくほうが後々の面倒が減るでしょう。
web.config の最小構成
設定は、アプリのルートに置く web.config ひとつで完結します。
ハンドラー登録と httpPlatform セクションの2ブロック、というのが最小構成です。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="PythonHandler" path="*" verb="*"
modules="httpPlatformHandler" resourceType="Unspecified"/>
</handlers>
<httpPlatform processPath="C:\Python312\python.exe"
arguments="C:\app\run_server.py"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\python.log"
startupTimeLimit="60">
<environmentVariables>
<environmentVariable name="SERVER_PORT" value="%HTTP_PLATFORM_PORT%" />
</environmentVariables>
</httpPlatform>
</system.webServer>
</configuration>
ここで一番大事なのが %HTTP_PLATFORM_PORT% という変数。
HttpPlatformHandler が空きポートを勝手に決めて、この変数に入れて子プロセスへ渡してきます。
アプリ側は「決め打ちの 8000 番」ではなく、渡されたポートで listen しなければ疎通しません。
主な属性と既定値
- startupTimeLimit … 起動待ちの秒数。既定は 10
- startupRetryCount … 起動リトライ回数。既定は 10
- requestTimeout … 応答待ちの上限。既定は 00:02:00
- stdoutLogEnabled … 標準出力をファイルへ落とすか。既定は false
- processesPerApplication … 起動するプロセス数。既定は 1、最大は 100
既定の startupTimeLimit は10秒なので、マイグレーションや重い import が入る構成では足りません。
私の感覚では、まず60秒あたりまで伸ばしておいて、あとから詰めるほうが切り分けが楽になります。
なお stdoutLogFile は、v1.2 から「ドットで始まるパスはサイトのルート基準、それ以外は絶対パス」という扱いに変わりました。
Django 側でやっておく3つの準備
web.config が正しくても、Django 側の準備が抜けていると 500 と真っ白なページに悩まされます。
1. 起動スクリプトを用意する
runserver は開発用なので、本番では waitress のような WSGI サーバーを挟むのが定石。
Windows では gunicorn が使えないため、waitress が現実的な選択肢になるでしょう。
import os
from waitress import serve
from myproject.wsgi import application
port = int(os.environ.get("SERVER_PORT", "8000"))
serve(application, host="127.0.0.1", port=port)
環境変数の名前は web.config 側で決めた名前と揃えます。
%HTTP_PLATFORM_PORT% を直接読ませても構いませんが、いったん自前の名前へ写しておくとローカル実行時に上書きしやすくなるはず。
2. ALLOWED_HOSTS を通す
Django は、リクエストの Host ヘッダーが ALLOWED_HOSTS に無いと DisallowedHost で弾きます。
公式ドキュメントにも、配列が空のときだけ localhost と 127.0.0.1 が自動で許可されると明記されていました。
本番のドメインを1つでも足した時点で、ローカル用のホストは自動では通らなくなる点に注意。
ALLOWED_HOSTS = ["example.local", "127.0.0.1"]
3. 静的ファイルの配信先を決める
collectstatic した先を IIS の仮想ディレクトリで配るか、WhiteNoise でアプリ側から配るか。
どちらでも動きますが、httpPlatform はすべてのリクエストを丸ごとプロキシする設定になっているため、既定のままだと静的ファイルも Python まで届いてしまいます。
つまずきやすいポイント
ログを先に出せるようにする
stdoutLogEnabled="true" を入れておかないと、Python 側の例外がどこにも残りません。
IIS の 500 画面だけを眺めても原因にたどり着けないので、まずログの出口を作るのが最短ルートでした。
権限まわり
アプリプールの実行アカウントに、アプリのフォルダとログの出力先への書き込み権限が要ります。
ログファイルだけ生成されない、という症状のときは、たいていここが原因です。
プロセス数を増やすときの前提
processesPerApplication を増やすと、その数だけ Python プロセスが立ち上がります。
最大値は100ですが、SQLite のようにファイルロックが絡む構成では素直にスケールしません。
最後に
IIS と Django の組み合わせは情報が散らばりがちですが、押さえる場所は多くありません。
web.config でハンドラーを登録し、起動スクリプトに %HTTP_PLATFORM_PORT% を渡し、Django 側で ALLOWED_HOSTS と静的ファイルを面倒みる。
この3点が揃えば、あとは普段の Django と同じように扱えます。
逆に言えば、疎通しないときの原因もほぼこの3点のどれかなので、切り分けはそれほど怖くありません。
まずは stdout のログを出すところから始めてみてください。
疎通の切り分けでは、返ってきたステータスコードの意味を先に押さえておくと迷いません。
以上です。











コメントを残す