Sassyブログ

埼玉県在住のシステムエンジニアです。多ジャンルなブログですが、基本的にはIT関連の内容を中心に他のちょいちょい他ジャンルの記事も発信していきます。

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

概要

現在、自社プロダクトで DjangoDjangoChannels を使用して双方向通信を行う小さなサービスを開発しています。

恐らく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設定等に関しては以下を参照してください。

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のドキュメントには載っていないのですが、私の場合以下の事象が発生していてうまく起動ができませんでした。

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