概要
現在、自社プロダクトで Django と DjangoChannels を使用して双方向通信を行う小さなサービスを開発しています。
恐らくDjango単体での本番環境デプロイは色々な記事があるかと思いますが、今回はNginx + Daphne + Django + DjangoChannels でやります。
特にsystemdでのデーモン化でハマりました。
Nginx + Daphne + Django も探せば色々出てくるので参考にしてサーバー側の設定を行えば起動できますが、
デーモン化できないとサービスとして運用できないのでそこまでをこの記事で説明していこうと思います。
前提条件
- サーバーはAmazonLinux2を使用しています。
またソフトウェアやライブラリは以下をインストールしています
- python 3.8
- django 3.1.2
- channles 3.0.2
- redis 6.0.9
- channels-redis 3.2.0
- daphne 3.0.1(ダフネと読むみたい)
- Nignx
補足事項
※1 必要な環境変数は設定済み ※2 Daphneはアプリケーションサーバーです。Djangoのリソースを読み込んでプロセスを作成してくれます。 ※3 最新のchannels-redisを使用する場合はredisをバージョンを5系以上でインストールしないとエラーが発生します。 AmazonLinux2が提供するExtras Library内のredisは4系なので、手動でインストールするとかでないとできません。 ※4 DB等はAWSのRDSで構築しています。 別途psycopg2をインストールします。 ※5 Nginx(Webサーバー)とDaphne(アプリケーションサーバー)との通信にはUnixソケットを使用して通信しています。 ※6 staticファイルの配信やsettingsファイルの開発環境と本番環境との分離についてなどは割愛します。 ※7 各コマンド内に出ている「sampleapp」は本ブログ用に仮でつけたディレクトリ名です。ご自身の環境に合わせて適宜書き換えてください。
channels側アプリのredis設定等に関しては以下を参照してください。
DaphneでDjangoアプリを起動できるようにする
まずは試しでサーバーのポートを解放して起動したアプリケーションに対して直接アクセスできるかを確認します。
ここでは8000ポートを解放しました。
アプリケーションを配置したフォルダまで移動します。
$ cd /var/sampleapp
アプリケーションのルートディレクトリで以下のコマンドを叩きます。
$ daphne -b 0.0.0.0 sampleapp.asgi:application
※ sampleapp.asgi:application
は、djangoのルートディレクトリにあるプロジェクト名と同名のフォルダ内に作成した asgi.py
を指定しています。
※ -b
オプションにはホストを指定します。
※ 外部からアクセスできるようにするため、 0.0.0.0
を指定します。
多分Django Channelsのドキュメントには載っていないのですが、私の場合以下の事象が発生していてうまく起動ができませんでした。
https://www.gitmemory.com/issue/django/channels/1564/722354397www.gitmemory.com
なので asgi.py
の先頭で django.setup()
を呼び出しています。
import django django.setup() import os from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter, URLRouter from django.core.asgi import get_asgi_application import hogehoge.routing os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sampleapp.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter( hogehoge.routing.websocket_urlpatterns ) ), })
まずはこれでアプリケーションと疎通できることを確認します。
Nginx(Webサーバー)とDaphne(アプリケーションサーバー)が疎通できるようにする
まずは nginx.conf
の設定です。
※重要な箇所しか記載していません。
WebSocket通信するため、必ずブラウザから渡ってくる Upgrade
Connection
ヘッダーをDjangoアプリに渡すようにします。
upstream
ディレクティブに記載している backend
はキー名のようなものであり、upstream
ディレクティブで設定した定義を server {・・・}
内で使用しています。
http { ・・・省略 upstream backend { server unix:/var/run/sampleapp/daphne.sock fail_timeout=0; } server { ・・・省略 location / { try_files $uri @proxy_to_app; } location @proxy_to_app { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } ・・・省略 } }
Unixソケットで通信を行うため、以下に移動し sampleapp.socket
というファイルを作成します。
$ cd /etc/systemd/system $ sudo mkdir sampleapp.socket
以下 /var/run/sampleapp/daphne.sock
は起動時に作成されるファイルなので自分では作成しません。
[Unit] Description=daphne socket [Socket] ListenStream=/var/run/sampleapp/daphne.sock User=www-data [Install] WantedBy=sockets.target
unixソケットを使用するようにしたため起動時のコマンドが変わります 。
$ daphne -u /var/run/sampleapp/daphne.sock -b 0.0.0.0 hirameki.asgi:application
-b
オプションで 0.0.0.0
を付けてますが、この状態で不要なのか必要なのかは実験できてません、要らなさそうな気がします。。。
Djangoアプリをデーモン起動できるようにする
デーモン化には Systemd を使用します。
AmazonLinux2では起動処理の仕組みとしてSystemdが使われているため、こちらを使用してアプリをバックグラウンドで起動できるようにしていきます。
以下に移動してサービスファイルを作成します。
$ cd /etc/systemd/system $ sudo mkdir sampleapp.service
※上の章で作成した sampleapp.socket
ファイルと同階層に作成します。
sampleapp.service
内の定義です。
[Unit] Description=Daphne Service Requires=sampleapp.socket After=network.target [Service] Type=simple User=ec2-user Group=root WorkingDirectory=/var/sampleapp EnvironmentFile=/var/env/sampleapp ExecStart=/home/ec2-user/sampleapp/bin/daphne -u /var/run/sampleapp/daphne.sock -b 0.0.0.0 -p 8000 sampleapp.asgi:application Restart=always [Install] WantedBy=multi-user.target
EnvironmentFile
キーは必要です。
StackOverFlowで同様にsystemdで起動できない記事を見てみると EnvironmentFile
がある場合とない場合に分かれていたんですが、
systemdからの起動時に EnvironmentFile
キーで環境変数ファイルを渡さないとsystemdとしては起動できたけどアプリケーション内でエラーが発生しており、アクセスしてもNginxのエラー画面が表示されてしまっていました。
これが sudo systemctl status sampleapp
コマンドで表示されるメッセージからはわからないので、systemdのジャーナルログを確認する必要があります。
$ journalctl
systemdで絞り込むには
$ journalctl -u 'systemd*'
を使ってください。
EnvironmentFile
には DJANGO_SETTINGS_MODULE="sampleapp.settings"
を定義して起動時にsettingsファイルの場所を教えてあげる必要があります。
ExecStart
のdaphneコマンドはフルパスで定義してください。
これで以下のコマンドを叩き起動します。
$ sudo systemctl start sampleapp
これで恐らく無事デーモン起動できるかとおもいます。
もしうまく起動できない場合は自分自身で作成したファイルの権限やサービスファイル(sampleapp.service)の User
や Group
の値を確認してみてください。