kivantium活動日記

プログラムを使っていろいろやります

Django ChennelsアプリをNginxとSupervisorでデプロイする

DjangoでWebsocketを使うときにはChannelsというライブラリがよく使われています。これまではHerokuにデプロイをしてきましたが、HerokuとChannelsの相性が良くないのかすぐに接続が切れてしまうので、これからはAWS上で開発しようと思いました。公式ドキュメントを読んでもデプロイ方法がよく分からなかったのでメモしておきます。

AWS LightsailでUbuntu 18.04のインスタンスを立てたとして、SSHで入ってからHello, world!するところまでを見ていきます。

ライブラリのインストール

Daphneの起動を楽にするためにvenvを使います。(参考: Djangoのインストール · Django Girls Tutorial

ssh ubuntu@xxx.xxx.xxx.xxx
sudo apt update
sudo apt install python3-venv
python3 -m venv env
source env/bin/activate

requirements.txtに以下を記述します。

django~=3.0.5
channels~=2.4.0

pipをアップデートしてから必要なライブラリをインストールします。

python -m pip install --upgrade pip
pip install -r requirements.txt

Hello, world!アプリの設定

Django プロジェクトを作成します。

django-admin startproject mysite
cd mysite

基本的にはChannels公式ドキュメントのInstallationに従って設定します。簡単のために本番環境と開発環境の設定の分離などは無視します。

mysite/settings.py を以下のように編集します。diffを示しています。

-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['*']  # 本当は適切なホストを指定するべきだが簡単のため全て許可
 
(略)

INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'channels',
 ]
 
+ASGI_APPLICATION = "mysite.routing.application"
+
 MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',

mysite/routing.py を以下の内容で作成します。

from channels.routing import ProtocolTypeRouter

application = ProtocolTypeRouter({
    # Empty for now (http->django views is added by default)
})

mysite/urls.py を以下の内容で作成します。

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

mysite/views.py を以下の内容で作成します。

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world!")

ここまで来たら python manage.py runserver を実行してエラーが出ないことだけ確認します。(サーバー上にあるのでこの時点ではブラウザで表示確認ができません)

NginxとSupervisorの設定

ここもChannels公式ドキュメントのDeployingに従って設定するだけなのですが、この通りにやっても動かなかったので以下のStackOverflowに従ってアレンジしました。

stackoverflow.com

まずはNginxとSupervisorをインストールします。

sudo apt install nginx supervisor

mysite/asgi.pyを以下の内容で作成します。

"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""

import os
import django
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
django.setup()
application = get_default_application()

/etc/supervisor/conf.d/asgi.confを以下の内容で作成します。

[fcgi-program:asgi]
# TCP socket used by Nginx backend upstream
socket=tcp://localhost:8000

# Directory where your site's project files are located
directory=/home/ubuntu/mysite

# Each process needs to have a separate socket file, so we use process_num
# Make sure to update "mysite.asgi" to match your project name
command=/home/ubuntu/env/bin/daphne --fd 0 --access-log - --proxy-headers mysite.asgi:application
# Number of processes to startup, roughly the number of CPUs you have
numprocs=4

# Give each process a unique name so they can be told apart
process_name=asgi%(process_num)d

# Automatically start and recover processes
autostart=true
autorestart=true

# Choose where you want your log to go
stdout_logfile=/var/log/asgi.log
redirect_stderr=true

設定を読み込みます。

sudo supervisorctl reread
sudo supervisorctl update

/etc/nginx/sites-available/defaultを以下のように編集します。

upstream channels-backend {
    server localhost:8000;
}
...
server {
    ...
    location / {
        try_files $uri @proxy_to_app;
    }
    ...
    location @proxy_to_app {
        proxy_pass http://channels-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;
    }
    ...
}

設定を読み込みます。

sudo service nginx reload

ブラウザのアドレス欄にこのサーバーのIPアドレスを入力すれば、Hello, world! と出力されたページを確認することができます。 次回はこのサーバーを使って簡単なWebsocketを使ったプログラムを書きます。

広告コーナー