DjangoでgunicornとNginxを使う(Nginxその3)

VPSサーバーのDjangoプロジェクトを、まずNginxだけで公開します。次にgunicornも連携させます。なお、NginxとDjango(gunicorn)はソケットではなく、httpで接続します。STATIC_URL等の意味も説明します。

実行環境はConoHaのVPSサーバーを用い、OSはUbuntu20.04 LTSです。

前の記事<<     >>次の記事

1.Nginxの設定の概要

この記事は、以下の記事の続編です。Nginxのインストールは完了し、Nginxで静的ファイルが表示できる状態を前提としています。

Nginxでまず静的ファイルのみ表示してみる(Nginxその2)

なお、上の記事で設定した設定ファイルは以下の通りです。ディレクトリ「/etc/nginx/sites-available/」の中にserver0、dj_testという2つの設定ファイルを用意しました。

#設定ファイル名:server0
server {
    listen 80 default_server;
    server_name _;  #すべてのリクエストに対応する。
    return 444;  #ステータスコード444を返す。
}
#設定ファイル名:dj_test
server {
	listen 80;  #ポート番号の指定http
	server_name ***.***.***.***;  #IPアドレス(ドメイン)の指定

        #ドキュメントルートの指定
(静的ファイル、メディアファイルの保存先)
	root /usr/share/nginx/html;

	# index.html等はファイル名の指定なしで実行
	index index.html index.htm index.nginx-debian.html;
    
	location /static {
                #.htmlは省略できる
		try_files $uri $uri/ $uri.html =404;
	}

	location /media {

	}

    #以下、djangoにデータを引き渡す設定です。前記事ではここの説明はしていません。
    location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X_Forwarded-Proto $scheme;

            proxy_pass http://127.0.0.1:8000;
        }
}

2.VPSサーバーの構成の概要

VPSサーバー構成の最終目標は下図のとおりです。クライアントとNginx、NginxとDjangoはそれぞれhttpで接続します。

なお、クライアントとNginxの接続は最終的にhttpではなくhttpsに変更することになりますが、NginxとDjngo(gunicorn)の接続は速度を優先してhttpのまま変更しません。httpsは暗号化され安全性の高い接続ですが、暗号化しないhttpの方が高速です。

NginxとDjango(gunicorn)の接続はサーバー内部でのデータのやり取りであり、暗号化する必要がありません。

01_Nginx_gunicorn_Django

3.httpによる接続とソケットによる接続

gunicornを介してNginxとgunicornを接続する場合には、httpによる接続の他にソケットにより接続する方法があります。サイト上の情報はソケットによる接続の方が多いようです。

2つの方法を比較した情報は少ないですが、以下の参考記事ではソケットの方が速いがhttpの方が構成変更の自由度が高いと解説されています。

NginxとGunicornの接続をソケットからHTTPに変更した

私の記事では設定の簡便さと構成変更の自由度を優先してhttp接続による設定方法を紹介したいと思います。

4.Djangoへの処理の引き渡し

httpで接続する方法は、gunicornなしでもNginxからDjangoに処理を引き渡すことが可能です。まずは、gunicornを介さずにNginxだけでDjangoのプロジェクトを公開したいと思います。

Djangoの設定では欲張らずに一つ一つ設定を固めていくのがコツです。

さて、Nginxの設定ファイル「/etc/nginx/sites-available/dj_test」の中で、NginxからDjangoにデータを渡す設定部分は以下の通りです。

IPアドレス「127.0.0.1」はお馴染みの「localhost」のことであり、いずれも自分自身をあらわします。最後の行は、クライアントからのリクエストをhttp接続によりサーバー自身の8000番ポートに渡すことを意味しています。サーバー内のデータのやりとりなのでファイアフォール無効化の手続きは不要です。

server {
    location / {
            proxy_set_header Host $http_host;  #ホスト名
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #送信元アドレス
            proxy_set_header X_Forwarded-Proto $scheme;  #URLスキーム

            proxy_pass http://127.0.0.1:8000;
        }
}

また、接続先だけではなくホスト名($http_host)、送信元アドレス($proxy_add_x_forwarded_for)、URLスキーム($scheme)などの情報も渡す必要があります。$http_host、$proxy_add_x_forwarded_for、$schemeはそれ自身が意味を持った変数名なので、このまま記述します。

つまり、あまり深く考えず、このままコピペするだけでO.K.です。

5.Djangoのsettings.pyの設定

Djangoの設定は、Djangoプロジェクトの中にあるsetings.pyで行います。もっとも重要な設定は「DEBUG」をFalseに書き直すことです。「DEBUG」がTrueではデバッグ用の画面が表示されてしまい、サーバー内の細かい設定が全て第3者に公開されてしまいます。

なお、DEBUGをFalseにした場合は、ALLOWED_HOSTSを指定し、許可するドメイン(IPアドレス)を指定する必要があります。つまり、ALLOWED_HOSTS = []のままではエラーになります。

ALLOWED_HOSTSの指定で必須なのはVPSサーバーのドメイン(IPアドレス)です。VPSサーバーのドメイン(IPアドレス)を指定することで、Djangoのプロジェクトを公開することができます。

'localhost'と'127.0.0.1'は、それぞれブラウザから「http://localhost:8000/」や「http://127.0.0.1:8000/」にアクセスしたい場合に必要に応じて追加します。なお、'localhost'の指定だけでは「http://127.0.0.1:8000/」にアクセスできません。

#デフォルト → DEBUG = True
DEBUG = False  

#デフォルト → ALLOWED_HOSTS = [] 
ALLOWED_HOSTS = ['vpsサーバーのドメイン(IPアドレス)', 'localhost', '127.0.0.1']  

ALLOWED_HOSTSの設定では、例えば以下のように設定すれば、第3者に公開することなく「http://localhost:8000」により、Djangoプロジェクトの内容を見ることができます。つまり、公開前に最終的な確認を行うことができます。

DEBUG = False  
ALLOWED_HOSTS = ['localhost']  

注意しなければならないのは以下の記述です。localhostにしか許可していないので、DEBUG = Trueにしても大丈夫なように思えますが、第3者がVPSのドメインでアクセスしたときに、アクセスできない理由をデバッグ情報として表示してしまいます。サーバーの詳細な情報が開示される可能性があるので、注意が必要です。やはり、Trueの設定は危険です。

DEBUG = True 
ALLOWED_HOSTS = ['localhost']  

また、例えば上記の設定において、Nginxが起動せずに「python manage.py runserver」でDjangoを実行した場合は、第3者のサーバードメインへのアクセスに対して「申し訳ございません。このページに到達できません」と表示されるだけです。しかし、NginxをWebサーバーとしてDjangoのプロジェクトを公開したときはサーバーの詳細デバッグ情報が開示されてしまいます。

このようにDjangoだけのときの表示で安心していると、思わぬ形で情報が漏れてしまう可能性があります。

つまり、VPSサーバー上でDjangoのプロジェクトを作成したら、まず最初にsettings.pyのDEBUGをFaulseに設定しなおすことを習慣にした方がよいと思います。

6.Djangoのプロジェクト作成から公開まで

settings.pyのDEBUGとALLOWED_HOSTSの設定が終了すると、公開に必要な設定は終了です。他にも細かい設定はありますがあとで説明します。ひとまず「hello! hello!」と表示するだけの簡単なプロジェクトを作成し公開してみましょう。

ここで、私が失敗したのは、おなじみのロケットの絵を表示しようとしたことです。ロケットの絵はsettings.pyのDEBUGの設定がFaulseの場合は表示されません。

また。Djangoのプロジェクトに自作のアプリケーションが追加されている状態では、DEBUGがTrueであっても表示されません。

そこで、少し面倒ですが「Django-admin startproject ~」で作ったデフォルト状態のプロジェクトを修正し、簡単なアプリケーションを作成した上でDjangoプロジェクトを公開したいと思います。

手順は以下の通りです。まず、仮想環境でDjangoを実行している場合にはターミナルを仮想環境に切り替えます。次にtest_appという名称のプロジェクトを作成し、helloというアプリケーションを作成します。

$ . 仮想環境名/bin/activate  #仮想環境へ切り替え(ubuntu)
$ django-admin startproject test_app
  #プロジェクトの作成
$ cd test_app
$ python manage.py startapp hello  #アプリケーションの作成

次にtest_app/test_app/settings.pyのDEBUGとALLOWED_HOSTSの設定を書き換えます。

DEBUG = True 
ALLOWED_HOSTS = ['vpsサーバーのドメイン', 'localhost']  

Djangoプロジェクトの/test_app/hello/views.pyを以下のコードに書き換えます。

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return HttpResponse('Hello! Hello!')

/test_app/test_app/urls.pyを以下のコードに書き換えます。これで、Djangoの自作プロジェクトは完成です。

from django.contrib import admin
from django.urls import path
import hello.views as hello

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', hello.index),
]

ターミナルでNginxを起動し、Djangoのプロジェクトを起動します。

$ sudo systemctl start nginx.service  #Nginxの起動
$ python3 manage.py runserver

ブラウザでDjangoのプロジェクトにアクセスします。

http://ドメイン名/hello/

すると、Djangoのプロジェクトが表示されます。

02_Django_test_app

7.STATIC_ROOT、STATIC_URL、MEDIA_ROOTの設定

settings.pyでは、STATIC_ROOT、STATIC_URL、MEDIA_ROOTにより、静的ファイル、メディアファイルの設定を行います。STATIC_URLはディフォルトで設定されていますが、STATIC_ROOT、MEDIA_ROOTは追加します。

STATIC_URL = '/static/'
STATIC_ROOT = '/usr/share/nginx/html/static'
MEDIA_ROOT = '/usr/share/nginx/html/media'

STATIC_ROOT、MEDIA_ROOTにおいて絶対パスで指定する場合には、一番最初の「/」を忘れないようにしましょう。

(1) STATIC_ROOT

STATIC_ROOTの設定では、Nginxの設定ファイルの「location /static { }」で指定したディレクトリと同じディレクトリを指定します。

自分のPC等で作成中の公開前のDjangoプロジェクトにおいて、staticファイルはプロジェクト内のディレクトリstaticに保存すると思います。

しかし、静的ファイルをNginxからアクセスして高速化を図る場合は、Djangoの静的ファイルはプロジェクト外の「STATIC_ROOT」で指定したディレクトリにまとめます。そして、Nginxからは「STATIC_ROOT」のディレクトリにアクセスします。

つまり、作成したプロジェクトはプロジェクト内の静的ファイルを「STATIC_ROOT」のディレクトリに移動(複写)する必要があるのです。

プロジェクト外のディレクトリとは、具体的にはNginxの設定ファイルの「location /static { }」に関連づけられたディレクトリです。以下の例では「/usr/share/nginx/html/static/」です。

server {
	root /usr/share/nginx/html;
	location /static {
	}
}

この静的ファイルの複写を自動で行ってくれるのがcollectstaticコマンドであり、Djangoのプロジェクトの中の静的ファイルを、STATIC_ROOTで指定したディレクトリに複写します。

具体的な手順は、以下の通りターミナル上で仮想環境に切り替え、対象とするDjangoプロジェクトのmanage.pyが存在するディレクトリに移動し、collectstaticコマンドを実行します。

$ . 仮想環境名/bin/activate  #仮想環境へ切り替え(ubuntu)
$ cd test_app
$ python manage.py collectstatic

なお、「python manage.py collectstatic」の実行で、以下のように聞かれたときに、「y」と答えるのではなく、「yes」と答える必要があります。つい、うっかりしますので注意しましょう。

Type 'yes' to continue, or 'no' to cancel:

(2) STATIC_URL

Djangoでテンプレート(拡張子が.htmlのファイル)内で、以下のような記述をみたことはないでしょうか。「{% static 'hello/css/style.css' %}」のstaticは静的なファイルであることを示し、「'hello/css/style.css'」のルートがどこであるかは具体的な記述がありません。

    <link rel = "stylesheet" type = "text/css"
        href = "{% static 'hello/css/style.css' %}" />

実は「'hello/css/style.css'」のルートの位置を指定するのが「STATIC_URL」なのです。

STATIC_URLは、以下の1行目ようにURLを直接指定する書式も可能ですが、「http://ドメイン名」の部分は省略し、2行目のように記述することができます。

つまり、上のコードはSTATIC_URLで静的ファイルのルートURLが指定されているからこそ静的ファイルにアクセスできるのです。

STATIC_URL = 'http://ドメイン名/static/'
STATIC_URL = '/static/'

「http://ドメイン名/static/」にアクセスした場合には、Nginxの設定では「location /static { }」の設定が適用されるので、ディレクトリ「/usr/share/nginx/html/static/」のファイルにアクセスできます。

したがって、「STATIC_URL = '/static/'」のstaticと「location /static { }」のstaticは同じ名前にする必要があります。

server {
	root /usr/share/nginx/html;
	location /static {
	}
}

なお、デフォルトでは「STATIC_URL = '/static/'」となっています。つまり、「location /static { }」で設定した「/usr/share/nginx/html/static/」内の静的ファイルを検索され、Djangoプロジェクト内の静的ファイルは検索されません。

ですから「python manage.py collectstatic」を実行しなければ、CSSファイルなどの静的ファイルは有効になりません。

(3) MEDIA_ROOT

MEDIA_ROOTは、例えばurls.pyの中で「settings.MEDIA_ROOT」といった表現によってアップロードファイルを扱うディレクトリを指定できます。

MEDIA_ROOTの詳しい説明は行いませんが、STATIC_ROOTと同様にMEDIA_ROOTの設定では、Nginxの設定ファイルの「location /media { }」で指定したディレクトリと同じディレクトリをパスで指定します。

8.gunicornの設定

いよいよ、以下の最終的な構成を完成させるためにgunicornの設定を行います。新たな設定ファイルは作らないので、すぐに設定は終了します。

01_Nginx_gunicorn_Django

Nginxはubuntuのaptでインストールしましたが、gunicornはpythonのpipでインストールします。従って、まず、インストールするpythonの仮想環境に切り替える必要があります。

$ . 仮想環境名/bin/activate  #仮想環境へ切り替え
$ python3 -m pip install -U pip  #pipのアップデート
$ python3 -m pip install gunicorn  #gunicornのインストール

次にgunicornを起動し、NginxとDjangoをバインドします。

さて、Nginxは設定ファイルの「proxy_pass http://127.0.0.1:8000;」により、クライアントからのリクエストを127.0.0.1:8000に送信するように設定しています。gunicornがないときにはDjangoがこの信号を受けていました。

server {
    location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X_Forwarded-Proto $scheme;

            proxy_pass http://127.0.0.1:8000;
        }
}

gunicornを使う場合には、この信号をDjangoではなくgunicornが受ける必要があります。そこで、gunicorn起動時の引数の設定により、gunicornとDjangoの127.0.0.1:8000をバインドし、Nginxからの信号をgunicornが受信します。

なお、この時Djangoは「python3 manage.py runserver」により起動する必要はありません。

cd /home/user_name/py1/test_app  #test_appのmanage.pyがあるディレクトリに移動
gunicorn --bind 127.0.0.1:8000 test_app.wsgi -D

また、上記コマンドの「test_app.wsgi」は、Djangoプロジェクトのtest_app/test_app/の中にあるwsgi.pyのことですが、test_app/test_app/に移動して「gunicorn --bind 127.0.0.1:8000 wsgi -D」と実行してもダメでした。

したがって、必ずtest_appのmanage.pyがあるディレクトリに移動し、そこで「gunicorn --bind 127.0.0.1:8000 test_app.wsgi -D」を実行する必要があるようです。

「-D」はデーモンモードでgunicornを実行するコマンドであり、バックグラウンドで黙々と働いてもらう指定です。また、127.0.0.1:8000の部分はlocalhost:8000でも構いません。

gunicorn --bind localhost:8000 test_app.wsgi -D

以上でgunicornの起動・設定が終了しました。ブラウザで以下のurlにアクセスすると、プロジェクトtest_appのhelloアプリの画面が出力されます。

http://ドメイン名/hello/
02_Django_test_app

なお、前述の通りgunicornを起動した状態ではDjangoは起動する必要はありません。

試しにgunicornを起動した状態でVS Codeのターミナルから「python manage.py runserver」を実行してみましたが、「Error: That port is already in use.」とエラーが表示されて起動しませんでした。

9.gunicornの主なコマンド

gunicornの主なコマンドは以下の通りです。

$ . 仮想環境名/bin/activate  #仮想環境へ切り替え(ubuntu)
$ cd Djangoのプロジェクト名  #プロジェクト内のmanage.pyがあるディレクトリに移動
$ gunicorn --bind 127.0.0.1:8000 Djangoのプロジェクト名.wsgi -D #gunicornの起動、バインド
$ pkill gunicorn  #gunicornの停止
$ ps ax | grep gunicorn  #gunicornのプロセスの確認

「ps ax | grep gunicorn」の実行に対して、gunicornが接続されていないときでも以下の出力が表示されます。出力の中に「--bind 127.0.0.1:8000 Djangoのプロジェクト名.wsgi -D」が表示されていなければgunicornには接続されていません。

2366 pts/5    S+     0:00 grep --color=auto gunicorn

なお、「ps ax」は現在実行されている全てのプロセス一覧を表示します。そして、「| grep gunicorn」は「ps ax」で出力された内容の内、gunicornという文字を含む行のみを出力します。

10.設定のまとめ

設定の手順をまとめます。まず、Djangoプロジェクトtest_appとアプリケーションhelloを作成し、Djangoプロジェクトのsettings.pyを修正します。

#settings.pyの設定
DEBUG = False  #修正
ALLOWED_HOSTS = ['vpsサーバーのドメイン', 'localhost', '127.0.0.1']  #修正
STATIC_URL = '/static/'  #デフォルトのまま
STATIC_ROOT = '/usr/share/nginx/html/static'  #新たに追加
MEDIA_ROOT = '/usr/share/nginx/html/media'  #新たに追加

静的ファイルを、STATIC_ROOTに複写します。なお、仮想環境に切り替え、プロジェクトtest_appのmanage.pyが存在するディレクトリに移動する必要があります。

$ . 仮想環境名/bin/activate  #仮想環境へ切り替え(ubuntu)
$ cd test_app
$ python manage.py collectstatic

Nginxを起動します。

$ sudo systemctl start nginx.service  #Nginxの起動

仮想環境に切り替えた状態でgunicornをインストールし、gunicornをバインドし、デーモンモードで起動します。なお、gunicornのバインド前にプロジェクトtest_appのmanage.pyが存在するディレクトリに移動する必要があります。

$ python3 -m pip install -U pip  #pipのアップデート
$ python3 -m pip install gunicorn  #gunicornのインストール
$ cd /test_appのパス/test_app  #test_appはプロジェクト名
$ gunicorn --bind 127.0.0.1:8000 test_app.wsgi -D #test_pyはプロジェクト名

ブラウザでアクセスすると、Djangoプロジェクトのhelloアプリが表示されます。

http://ドメイン名/hello/
02_Django_test_app

なお、Djangoのsetting.pyを変更したときには、以下の実行で設定を有効にしてください。なお、gunicornのコマンドを実行するディレクトリはプロジェクトのmanage.pyが存在するディレクトリである必要があります。

$ pkill gunicorn  #gunicornの停止
$ cd /test_pyのパス/test_app  #test_appはプロジェクト名
$ gunicorn --bind 127.0.0.1:8000 test_app.wsgi -D #test_appはプロジェクト名

Nginxの設定を変更したときは、以下の実行で設定を有効にしてください。

$ sudo systemctl reload nginx.service  #設定ファイルの再読み込み

以上で、前回の記事とあわせてNginx、gunicorn、Djangoの設定が完了しました。

私が実際にレンタルしたVPSサーバー

私が実際にレンタルしたVPSサーバーはConoHa VPSです。私は1GBのプランを申し込みました。VPSサーバーは一般のレンタルサーバーと異なりOSやアプリケーションを自由に設定できるので、Pythonで計算した結果をサイトに表示することもできます。

なお、ConoHa VPSの特長として、サーバーのディスクイメージを丸ごとバックアップできるイメージ保存機能を無料で使用することができます。コードを変更して元に戻せなくなった場合にも安心です。

また、ConoHa VPSは途中でプランをスケールアップできるだけでなく、スケールダウンすることもできます。つまり、2Gプランを1Gプランに変更することができます。ただし、512MBプランだけはスケールアップ・ダウン機能が使用できないので注意してください。

また、初期費用なしで3日だけ借り、3日分の費用だけ払うといったことも可能なので気軽に始められます。※時間課金(月の上限額は決まっています)

私からの友達紹介でクーポンをゲット

なお、以下のリンクから入ると、私からの友達紹介の扱いとなり1000円分のクーポンが支給されます。リンク先には「友達紹介」といった表記がないので「友達紹介」が適用されているのか不安になりますが大丈夫です。登録終了後にログインしたユーザーTop画面を確認すると支給されたクーポン1000円が表示されるはずです。

[ Conohaの友達紹介 ]

なお、友達紹介では個人情報が紹介者には開示されないので安心してご利用ください。

その他

Twitterへのリンクです。SNSもはじめました♪

液晶ペンタブレットを購入しました
 (1) モバイルディスプレイを買うつもりだったのに激安ペンタブレット購入

以下、私が光回線を導入した時の記事一覧です。
 (1) 2020年「光回線は値段で選ぶ」では後悔する ←宅内工事の状況も説明しています。
 (2) NURO光の開通までWiFiルーターを格安レンタルできる
 (3) NURO光の屋外工事の状況をご紹介。その日に開通!
 (4) 光回線開通!実測するとNURO光はやっぱり速かった
 (5) ネット上のNURO光紹介特典は個人情報がもれないの?