VPSを契約してからCentOS + Nginx + uWSGI でDjangoアプリをデプロイするまで

## はじめに
Djangoで作っていたアプリを、VPS借りて動かしたいと思ったときにいろいろ躓いたのでメモ。

## 前提
- CentOS 7.6
- VPSは契約済(この記事ではConohaVPS)。

## 構成
イメージとしては下記のようになる
Web Browser <--> Nginx <--> socket <--> uWSGI <--> Djnago <--> SQLite

## リポジトリを作成

下記コマンドでリポジトリを作成する

```CentOS:CentOS
# yum install -y https://centos7.iuscommunity.org/ius-release.rpm
```

きちんと作成されているか確認

```CentOS:CentOS
# yum install -y https://centos7.iuscommunity.org/ius-release.rpm
```

## Pythonをインストールする

Pythonをインストールし、エイリアスを登録しておく

```CentOS:CentOS
# yum install -y python36u python36u-libs python36u-devel python36u-pip
# alias puthon3='python3.6'
```

## SSH、firewallの設定
SSHのポート番号が22というのは広く知れ渡っているため、悪意のあるロボットの「無作為IPアドレス+22番ポート」
攻撃から身を守る必要がある。そのため、ポートを変えたほうが良いとのこと。

まず、firewallの起動と自動起動設定を行う。

```CentOS:CentOS
# systemctl start firewalld.service
# systemctl enable firewalld
# systemctl status firewalld
```

次に、http、https、sshでの外部アクセスを許可する。また、80も空けておく。

```CentOS:CentOS
# firewall-cmd --permanent --add-service=http
# firewall-cmd --permanent --add-service=https
# firewall-cmd --permanent --add-port=xxxxx(任意のポート番号)/tcp
# firewall-cmd --permanent --zone=public --add-port=80/tcp

# systemctl restart firewalld
```
/etc/ssh/sshd_configファイルを編集する。

```CentOS:CentOS
# vi /etc/ssh/sshd_config

...略...
#Port 22 ← デフォルトはポート番号22
Port 54321 ←【例】任意の空きポート番号を指定する
...略...
```

設定反映を行う。

```CentOS:CentOS
# service sshd restart
```

設定したポートでSSHで接続できるか確認する。

## SQLiteの最新化
後でDjangoを入れて、サーバーを起動すると、SQLiteのバージョンが古いとエラーが発生する。

```CentOS:CentOS
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
```

そのため、新しいSQLiteを入れてあげる必要がある。

まず、最初はCのコンパイラが入ってないので入れてあげる。

```CentOS:CentOS
# yum -y install gcc
```

SQLiteのソースを取得、ビルドしてインストールまで行う。

```CentOS:CentOS
# wget https://www.sqlite.org/2019/sqlite-autoconf-3290000.tar.gz
# tar zxvf ./sqlite-autoconf-3290000.tar.gz
# cd ./sqlite-autoconf-3290000
# ./configure --prefix=/usr/local
# make
# make install
# find /usr/ -name sqlite3
```

不要なファイルを削除する。

```CentOS:CentOS
# cd ..
# rm -rf ./sqlite-autoconf-3290000 ./sqlite-autoconf-3290000.tar.gz
```

新しいSQLiteを適用する。

```CentOS:CentOS
# mv /usr/bin/sqlite3 /usr/bin/sqlite3_old
# ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3
# echo 'export LD_LIBRARY_PATH="/usr/local/lib"' >> ~/.bashrc
# source ~/.bashrc
```

下記でバージョンを確認すると、新しくなっていることが確認できる。

```CentOS:CentOS
# sqlite3 --version
```

## nginxを入れる

下記コマンドでインストールする。

```CentOS:CentOS
# rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
# yum install nginx -y
```

## デプロイ先ディレクトリ、仮想環境の作成
今回は/var/www/配下にいろいろ作っていきます。

```CentOS:CentOS
// デプロイ先ディレクトリ作成
# mkdir -p /var/www/test_mysite

// 仮想環境の作成
# cd /var/www/test_mysite
# python3 -m venv myvenv

// 仮想環境を有効化
# source myvenv/bin/activate
```

## パッケージをインストール

pipで必要なパッケージをインストールする。

```CentOS:CentOS
// pipを更新
(myvenv)# pip install --upgrade pip

// 仮想環境にDjangoをインストール
(myvenv)# pip install django

// 仮想環境にuWSGIをインストール
(myvenv)# pip install uwsgi
```

Djangoプロジェクトも作成しておく。

```CentOS:CentOS
(myvenv)# django-admin startproject mysite

// 一旦仮想環境を無効にする
(myvenv)# deactivate
```

## uWSGIの設定

socketファイルとpidファイルを格納するディレクトリを作成し、所有者や権限を変更する。

```CentOS:CentOS
// ディレクトリ作成
# mkdir -p /var/run/uwsgi

// 所有者変更
# chown root:nginx /var/run/uwsgi

// 権限変更
# chmod +w /var/run/uwsgi
```

先ほど作成した仮想環境myvenv配下にuWSGIのための変数を定義するファイルuwsgi_paramsを作成する。

```CentOS:CentOS
// 移動
# cd /var/www/test_mysite/myvenv

// ファイル作成
# vi uwsgi_params
```

```bash:/var/www/test_mysite/myvenv/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;
```

同じくmyvenv配下にuwsgi.iniファイルを作成する。

```CentOS:CentOS
// 移動
# cd /var/www/test_mysite/myvenv

// ファイル作成
# vi uwsgi.ini
```

```bash:/var/www/test_mysite/myvenv/uwsgi.ini
[uwsgi]
uid = nginx
gid = nginx

# 作成したDjangoアプリのルートを指定する
chdir = /var/www/test_mysite/mysite

# wsgi.pyの場所を指定する
# (Djangoのプロジェクト名を「mysite」にするとmysite/配下にwsgi.pyがあるはず)
module = mysite.wsgi

# 仮想環境の場所を指定する
home = /var/www/test_mysite/myvenv

master = true
processes = 2
threads = 1

# 先ほど作成したsocketとpidファイルの作成場所を指定する
socket = /var/run/uwsgi/master.sock
pidfile = /var/run/uwsgi/master.pid

chmod-socket = 666
vacuum = true
thunder-lock = true
max-requests = 6000
max-requests-delta = 300

# log
logto = /var/log/uwsgi/uwsgi.log
deamonize = /var/log/uwsgi/uwsgi-@(exec://date +%Y-%m-%d).log
log-reopen = true
```

iniファイルの文法は[こちら](https://qiita.com/11ohina017/items/da2ae5b039257752e558)を参考にさせていただきました。

サーバーを再起動したときにuWSGIが自動で起動するように[/etc/systemd/system/]配下にuwsgi.serviceを作成する。

```CentOS:CentOS
// 移動
# cd /etc/systemd/system

// ファイル作成
# vi uwsgi.service
```

```CentOS:/etc/systemd/system/uwsgi.service
# uwsgi.service
[Unit]
Description=uWSGI
After=syslog.target

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /var/run/uwsgi; chown root:nginx /var/run/uwsgi; chmod g+w /var/run/uwsgi;'
# 自分が作成した仮想環境配下のactivateとuwsgi.iniファイルを指定する。
ExecStart=/bin/bash -c 'source /var/www/test_mysite/myvenv/bin/activate; uwsgi --ini /var/www/test_mysite/myvenv/uwsgi.ini'
#Restart=always
Restart=on-failure
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target
```

サービスの自動起動をONにする。

```CentOS:CentOS
// サービスの停止
# systemctl stop uwsgi

// サービスの自動起動の有効化
# systemctl enable uwsgi

// サービスの開始
# systemctl start uwsgi
```


## nginxの設定

[こちら](https://qiita.com/si-masuda/items/6df2b19dfae5bef107a8)を参考にさせていただきました。

同一サーバーで複数のアプリケーションを運用することも想定し、バーチャルホストの設定を行う。

まず、[/etc/nginx/sites-available]と[/etc/nginx/sites-enabled]を作成する。

```CentOS:CentOS
// 設定ファイルを格納するディレクトリを作成する。
# mkdir /etc/nginx/sites-available

// 有効にしたいバーチャルホストの設定ファイルへのシンボリックリンクを格納するディレクトリ
# mkdir /etc/nginx/sites-enabled
```

[/etc/nginx/sites-available]配下に設定ファイルを作成する。

```CentOS:CentOS
// 移動
# cd /etc/nginx/sites-available

// ファイル作成(名前は任意)
# vi test_mysite
```

```bash:/etc/nginx/sites-available/test_mysite
# Djangoの設定
upstream django {
    # 上記で設定したソケットファイルの場所を指定する。
    server unix:///var/run/uwsgi/master.sock;
}

# サーバの設定
server {
    # ポート番号
    listen      80;
    # VPSのIPアドレスを入力する
    server_name [IPアドレス];
    charset     utf-8;

    # Djangoの静的ファイルの設定
    location /static {

        # staticディレクトリが存在する場所を指定する。
        # python manage.py collectstaticをするとプロジェクト直下のstaticに集約されるので、プロジェクト直下を指定しておく。
        root /var/www/test_mysite/mysite;
    }

    location / {
        uwsgi_pass  django;
        # 上記で設定したuwsgi_paramsの場所を指定する
        include     /var/www/test_mysite/myvenv/uwsgi_params;
    }
}
```

nginxが起動したときに[/etc/nginx/sites-enabled]配下を読み込むようにして、[/etc/nginx/sites-enabled]配下では[/etc/nginx/sites-available]の設定ファイルへのシンポリックリンクを張ったり削除することで、バーチャルホストの設定を有効にしたり、無効にしたりし、管理を楽にする。

上記で作成した設定ファイルのシンボリックリンクを[/etc/nginx/sites-enabled]に貼る。

```CentOS:CentOS
// 有効にしたいバーチャルホストの設定ファイルへリンクを張る。
# ln -s /etc/nginx/sites-available/test_mysite /etc/nginx/sites-enabled/test_mysite
```

[/etc/nginx/]配下にあるnginx.confを修正し、[/etc/nginx/sites-enabled]を見るようにする。

```CentOS:/etc/nginx/nginx.conf
http {
    ...
     #gzip  on;
+    include /etc/nginx/sites-enabled/*;
     include /etc/nginx/conf.d/*.conf;
     ...
}
```

OSを再起動してもnginxを自動で起動するようにする。[/etc/systemd/system/]配下にnginx.serviceを作成する。

```CentOS:/etc/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

サービスの自動起動をONにする。

```CentOS:CentOS
// サービスの停止
# systemctl stop nginx

// サービスの自動起動の有効化
# systemctl enable nginx

// サービスの開始
# systemctl start nginx
```

## Djangoの設定
Djangoのマイグレーション等を行う

```CentOS:CentOS
# cd /var/www/test_mysite

# source myvenv/bin/activate

(myvenv)# cd mysite

// マイグレーションを行う
# python3 manage.py makemigrations
# python3 manage.py migrate
```

また、Djangoの設定ファイルを編集する。まずはsetting.pyのALLOWED_HOSTSを編集する。

```CentOS:/var/www/test_mysite/mysite/mysite/setting.py
・・・略・・・
ALLOWED_HOSTS = ['IPアドレスまたはドメイン']
・・・略・・・
```

静的ファイルを下記コマンドで集める

```CentOS:CentOS
(myvenv)# python3 manage.py collectstatic
```

## これでアクセスできるようになるはず

VPSのIPアドレスで接続すれば、Djangoのページが出てくるはず。
もし、エラーが出た場合は、nginxのログファイル(/var/log/nginx/error.log)やuWSGIのログファイル(/var/log/uwsgi/uwsgi.log)を確認してみる。


## Djangoのデバッグエラー画面が出てきたときのメモ

setting.pyにてDEBUGモードをTrueにしたままだったので、何も問題が発生しなければFalseにするべきだが、いくつかエラーが発生したので、その解決方法をメモしておく。


### unable to open database file と出てくるとき
SQLiteが開けないと怒られるエラー。SQLiteは、データベースファイルへの書き込み権限だけでは無く、ディレクトリにも書き込み権限が無いとダメらしい。
そのため、権限を見直すと解決する。

### SQLite 3.8.3 or later is requiredと出てくるとき

上でSQLiteを新しくして、python manage.py runserverでは普通に動くのに、ブラウザからアクセスすると上記のエラーで怒られた。
原因はさっぱりわからなかったので、怒られてる該当箇所を書き換えた。趣味レベルだったらこれでいいかなと。

怒られてるファイル(/var/www/test_mysite/myvenv/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py)を編集する。

```CentOS:base.py
・・・略・・・
def check_sqlite_version():
    # if Database.sqlite_version_info < (3, 8, 3):
    if Database.sqlite_version_info < (3, x, x):
        raise ImproperlyConfigured('SQLite 3.8.3 or later is required (found %s).' % Database.sqlite_version)
・・・略・・・
```