この記事は ハンズラボ AdventCalendar2022 2日目の記事です。
業務中に気になったDjangoの動作環境について書いてみました。
背景
先日、社内のシステム(Nginx+Django環境)への問い合わせがありシステムのエラーログに以下のものがでていました。
upstream timed out (110: Connection timed out) while reading response header from upstream,
このエラーでインターネット検索をしてみると fastcgi_read_timeout
であったり、proxy_read_timeout
を設定したほうが良いという Q&A が多く出てきました。
ハンズラボに入社するまで Python に触っていない私からすると、fastcgi? え? nginx じゃないの? そういえば WSGI というキーワードも見たけど、これらの関係が何がなんだかわかってないな。。ということで、関係性を調べてみました。
要約(調査経緯は知りたくない人向け)
Djangoアプリケーションは、以下のように動作している。(今回のシステム構成の場合)
したがって、今回の場合は、uwsgi_read_timeout
, uwsgi_send_timeout
を設定すれば良い。
ブラウザ <-- HTTP/HTTPS --> Nginx <-- socket(uwsgi protocol) --> uWSGI <----> Python(Django)
Python Webアプリケーションの歴史的背景
どのようにPythonのアプリケーションをWebサーバ上で動作させるようにしたのか?という歴史的背景については
古いですが、https://docs.python.org/2/howto/webservers.html を読みました。
読んだ内容を内容をまとめると、WSGI が定義されるまでの流れが理解できました。
- Webサーバは直接Pythonプラグラムは実行できないよ
- mod_python があるけど、プログラム修正しても再起動が必要だよ
- FastCGI, SCGI がでてきたよ。インタプリタをサーバに埋め込むのではなく、バックグラウンドプロセスを作り、Pythonプログラムの実行はバックグラウンドに委ねるよ。近年(記事が書かれた頃)はFastCGIは、直接使用されることはないよ。WSGIアプリケーションのデプロイのためだけに使われるよ
- mod_wsgi がでてきたよ。ただし、Apacheでしか動かないよ。
- mod_wsgiは、組み込みモードと、デーモンモードのいずれかで動作するよ
- WSGIは、Web Server Gateway Interfaceの略でPEP333(1)で定義されているよ
- WSGIサポートをしているフレームワークを選ぶことでWSGIに対応しているサーバで動作するよ
- ミドルウェアが使えるし、作れるよ。セッション管理、通信データの圧縮ミドルウェアもあるよ
エラー対応を・・・
ある程度、背景が理解できた段階でNginxの設定ファイルをみてみると・・・
location /xxx {
uwsgi_read_timeout 600;
uwsgi_send_timeout 600;
include uwsgi_params;
uwsgi_pass unix:/app/mdapi.sock;
}
uwsgi・・・、これまでにでてきてないキーワードがでてきました。
uWSGIとは
uwsgiがわかっていないので、公式サイトを読みました。
https://uwsgi-docs.readthedocs.io/en/latest/index.html
uWSGIについて以下のように理解しました。
- uWSGIはアプリケーション
- uwsgiはネイティブバイナリプロトコル
uWSGIがアプリケーションとして動作している理由としては、歴史的背景から察すると、FastCGIのところで書かれていたとおりにインタプリタを埋め込まない方式が使われており、WSGIを使うことで統一的なインターフェースを提供しているということかなと考えました。
したがって以下のように動作していると理解しました。
ブラウザ <-- HTTP/HTTPS --> Nginx <-- socket(uwsgi protocol) --> uWSGI <----> Python(Django)
こうなると、uWSGIのプロセスは1つではないだろうから、プロセス数の定義とかあるのではないか?という推測ができます。
https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html#deploying-django
[uwsgi]
socket = 127.0.0.1:3031
chdir = /home/foobar/myproject/
wsgi-file = myproject/wsgi.py
processes = 4
threads = 2
stats = 127.0.0.1:9191
推測したとおりに、設定ができるようです。
対応方法
かなり遠回りしてしまった気がしましたが、対応方法が判断できるようになったと思います。
エラーメッセージにあった(Nginxからみたときの)upstreamは、uWSGI。
今回のタイムアウトは、Nginx-uWSGI間のsocket通信間のタイムアウト。
したがって、Nginxで以下のパラメータを設定しました。
location /xxx {
uwsgi_read_timeout 3600;
uwsgi_send_timeout 3600;
include uwsgi_params;
uwsgi_pass unix:/app/mdapi.sock;
最後に
対応後は、同じ問い合わせがなくなったため、対応に問題はなかったと判断しています。
なぜそうなっているのか、どう対応すべきなのかを納得した上で対応できたので良かったと感じています。
-
PEP333は、WSGI v1, PEP3333がWSGI v1.0.1 ↩