Djangoのrunserverがつながらない?原因はWebpackでした

318, 2021-09-09

目次

Djangoのrunserverがつながらない?

私の開発環境ではDockerとDjangoを組み合わせて使っています。
Dockerのコンテナを走らせて、そのコンテナ内でDjangoの開発用サーバーを起動し、ホストのブラウザでlocalhostにアクセスして開発中のWebサイトを表示していました。

先日も、あるアプリの修正作業をしていて、それをコミットしてしばらくコンテナを放置していました。
2~3日経ってから再びアプリの修正をしようとブラウザでlocalhostにアクセスしました。
このlocalhostにはDjangoの開発用サーバーが繋がっているはずなので、ブラウザでアクセスしたら画面にはアプリの画面が表示されるはずでした。

しかし、ブラウザでアクセスしても一向に画面が表示されません。
Chromeブラウザでアクセスしていたのですが、タブのアイコンがくるくる回るだけでページは真っ白なままです。
つい最近まで何の問題もなく使えていたのに、いったいどうして?
私はこの問題に取り組まざるを得なくなりました。

やってやるぜ

原因はDocker?

私の開発用PCはWindowsを使っています。
DockerもWindows用のDockerです。

最初、私はDockerの不具合を疑いました。
そこでDockerを再起動したり、コンテナを削除して再度コンテナを走らせたりをしました。
さらにDockerのバージョンを更新して、最新の状態で使うようにもしました。

しかし、相変わらず画面がつながりません。
これはWindows版のDockerのバグかもしれない、という考えが頭によぎりましたが、それは最終終着駅だと思い直し、他にも色々試してみることにしました。

ポートが間違っている?

実行しているDjangoのコマンドは↓です。

python manage.py runserver 0.0.0.0:80

コンテナ内では↑のコマンドを実行していました。
コンテナはポートフォワーディングでホストのポート7777にコンテナのポート80が繋がっている状態です。
このフォワーディングでは↑のコマンドを実行した後に、ブラウザでhttp://localhost:7777にアクセスすれば繋がるはずです。
しかし、繋がらないのでポートが間違っているのかな? と疑うようになりました。

そこでrunserverに指定するポートを変えてみたりしましたが、結果は同じでした。

アドレスが間違っている?

localhostが間違っているのでは? と疑うようにもなった私は、アドレスを変えてみたりもしました。
Windowsのipconfigコマンドで表示されるIPアドレスをブラウザのアドレスバーに貼り付け、ポートを指定してアクセスしたりもしましたが、やはり結果は同じです。

さらには関係のないWSL2のIPアドレスも試してみました。
これはWSL2の環境ではip routeというコマンドを実行するとWSL2のIPアドレスが表示されるので、これをブラウザに貼り付けたりもしました。
しかし、当然ですが今回はWSL2は関係ないので結果は変わりません。

アドレスもポートも間違っていないなら、なにか他の原因があるということか・・・。
私の推理は一歩進みました。

ポートが使用中?

別のアプリがrunserverと同じポートを使っていて、接続がコリジョンしているのではないか?
という案が出ました。
そこでプロセスを確認して見ることにしました。

Windowsの端末で以下のコマンドを実行します。

netstat -oan

するとポートを使っているプロセスのプロセスIDが表示されます。
このコマンドでポート7777を使っているプロセスを調べて、このプロセスをkillすることにしました。
Windowsでプロセスをkillするには↓のコマンドを使います。

taskkill /pid プロセスID

このコマンドは管理者権限で立ち上げた端末で実行する必要があります。
↑のコマンドでプロセスをkillしてみましたが、やはり結果は同じでした。

Pythonの組み込みサーバーを起動する

Djangoとは別に、Pythonには組み込みサーバーがあります。
これは↓のコマンドで起動できます。

python -m http.server

オプション-mはモジュールをコマンドとして実行するオプションです。
↑の場合はhttp.serverというモジュールをコマンドとして実行しています。
さらにポートを80に指定するには↓のようにコマンドを打ちます。

python -m http.server 80

↑のコマンドを実行すると、コンテナのポート80に組み込みサーバーが公開されます。
この状態でブラウザを確認してみると、なんとちゃんと画面が表示されていました。

画面と言っても、出力されているのはアプリの画面ではなく、http.serverが配信する画面です。
これは現在のディレクトリを基準としたインデックスです。

Pythonの組み込みサーバーでは表示されるということは、どうやらDockerには問題がないということがわかりました。
原因はrunserver, つまりmanage.pyにありそうです。

プロジェクトが壊れている?

私はコンテナ内の開発中のプロジェクトとは別のディレクトリにdjango-admin startprojectで新しいDjangoプロジェクトを作成しました。
そしてそのプロジェクトに移動して、python manage.py runserver 0.0.0.0:80を実行してみました。
するとなんと、ブラウザにはちゃんとDjangoのページが表示されています。

これはプロジェクトを作成直後のDjangoのウェルカムページです。
この時の私にはこのページがまぶしく見えました。

この調査でわかったのは、どうやらDockerには問題はなく、Djangoのrunserverにも問題はない。
原因は、開発中のプロジェクトのmanage.pyにある、ということでした。

私は開発中のプロジェクトの破損を疑いました。
しかし、プロジェクトはGitで管理していますが、それらしいファイルの破損はありません。
まさかGitも壊れているのか? と疑うようにもなりましたが、とりあえずそれは置いておきました。

開発中のプロジェクトのmanage.pyは内部でプロジェクトのsettings.pyを読み込んでいます。
ということはこのsettings.pyに問題がある可能性が高い、ということで私はデバッグを行うことにしました。

settings.pyに書いてある独自の定義をコメントアウトしていき、作成したアプリのアンインストール(INSTALLED_APPSからの除外)も行いました。
このようなデバッグは、アプリが複雑に絡み合っているプロジェクトにおいてほとんど無意味です。
なぜならエラーが頻発するからです。
しかし、この時の私はこの方法以外に他にやることがありませんでした。

当然ながら、このようなデバッグを行ったところ、画面にはDjangoのエラー画面が表示されました。
エラー画面が・・・・・・。え? エラー画面?

あれぇ? 画面が表示されてるじゃん!

settings.pyの読み込みはOK?

なんと画面には、あれだけなにをしても何も表示されなかったのに、Djangoのエラー画面が表示されています。
私は思わず椅子から立ち上がり、そしてすぐに座り直しました。

ということはmanage.pysettings.pyの読み込みは行っているということになります。
このことからわかるのは、どうやらアプリ部分のコードがあやしいということになります。

私はデバッグを続けることにしました。
具体的には表示されているエラーを除外していきます。
このエラーは、settings.pyでアプリをアンインストールしたことに起因するエラーです。
つまり、アンインストールされているアプリが、プロジェクト内で参照されている(インポートなど)というものです。

このエラーをすべて取り除いていけば、最終的に動作する最小形態のプロジェクトを得られることになります。
私はデバッグを続け、エラーを取り除いていきました。

すると、urls.pyのデバッグで、あるルート情報をコメントアウトすると画面が表示され、コメントアウトを戻すと画面が再びビジーになることがわかりました。
どうやらこのルート情報からつながる処理にビジーの原因があることがわかりました。

ビジーの原因はだれ?

私はルート情報からビュー、テンプレートとデバッグを続けました。
するとあるテンプレートファイル内の行を取り除くとビジーが無くなることがわかりました。
そのテンプレートファイルの行は↓のようなものでした。

  {% render_bundle 'main' 'js' 'CORE' %}

render_bundleというタグはdjango-webpack-loaderで追加されるタグです。
開発中のプロジェクトはSPAも扱っていて、そのSPAの開発にWebpackを使っていました。
そのため↑のようにrender_bundleでWebpackが生成するファイルを描画していたのです。

この行を取り除くと、ビジーがなくなりました。
どうやら原因はこのrender_bundleにあることがわかりました。

この時、私はすぐにピンときました。
render_bundleがビジーになるのは、描画するファイルがないからだ!
私はすぐにそう判断し、Webpackのディレクトリに移動しました。

原因はWebpack関連だった

私はnpmとWebpackを使ってすぐにソースコードのビルドを行いました。
具体的には↓のコマンドを実行します。

npm run build

buildコマンドはpackage.jsonには↓のように書かれています。

{
  "scripts": {
    "build": "webpack",
  },
}

このコマンドを実行するとJavaScriptのソースコードがビルドされ、ディレクトリにファイルが配置されました。
するとビンゴ! 画面のビジーが無くなり懐かしのアプリの動作画面が表示されました。

おわりに

今回のDjangoのrunserverが繋がらない原因ですが、結果的にrunserverは繋がっていたが、コンテンツの描画でビジーになっていたという結果になりました。
画面が表示されていなかったのでてっきりrunserverの不具合を疑うことになりましたが、結果は違いました。

こういったエラーで一番最初に疑うべきなのは開発者のオリジナルのコンテンツの部分だと、あらためて思い知らされました。
私はDockerもDjangoもWSL2も疑ってしまった罪深き開発者です。
どうかお許しください。

ラーメン

タンメン