はじめに
ちょっとしたお仕事で Django Channels を uWSGI 上で動かそうとしたら、色々と uWSGI 以外のプロセスも起動していく必要がありました。
ぐぐっても 開発環境や使い方の説明はあるものの、デプロイ時の How to があまり見つからなかったので備忘的に残しておきます。
Django Channels とは?
Django Channels の使い方を説明する記事では無いので、使い方を知りたいひとは、次のページをどうぞ。
Python の WebSocket ライブラリを選定してて、このページに来ちゃった人は次のページも参考にどうぞ。(スケールしないよ、と言う話)
何が問題か?
前述のページを見ると runserver で普通に動いてるじゃん?と思われますが、本番環境用に uWSGI を使うと uWSGI 上で起動した Django アプリだけでは WebSocket 通信が行えません。
Channel Layer と Interface Server は Django の組み込み Web サーバ上でのみロードされ、機能するように作られるためです。 uWSGI を使うときは当然、 manage.py runserver なんて叩かないし、エントリーポイントは wsgi ファイルですよね。
どうやって解決するか
uWSGI と一緒に別のプロセスで Channel Layer と Interface Server が起動してあげればよいです。
Django Channels を利用したアプリが既にあったとしてもいくつか注意が必要です。
Channel Layer の変更
Channel Layer のバックエンドに In-memory を使っていると別のプロセスで動作する uWSGI と連動できなくなります。Redis バックエンドに変更しましょう。
$ pip install asgi-redis \ msgpack-python \ redis
そして、Django Settings の設定を変更します。
CHANNEL_LAYERS = { "default": { "BACKEND": "asgi_redis.RedisChannelLayer", "CONFIG": { "hosts": [("redis://127.0.0.1:6379/0")], }, "ROUTING": "websocket.routing.channel_routing" }, }
Django Session に Redis を使っている場合は DB 番号に気をつけてください。
asgi.py ファイルの準備
Channel Layer のサーバプロセスに読み込ませる asgi.py ファイルを用意します。
import os from channels.asgi import get_channel_layer os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") channel_layer = get_channel_layer()
Channel Layer を起動する時に Django Settings ファイルも環境に合わせたものを読むので注意してください。
Channel Layer (daphne) と Interface Server (manage.py runworker)
さあ、いよいよ起動です。Channel Layer として daphne と、Interface Server として manage.py runworker の2つのプロセスを起動します。
自分の管理サーバでは Ubuntu 16.04 になって、uWSGI も systemd にぶら下げましたが、へたれなので supervisor を導入します。( supervisor 導入手順は省略して、設定のみ晒します。)
[supervisord] minfds = 4096 [program:workerserver] command=/usr/local/bin/python /home/user/git/your-app/manage.py runworker -v1 --pythonpath /home/user/git/your-app --settings app.procution user=user autorestart=true stdout_logfile=/var/log/supervisor/workerserver.log stdout_logfile_maxbytes=1MB stdout_logfile_backups=10 redirect_stderr=true [program:daphne] command={{ virtualenv_path }}/bin/daphne --root-path /home/user/git/your-app app.asgi:channel_layer directory=/home/user/git/your-app environment=DJANGO_SETTINGS_MODULE="app.production" user=user autorestart=true stdout_logfile=/var/log/supervisor/daphne.log stdout_logfile_maxbytes=1MB stdout_logfile_backups=10 redirect_stderr=true
workerserver と daphne として supervisor にぶら下げてみました。
まとめ
振り返りになりますが、何が必要か上げていくと次のようになります。
- Channel Layer を in-memory から Redis に変更する
- Channel Layer (daphne) のサーバプロセスが使う asgi.py ファイルを用意する
- 本番環境で Redis を起動しておく
- supervisor を導入する(オプション、systemd でがんばっても良い)
- supervisor に Channel Layer の daphne と Interface Server の manage.py runworker をぶら下げる
以上のことで、uWSGI で Django Channels 導入まで行けるはず。
Django Channels は SwampDragon や Tornado は開発時に不要で楽という触れ込みだけど、本番環境では uWSGI とは別のプロセスを起動する羽目になり、何かモヤモヤする結果となりました。でも、ちゃんと動くからご安心を!