CentOS7 Nginx+GunicornでDjangoを動かす

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Linux - CentOS7
2018年11月22日21:14に更新(約21日前)
2018年10月11日0:50に作成(約63日前)

旧ブログ移行記事です。

概要

CentOS7でDjangoを動かすシリーズシリーズの一つです。CentOS7でDjangoアプリケーションを実際に公開していきます。WebサーバーはNginx、WSGIサーバーはGunicornを利用します。

Djangoのインストールと初期設定

Pythonのインストールが終わっているなら、Djangoのインストールと初期設定を行います。

まず、Djangoのインストールです。venvやpipenvといった仮想環境を利用しても良いのですが、今回はグローバルな環境にpipでインストールを行います。

sudo pip3.7 install django

次にDjangoプロジェクトを作成します。プロジェクトの置き場所はいろいろ考えられますが、Django公式では以下のように推奨しています。

コードはどこに置くの?

(モダンなフレームワークを使わない) 古いプレーンな PHP の経験があるなら、これまでは Web サーバのドキュメントルート下 (/var/www といった場所) にコードを配置してきたことでしょう。 Django ではそうしないでください。 Python コードを Web サーバーのドキュメントルート下に置かないでください。コードをドキュメントルート下に置くと、 誰かがコードを Web を介して読めるようになってしまうからです。これは安全上良くありません。

コードはドキュメントルートの外、例えば /home/mycode の ような場所に置きましょう。

今回はユーザーのホームディレクトリ直下にDjangoプロジェクトを置くことにします。

cd ~
django-admin startproject project
cd project

設定ファイルを少し編集します。

vim project/settings.py

開いてすぐにあります。DEBUGをFalseにし、ALLOWED_HOSTSにはそのサーバーのIPアドレスをいれます。ドメインを既に取得した方は、IPアドレスの代わりにドメインでも良いです。'*'とすると全てのホストを受け入れるので、面倒であればこちらでも良いです。

DEBUG = False

ALLOWED_HOSTS = ['153.126.216.172']

下側にスクロールし、言語を日本語、タイムゾーンを東京に変更しておきます。

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

以下のようにしておきます。

STATIC_URL = '/static/'  # これは元からあります。
STATIC_ROOT = '/usr/share/nginx/html/static'

MEDIA_URL = '/media/'
MEDIA_ROOT = '/usr/share/nginx/html/media'

開発環境ではstaticファイルは自動的に配信されていました。アプリケーション内にstaticディレクトリを作ったり、settings.pyのSATICFILES_DIRS変数の設定をするとそこから配信されました。しかし、DEBUG=Falseにすると事情が変わります。

公式ドキュメントから抜粋

ファイルを配信する

これらの設定の手順に加えて、実際に静的ファイルを配信する必要があります。

開発中に django.contrib.staticfiles を使用する場合には、DEBUG を True に設定して runserver を実行すれば、自動的に設定が行われます。(詳しくは、django.contrib.staticfiles.views.serve() を参照)

ただし、この方法は 極めて非効率 であり、セキュリティ上の問題がある 可能性が高いため、本番環境で使うべきではありません。

本番環境で静的ファイルを配信するための適切な戦略については、静的ファイルのデプロイ を読んでください。

DEBUG=True はエラー内容がページ上に表示されます。攻撃者に余計な情報を与えてしまいます。また、Djangoフレームワーク側で静的ファイルの配信を行うよりも、その前の段階であるWebサーバー側で配信するほうがパフォーマンスも高くなります。

最近はWhiteNoise等の便利な物も出てきましたが、staticファイル、mediaファイルはWebサーバー側で配信する人はまだ多くいます。

static, mediaファイルはWeサーバー側で配信するとして、置き場所をどうするかというと、よくやるのはWebサーバーのドキュメントルート付近です。Nginxのデフォルトのドキュメントルートは/usr/share/nginx/htmlですので、今回はその直下にmediaとstaticディレクトリを作って配信することにしました。

Nginxのドキュメントルート以下に、media, static ディレクトリを作ります。

sudo mkdir /usr/share/nginx/html/media
sudo mkdir /usr/share/nginx/html/static

mediaファイル...FileFieldやImageFieldでのファイルアップロードは/usr/share/nginx/html/media に自動的に置かれます(MEDIA_ROOTの場所)。
しかしstaticファイルについては自分で集めて/usr/share/nginx/html/staticに置く必要があります。それを自動で行うためのコマンドがDjangoにあるので、使いましょう。

sudo python3.7 manage.py collectstatic

このコマンドは各アプリケーション内のstatic、STATICFILES_DIRSにあるstaticなファイルを、STATIC_ROOT の場所にコピーするという単純な処理をしてくれます。staticファイルの追加や修正をすると、その都度collectstaticコマンドでの反映が必要になることも覚えておきましょう。

migrateと、createsuperuser でスーパーユーザーを作成しておきます。

python3.7 manage.py migrate
python3.7 manage.py createsuperuser

Nginxの設定ファイル

Nginxの設定ファイルを編集しましょう。

sudo vim /etc/nginx/conf.d/project.conf

以下のようにしておきます。

server {
    listen  80;
    server_name 153.126.216.172;

    location /static {
        alias /usr/share/nginx/html/static;
    }

    location /media {
        alias /usr/share/nginx/html/media;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server_name はIPアドレスかドメインを入れます。
よく間違えやすいのはlocationの部分です。

/media から始まるURLは/usr/share/nginx/html/mediaを、/static から始まるURLは/usr/share/nginx/html/static を見に行くという指定です。それ以外で/から始まるURL...つまりすべてですが、このリクエストはDjangoを動かしている127.0.0.1:8000 にリクエストを転送します。

もし/blogから始まるURLだけDjangoで処理するならば、//blog のように書き換えるだけです。これを利用して、複数のDjangoプロジェクトを起動させることも簡単です。

proxy_path の部分は、今回は1サーバー構成なので127.0.01....ローカルホスト、このサーバー自体ですが、http://160.16.126.11:8000 のような別の場所になることもありますし、幾つかのアプリケーションサーバーの中から、今暇な奴に転送する、といったことも行います。

もしHTTPS対応するならば以下のような感じになるでしょう。上側のserverディレクティブは、httpのアクセスは無条件でhttpsにリダイレクトさせています。ssl関連の記述もありますね。

server {
    listen 80;
    listen [::]:80;
    server_name narito.ninja;
    return 301 https://$host$request_uri;
}


server {
    listen  443 ssl;
    server_name narito.ninja;

    ssl on;
    ssl_certificate         /etc/letsencrypt/live/narito.ninja/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/narito.ninja/privkey.pem;

    location /static {
        alias /usr/share/nginx/html/static;
    }

    location /media {
        alias /usr/share/nginx/html/media;
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

設定ファイルに記述ミスがないかを調べましょう。

sudo nginx -t

以下のようにokと言われればOKです。

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

問題なさそうなので、Nginxを再起動して設定ファイルを反映してもらいましょう。

sudo systemctl reload nginx

Gunicornで動かす

まず、Gunicronをインストールします。これはpipでインストールできます。

sudo pip3.7 install gunicorn

単純に動かす

cd ~/project
sudo gunicorn --bind 127.0.0.1:8000 project.wsgi:application

このあと、ブラウザでアクセスしますが...このチュートリアルどおりにやっている場合は、Djangoプロジェクトに何かDjangoアプリケーションを入れている訳でもありません。なので、標準で入っている管理サイトにアクセスして動いているかを確認しましょう。ブラウザのURL入力欄に、http://ドメインorIP/admin とします。

見慣れた管理画面が見えたら、ここまでは問題なしです。 管理画面が表示された

念のため、ユーザーの追加もおこなってエラーが出ないか確認しておきましょう。 ユーザーの追加画面

デーモンモードで動かす

↑のコマンドでは、サーバーを起動するとコンソールにログが表示され、そのままになります。サーバーを起動するためにコンソールを1つ開きっぱなしの状態になるのは嫌なので、デーモンモードで動かすことにします。

sudo gunicorn --daemon --bind 127.0.0.1:8000 config.wsgi:application

--daemonが追加されただけですね。

Djangoを停止させたい場合は以下のようにします。まず、8000番ポートで開いているプロセスを列挙します。

sudo lsof -i:8000

12902の列がプロセスの番号です。

gunicorn 12902 root    6u  IPv4 198206      0t0  TCP localhost:irdmi (LISTEN)
gunicorn 12905 root    6u  IPv4 198206      0t0  TCP localhost:irdmi (LISTEN)

後は、そのプロセスをkillします。

sudo kill -9 12902
sudo kill -9 12905

systemctlコマンドへの登録

↑の方法も次第に面倒になります。今までsystemctlコマンドで起動や再起動をしてきましたが、自分で登録したものをsystemctl ...で管理することもできます。

まず設定ファイルを作ります。/etc/systemd/system内にファイルを作る必要があります。

sudo vim /etc/systemd/system/project.service

以下のようにしておきましょう。

[Unit]
Description=gunicorn
After=network.target

[Service]
WorkingDirectory=/home/narito/project
ExecStart=/usr/local/bin/gunicorn --bind 127.0.0.1:8000 project.wsgi:application

[Install]
WantedBy=multi-user.target

WorkingDirectoryはカレントとなるパスで、ExecStartは実行コマンドです。以下のようなことをしていると思えばイメージしやすいでしょう。

cd WorkingDirectory
ExecStart

フルパスで書くようにしましょう。gunicornのフルパスがわからない場合は、which gunicorn として探せます。例えば仮想環境を利用していれば/home/.virtualenvs/project/bin/gunicorn のようになるかもしれません。

これで、以下のようなコマンドが使えます。projectの部分は、ファイル名に対応しています。

# 起動
sudo systemctl start project

# 再起動
sudo systemctl restart project

# 停止
sudo systemctl stop project

また、今度設定ファイルを修正した場合はsudo systemctl daemon-reload というコマンドも必要になります(そのようにメッセージ表示されるので直ぐにわかります)。

FAQ

エラー 413 (Request Entity Too Large)

このエラーが出た場合、Nginxのデフォルトのアップロード許容サイズを超えています。confファイルに以下のように追加しましょう。

client_max_body_size 100M;

settings.pyを分けている場合

開発環境と本番環境でsettings.pyを分けるケースはよくあります。その際、本番環境のGunicoronに別のsettings.pyを読み込んでもらいたい場合は以下のようにします。

gunicorn --env DJANGO_SETTINGS_MODULE=project.other_settings --bind 127.0.0.1:8000 project.wsgi:application

--env DJANGO_SETTINGS_MODULE=project.other_settingsの部分ですね。

他に私がよくやるのは、本番環境用にproduction_wsgi.py, production_settings.pyを別途用意しておき、gunicorn --bind 127.0.0.1:8000 project.production_wsgi:application として本番用のwsgi.py...production_wsgi.pyをまず読み込ませます。本番環境でシンプルに進めた場合、どの設定ファイルを使うかはwsgi.pyを読み込んだ際に決定するためです。wsgi.pyの以下の部分です。

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.production_settings')

本番用のwsgi.pyを読み込ませることで、本番用のsettings.pyを読み込ませる形になります。urls.pyも本番環境ではちょっと変更したい場合がある(admin管理サイトのパス変更)ので、production_urls.py も作成することがあります。それをするために、production_settings.pyで以下のように書き換えます。

from .settings import *
#本番環境用に色々上書きしていく...
...
...
ROOT_URLCONF = 'project.produnction_urls'

ROOT_URLCONF = 'project.produnction_urls'の部分ですね。

サイトマップやフィードのURLがhttpsにならない

↑にあるconfファイルのproxy_set_header...の部分があることを確認し、settings.pyに以下を追加します。

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする