uwsgi＋nginx＋postgresqlでDjangoの構築（AmazonLinux2）

システム要件
---
OS : AmazonLinux2
python : 3.9.2
sqlite : 3.35.0
Django : 4.0.5
uWSGI : 2.0.20
nginx : 1.20.0
psycopg2-binary : 2.9.3
php : 8.0.18
postgresql : 14.1

ディレクトリ構成
---
完成後のディレクトリ構成は下記のようになっています。
実行階層が重要になってくるので確認しながら進めてください。
今回はec2-userのホームディレクトリに配置するように構築しています。
（一部省略しています。）
```
～/
└ uwsgi-tutorial/
    └ mysite
        ├ manage.py
        ├ media/
        ├ mysite/
        │    ├ asgi.py
        │    ├ __init__.py
        │    ├ __pycache__/
        │    ├ settings.py
        │    ├ urls.py
        │    └ wsgi.py
        ├ mysite_uwsgi.ini
        ├ static
        │    └ admin/
        └ test.py
```


Djangoだけで動かす方法
---
Djangoには簡単な機能が備わっているので、Django単体でブラウザ上で動かすことも可能です。単体で動かす場合は下記の記事を参照して、上からコマンドをコピペしていくだけで実現します。
[venvでDjangoの環境を作成してみたった(Python3.3以降)](https://qiita.com/taaaaak/items/bc06c45158b42b6450ba)

構築作業
---
基本的に公式ドキュメントに沿って進めていきます。
[uWSGI公式ドキュメント](https://uwsgi.readthedocs.io/en/latest/tutorials/Django_and_nginx.html)
[Django公式ドキュメント](https://docs.djangoproject.com/ja/4.0/topics/install/)

## python3.9.2のインストール
こちらの記事にまとめましたので、参照してください。
記事内のpyenvを使用した方法で進めています。
[pythonのバージョン変更方法（aliasとpyenv）](https://qiita.com/m0chim0chi/items/bc51bf5e5165ac646857)

## sqliteをアップグレードする
AmazonLinux2ではデフォルトでsqlite3.7.17が入っています。
Djangoはsqlite3.9.0以上でないとエラーが出てしまうので、アップグレードしていきます。
本記事は最終的にはposgresqlを使用していくため、この工程は飛ばすことも可能です。
今回は公式ドキュメントに沿って進めるため、実行していきます。
```
$ wget https://www.sqlite.org/2021/sqlite-autoconf-3350000.tar.gz
$ tar fxvz sqlite-autoconf-3350000.tar.gz

$ cd sqlite-autoconf-3350000/
$ ./configure
$ make
$ sudo make install

$ sqlite3 --version
3.35.0 2021-03-12 15:10:09 acd63062eb06748bfe9e4886639e4f2b54ea6a496a83f10716abbaba4115500b
```
共有ライブラリにパスを通しておきます。
```
vi ~/.bashrc
```
```bash:.bashrc
#一番下に追記する
export LD_LIBRARY_PATH="/usr/local/lib"
```
```
$ source ~/.bashrc
```
pythonとsqlilteのバージョンを変更したら、Djangoの構築に入っていきます。

## uWSGIでDjangoを動かす
venvを使って仮想環境でDjangoとuWSGIをインストールしていきます。
```
$ cd
$ python3 -m venv uwsgi-tutorial
$ cd uwsgi-tutorial
$ source bin/activate
```
Djangoをインストールして、プロジェクトを作成します。
pythonのバージョンに合わせたバージョンのDjangoがインストールされます。（今回は`4.0.5`）
pipでインストールしたものは`pip list`でバージョンを確認できます。
```
$ pip install Django
$ django-admin startproject mysite
$ cd mysite
```
uWSGIをインストールしてテストページを作成します。
```
$ pip install uwsgi
$ vi test.py
```
```python:test.py
# test.py
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return [b"Hello World"] # python3
    #return ["Hello World"] # python2
```
uWSGIを起動してテストページを表示させます。
今回は公式に沿って、8000ポートを使用していきます。
```
$ uwsgi --http :8000 --wsgi-file test.py
```
`http://パブリックIPアドレス:8000`にアクセスして、テストページを表示させてみましょう。
uWSGIが動いていることを確認出来たら、DjangoをuWSGIを使用して表示させていきます。
```
vi mysite/settings.py
```
```python:settings.py
#変更前
ALLOWED_HOSTS = []

#変更後
ALLOWED_HOSTS = ['パブリックIPアドレス']
```
```
# manage.pyがある階層で実行
$ uwsgi --http :8000 --module mysite.wsgi
```
再度`http://パブリックIPアドレス:8000`にアクセスして、Djangoのページが表示されれば成功です。

## nginxでDjangoを表示させる
これまでの操作で`the web client <-> uWSGI <-> Django`まで連携が終了しています。
続いて`the web client <-> the web server <-> the socket <-> uWSGI <-> Python`の連携を進めていきます。

まずnginxをインストールし、起動させていきます。
```
$ sudo amazon-linux-extras install nginx1
$ sudo systemctl start nginx.service
$ sudo systemctl enable nginx.service
```
nginxに設定ファイルを作成していきます。
```
$ sudo mkdir /etc/nginx/sites-available/
$ sudo vi /etc/nginx/sites-available/mysite_nginx.conf
```
```conf:mysite_nginx.conf
# mysite_nginx.conf

# the upstream component nginx needs to connect to
upstream django {
    # server unix:///path/to/your/mysite/mysite.sock; # for a file socket
    server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8000;
    # the domain name it will serve for
    server_name パブリックIPアドレス; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Django media
    location /media  {
        alias /home/ec2-user/uwsgi-tutorial/mysite/media;  # your Django project's media files - amend as required #projectのパス配下にmediaを配置します。
    }

    location /static {
        alias /home/ec2-user/uwsgi-tutorial/mysite/static; # your Django project's static files - amend as required #projectのパス配下にstaticを配置します。
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
    }
}
```
/etc/nginx/sites-enabled にシンボリックリンクを作成します。
```
$ sudo mkdir /etc/nginx/sites-enabled/
$ sudo ln -s /etc/nginx/sites-available/mysite_nginx.conf /etc/nginx/sites-enabled/
```
nginx.confの最下部に追記して上記で作成した設定ファイルを読み込めるようにします。
```
$ sudo vi /etc/nginx/nginx.conf
```
```conf:nginx.conf
　　　～～～前略～～～
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

    include /etc/nginx/sites-enabled/*;

}
```
```
$ sudo systemctl restart nginx.service
```
Djangoの静的ファイルをnginxで表示させるため、プロジェクト内にコピーします。
まず始めに先ほど`mysite_nginx.conf`で指定したパスに`media`と`static`ディレクトリを作成します。
```
$ mkdir media
$ mkdir static
```
続いてプロジェクト配下にある`settings.py`に追記していきます。
```
$ vi mysite/settings.py
```
```python:settings.py
"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 4.0.5.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

import os           #追記
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

　
　　　～～～中略～～～


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

STATIC_ROOT = os.path.join(BASE_DIR, "static/")　　　#追記

```
`manage.py`がある階層で実行します。
```
$ python manage.py collectstatic
```
ここまで完了したら、uWSGIとnginxの連携を確認するため、テストページを表示させてみましょう。
`test.py`のある階層で下記コマンドを実行します。
```
$ uwsgi --socket :8001 --wsgi-file test.py
```
`http://パブリックIPアドレス:8000`にアクセスしてテストページが表示されたら、Djangoの表示も確認してみましょう。
`manage.py`のある階層で下記コマンドを実行します。
```
$ uwsgi --socket :8001 --module mysite.wsgi
```

## UNIXソケットを使用して接続する
UNIXソケットを使用して接続できるようにします。
`mysite_nginx.conf`の内容を修正します。
```
$ sudo vi /etc/nginx/sites-available/mysite_nginx.conf
```
```conf:mysite_nginx.conf
# mysite_nginx.conf

# the upstream component nginx needs to connect to
upstream django {
     server unix:/run/uwsgi/mysite.sock; # for a file socket #コメントアウトを外し、パスを入力する
    #server 128.0.0.1:8001; # for a web port socket (we'll use this first) #コメントアウトする
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8000;
    # the domain name it will serve for

```
```
$ sudo systemctl restart nginx.service
```
ソケットファイルができるディレクトリを作成し、権限を変更します。
このディレクトリの権限は所有者が`uWSGIを実行するユーザー`（今回はec2-user）、所有グループが`webサーバーを実行するユーザーグループ`に設定する必要があります。
```
$ sudo mkdir /run/uwsgi
$ sudo chown ec2-user:nginx /run/uwsgi/
```
UNIXソケットを使用して、Djangoを表示できるか確認してみましょう。
`manage.py`のある階層で下記コマンドを実行します。
`--chmod-socket=666`を使用して、ソケットファイルの権限を666にして実行します。
```
$ uwsgi --socket /run/uwsgi/mysite.sock --module mysite.wsgi --chmod-socket=666
```

## .iniファイルを使用してuWSGIを起動
これまでコマンドで実行していた操作を.iniファイルを作成して、起動できるようにします。
```
$ vi mysite_uwsgi.ini
```
```ini:mysite_uwsgi.ini
# mysite_uwsgi.ini file
[uwsgi]

# Django-related settings
# the base directory (full path)
chdir           = /home/ec2-user/uwsgi-tutorial/mysite
# Django's wsgi file
module          = mysite.wsgi
# the virtualenv (full path)
home            = /home/ec2-user/uwsgi-tutorial

# process-related settings
# master
master          = true
# maximum number of worker processes
processes       = 10
# the socket (use the full path to be safe
socket          = /run/uwsgi/mysite.sock
# ... with appropriate permissions - may be needed
chmod-socket    = 666
# clear environment on exit
vacuum          = true
```
起動してブラウザから確認してみましょう。
```
$ uwsgi --ini mysite_uwsgi.ini
```
ここまでできたら、仮想環境からでても、簡単にDjangoを立ち上げることが可能です。
以下の操作で試してみます。
```
$ deactivate
$ pip3 install uwsgi
$ uwsgi --ini mysite_uwsgi.ini
```

## postgresqlとDjangoを連携する

### インストールと設定を行う

pythonとpostgresqlを連携させるためにpsycopg2を使用します。
psycopg2のインストールは仮想環境化で行ってください。
```
$ cd ..   #uwsgi-tutorialがある階層へ移動
$ source bin/activate
$ pip3 install psycopg2-binary
```
インストールしたら`pip list`で確認しましょう。

phpとpostgresqlをインストールしていきます。
```
$ sudo amazon-linux-extras install php8.0 postgresql14
```
pyenvを使用していた場合は下記のようなエラーがでる可能性があります。
```
/home/ec2-user/.pyenv/versions/3.9.2/bin/python: No module named amazon_linux_extras
```
シンボリックリンクをはることで使用できるようになるはずです。
```
ln -s /lib/python2.7/site-packages/amazon_linux_extras ~/.pyenv/versions/3.9.2/lib/python3.9/site-packages/
```
必要なツールをインストールしていきます。
```
$ sudo yum install postgresql-server postgresql-devel postgresql-contrib
```
データーベースを初期化してから、一時的にパスワードなしでログインできるように設定を変更します。設定ファイルを開き、`ident`の箇所を`trust`に変更していきます。
```
$ sudo postgresql-setup initdb
$ sudo vi /var/lib/pgsql/data/pg_hba.conf
```
```conf:pg_hba.conf
    ～～～前略～～～
# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust #ここを変更
# IPv6 local connections:
host    all             all             ::1/128                 trust #ここを変更
# Allow replication connections from localhost, by a user with the
　　　～～～以下略～～～
```
システムを起動していきます。
```
$ sudo systemctl start postgresql
$ sudo systemctl enable postgresql
$ psql -h localhost -p 5432 -U postgres
```
起動できたら、データベース`django`、ユーザー名`django`、パスワード`django`を設定していきます。
それぞれの名前は適宜設定してください。
Djangoの推奨設定にも変更していきます。
```sql
postgres=# CREATE DATABASE django;
CREATE DATABASE
postgres=# CREATE USER django WITH PASSWORD 'django';
CREATE ROLE
postgres=# ALTER ROLE django SET client_encoding TO 'utf8';
ALTER ROLE
postgres=# ALTER ROLE django SET default_transaction_isolation TO 'read committed';
ALTER ROLE
postgres=# ALTER ROLE django SET timezone TO 'Asia/Tokyo';
ALTER ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE django TO django;
GRANT
```
コマンドに戻り、パスワード認証に設定を変更します。
設定ファイルを開き、`peer`を`md5`に変更します。
```
$ sudo vi /var/lib/pgsql/data/pg_hba.conf
```
```conf:pg_hba.conf
     ～～～前略～～～
# "local" is for Unix domain socket connections only
local   all             all                                     md5 #ここを変更
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
```
```
$ sudo systemctl restart postgresql
```
ここまででpostgresqlの設定は完了です。


### Djangoの設定

次にDjango側の設定を行います。
ユーザー名やデータベース名は適宜変更してください。

```
$ vi mysite/mysite/settings.py
```
変更前
```python:settings.py
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
```
変更後
```python:settings.py
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'django',
        'USER': 'django',
        'PASSWORD': 'django',
        'HOST': 'localhost',
        'PORT': '',
    }
}
```
設定が終わったら、マイグレーションを行います。
```
$ cd mysite    #manage.pyがある階層へ
$ python manage.py migrate
```
ここまでできたら連携は完了です。

起動してみましょう。
```
$ uwsgi --ini mysite_uwsgi.ini
```

`http://パブリックIPアドレス:8000`にアクセスして、ロケットが表示されることを確認しましょう。
![スクリーンショット 2022-06-28 153539.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2724001/c66e15ed-84c8-e04b-467b-1c16e36adb23.png)




# 参考サイト
[venvでDjangoの環境を作成してみたった(Python3.3以降)](https://qiita.com/taaaaak/items/bc06c45158b42b6450ba)
[uWSGI公式ドキュメント](https://uwsgi.readthedocs.io/en/latest/tutorials/Django_and_nginx.html)
[Django公式ドキュメント](https://docs.djangoproject.com/ja/4.0/topics/install/)
[pyenvを使ってPython実行環境の構築](https://codeaid.jp/pyenv-linux/)
[(Amazon Linux2(ec2),CentOS7)SQLite3の最新バージョンをインストールする](https://it-jog.com/sqlite/installnewsqlite3)
[Django2.2で開発サーバー起動時にSQLite3のエラーが出た場合の対応](https://qiita.com/rururu_kenken/items/8202b30b50e3bfa75821)
[Djangoのデプロイ：Nginx、uWSGI](https://intellectual-curiosity.tokyo/2018/10/06/django%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%EF%BC%9Anginx%E3%80%81uwsgi/)
[psycopg2をインストールしてPythonからPostgreSQLを操作する](https://self-development.info/psycopg2%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%81%A6python%E3%81%8B%E3%82%89postgresql%E3%82%92%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B/)
[「amazon-linux-extras」→「No module named amazon_linux_extras」になる場合の対処法](https://qiita.com/Yamkaz/items/e3e427fc613715255a4c)
[DjangoにPostgreSQLを適用する](https://qiita.com/shigechiyo/items/9b5a03ceead6e5ec87ec)