概要
現在、自社プロダクトでDjangoとDjangoChannelsを使用して双方向通信を行う小さなサービスを開発しています。
恐らくDjango単体での本番環境デプロイは色々記事はあるかと思いますが、今回はDjangoChannelsも使用しており、
この記事ではNginx+daphne+Djangoでやります。
特に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
・必要な環境変数は設定済み
※最新のchannels-redisを使用する場合はredisをバージョンを5系以上でインストールしないとエラーが発生します。
AmazonLinux2が提供するExtras Library内のredisは4系なので、手動でインストールするとかでないとできません。
※DB等はAWSのRDSで構築しています。
別途psycopg2をインストールします。
※NginxとDjangoとの通信にはUnixソケットを使用して通信しています。
※staticファイルの配信やsettingsファイルの開発環境と本番環境との分離についてなどは割愛します。
※各コマンド内に出ている「sampleapp」は本ブログ用に仮でつけたディレクトリ名です。ご自身の環境に合わせて適宜書き換えてください。
channels側アプリのredis設定等に関しては以下を参照してください。
channels.readthedocs.io
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のドキュメントには載っていないのですが、私の場合以下の事象が発生していてうまく起動ができませんでした。
なので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とDjangoアプリが疎通できるようにする
まずは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が使われているため、こちらを使用してアプリをバックグラウンドで起動できるようにしていきます。
以下に移動してサービスファイルを作成します。
$ /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のジャーナルログで確認するとわかります。
EnvironmentFileには「DJANGO_SETTINGS_MODULE="sampleapp.settings"」を定義して起動時にsettingsファイルの場所を教えてあげる必要があります。
※ExecStartのdaphneコマンドはフルパスで定義してください。
これで以下のコマンドを叩き起動します。
$ sudo systemctl start sampleapp
これで恐らく無事デーモン起動できるかとおもいます。
もしうまく起動できない場合は自分自身で作成したファイルの権限やサービスファイル(sampleapp.service)のUserやGroupの値を確認してみてください。