Sassyブログ

埼玉県在住のシステムエンジニアです。基本的にはIT技術関連の内容を中心に発信していきます。たまにゲーム関連ネタも載せます

スポンサーリンク

Djangoの本番環境デプロイについて

概要

現在、自社プロダクトで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
・必要な環境変数は設定済み

※Daphneはアプリケーションサーバーです。Djangoのリソースを読み込んでプロセスを作成してくれます。

※最新のchannels-redisを使用する場合はredisをバージョンを5系以上でインストールしないとエラーが発生します。
 AmazonLinux2が提供するExtras Library内のredisは4系なので、手動でインストールするとかでないとできません。

※DB等はAWSのRDSで構築しています。
 別途psycopg2をインストールします。

※Nginx(Webサーバー)とDaphne(アプリケーションサーバー)との通信には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のドキュメントには載っていないのですが、私の場合以下の事象が発生していてうまく起動ができませんでした。

www.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の値を確認してみてください。