Python
Django
nginx
AWS

Nginx + uWSGI + SupervisorでDjangoアプリケーションをデプロイする

これがベストプラクティスかどうなのかわかりませんが、おれはこうやりました、って話
もし、これでうまくいかなかったらコメントください

環境

  • Python:3.6.0(anacondaで構築)
  • Django:1.10.2
  • nginx:1.11.10
  • Amazon Linux AMI

今回はAMIでやりましたが、多分CentOSでも大丈夫なはず

大雑把な流れ

1.uWSGIのインストール & uWSGIでDjangoアプリケーションを立ち上げる
2.nginx + uWSGIでアプリケーションを立ち上げる
3.supervisorでプロセスをdaemon化する

uWSGIのインストール & uWSGIでDjangoアプリケーションを立ち上げる

まずはuWSGIをpipでインストール

$ pip install uwsgi

もしもインストールがうまくいなかったりしたらuwsgiをEC2インスタンスにインストールしようとして失敗した話 - Qiitaを参考にしてみましょう

uWSGIからDjangoアプリケーションを立ち上げる

インストールができたら、下記コマンドでuWSGIからDjangoアプリケーションを立ち上げてみる
(実行するディレクトリはDjangoプロジェクトのルートディレクトリで)

$ uwsgi --http :8000 --module プロジェクト名.wsgi

この状態でhttp://[EC2のパブリックIP]:8000にブラウザからアクセスすれば、アプリケーションに接続できるはず


nginx + uWSGIでアプリケーションを立ち上げる

nginxのuwsgiモジュール用の設定を行う

Djangoプロジェクト直下にuwsgi_paramsというファイルを作成する
ファイルの中身は下記の通り

uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;

nginx用のconfファイルを作成

Djangoプロジェクト直下にnginx用のconfファイルを作成
今回はmyweb_nginx.confという名前にしています

django_project/myweb_nginx.conf
upstream django {
    server 127.0.0.1:8000;
}

server { 
    listen      80;
    server_name EC2のパブリックIP or ドメイン;
    charset     utf-8;
    client_max_body_size 100M;

    location /static {
        alias /django_projectのディレクトリ(フルパス)/static;
    }

    location / {
        uwsgi_pass  django;
        include    /django_projectのディレクトリ(フルパス)/uwsgi_params;
    }

}

myweb_nginx.confを/etc/nginx/nginx.confに読み込ませる

$ sudo vi /etc/nginx/nginx.confhttp {}内を編集
include /etc/nginx/sites-enabled/*;を追加する

/etc/nginx/nginx.conf
http {
    ...中略...
    include /etc/nginx/sites-enabled/*; #ここを追加

    server {
    ...中略...

sites-enabledにシンボリックリンクを貼る

$ mkidr /etc/nginx/sites-enabledでsites-enabledを作成
さらに下記コマンドでmyweb_nginx.confを/etc/nginx/sites-enabled/にシンボリックを貼る

$ sudo ln -s /django_projectのディレクトリ(フルパス)/myweb_nginx.conf /etc/nginx/sites-enabled/

nginxを再起動し、Djangoアプリケーションを立ち上げる

$sudo /etc/init.d/nginx restartでnginxを再起動
下記のコマンドをDjangoプロジェクト直下で実行する

$ uwsgi --socket :8000 --module プロジェクト名.wsgi

この状態でhttp://[EC2のパブリックIP]にブラウザからアクセスすれば、アプリケーションに接続できるはず

補足:静的ファイルの読み込み

上記の状態でアプリケーションに接続した場合、staticディレクトリに配置しているcssなどが適用されていないはず
settings.pyにSTATC_ROOTを指定する必要がある

settings.py
STATIC_ROOT = os.path.join(BASE_DIR, "static/")

下記コマンドでstaticファイルをSATIC_ROOTで指定したディレクトリにまとめる

$ python manage.py collectstatic


supervisorでプロセスをdaemon化する

python2系への切り替え

supervisorはpython3系では動いてくれないので、anacondaでpython2.7の仮想環境を構築していく

$ conda create -n supervisor python=2.7 anaconda

仮想環境への切り替えは

$ source activate supervisor

でおこない、抜ける場合は

$ source deactivate

でおこなう

supervisorのインストール

python2系の状態でsupervisorをインストールする

$pip install supervisor

supervisord.confの生成

ホームディレクトリで$echo_supervisord_conf > supervisord.confを実行
さらに$ sudo mv supervisord.conf /etcを実行し、supervisord.confを/etcに配置する

supervisord.dディレクトリの作成

/etc配下にsupervisord.dを作成する

$ sudo mkdir /etc/supervisord.d

supervisorログの設定

/var/logにログを吐かせるため、supervisord.confを編集

/etc/supervisord.conf
[supervisord]
;logfile=/tmp/supervisord.log
logfile=/var/log/supervisor/supervisord.log

ディレクトリの作成とlogファイルの作成

$ sudo mkdir /var/log/supervisor
$ sudo vi /var/log/supervisor/supervisord.log

また、下記のコマンドでログローテションを設定する

$ sudo sh -c "echo '/var/log/supervisor/*.log {
       missingok
       weekly
       notifempty
       nocompress
}' > /etc/logrotate.d/supervisor"

pid,includeの設定

supervisord.confを編集する

/etc/supervisord.conf
[supervisord]
...中略...
# pidファイルの配置場所の指定
;pidfile=/tmp/supervisord.pid
pidfile=/var/run/supervisord.pid

# ...中略

# includeセクションがコメントアウトされているので、コメントインして下記の用に修正。
[include]
files = supervisord.d/*.conf

supervisord.d/*.confの作成

supervisord.d配下のconfファイルにデーモン化するプロセスを指定する
今回はuWSGI + nginxでのDjangoアプリケーションの立ち上げをデーモン化する

/etc/supervisord.d/django.conf
[program:django_app] ;
directory=/djangoアプリケーションのディレクトリ(フルパス)/;
command=/home/ec2-user/anaconda3/bin/uwsgi --socket :8000 --module プロジェクト名.wsgi ;
numprocs=1 ;
autostart=true ;
autorestart=true  ;
user=ec2-user  ;
redirect_stderr=true  ;

supervisorの再起動->サービス立ち上げ

$ supervisordでsupervisorのサービスを起動
$ supervisorctl rereadで設定ファイルの再読み込み
$ supervisorctl reloadでsupervisorの再起動
$ supervisorctl start django_appでDjangoアプリケーションの立ち上げるプロセスがデーモン化される

この状態でhttp://[EC2のパブリックIP]にブラウザからアクセスすれば、アプリケーションに接続でき、且つサーバからログアウトした状態でもプロセスが死なない


参考リンク

Supervisorで簡単にデーモン化 - Qiita
EC2上で Django + Nginx + uWSGI を試す - Qiita