nginx、uWSGI、Djangoの構成でEC2にアプリケーションをデプロイする

# 概要

　この記事は初心者の自分がRESTful なAPIとswiftでiPhone向けのクーポン配信サービスを開発した手順を順番に記事にしています。技術要素を1つずつ調べながら実装したため、とても遠回りな実装となっています。

前回の [Djangoで画像を配信できるwebAPIを作る](https://qiita.com/Ajyarimochi/items/b7278c357b2ffca183b5) で クーポンの画像も配信できるようにし、必要な機能については一旦全て実装できたと思います。次はサーバにデプロイをしてパブリックな環境からAPIを利用できる状態にします。

大まかな作業手順は

1. クラウドのインスタンスを立てて必要なソフトやパッケージをインストール
2. APIをサーバにデプロイ

です。

この記事は **2. APIをサーバにデプロイ** について纏めています。
[1. クラウドのインスタンスを立てて必要なソフトやパッケージをインストール の記事はこちら](https://qiita.com/Ajyarimochi/items/674b703622155e46dc1d)です。

### 構成

　djangoで開発したwebAPIは nginx、uwsgi、djangoの構成で動かします。お互いの関係は下記のイメージになります。

```
Webクライアント <-> nginx <-> the socket <-> uwsgi <-> django
```

　今回は nginxに8000番ポート、djangoに8001番ポートを割り当てます。8000番ポートでリクエスト受け取ったnginxが、リクエストを8001番ポートで待ち受けているuwsgiにsocketで渡します。

　デプロイする環境の構成はこちら

<img width="980" alt="system-stack.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526594/7a246a60-a91e-7391-61ff-1476345393e9.png">




### 参考

* [Django + uWSGI + nginx (uWSGIチュートリアルの和訳)](https://qiita.com/Ningensei848/items/e91fbeef5f03685a7a0b)
* [EC2上で Django + Nginx + uWSGI を試す](https://qiita.com/melos/items/91dc3b45a589ea5a369b)



### 環境

Amazon Linux 2
Python 3.7.4
Django 3.0.3
SQLite 3.31.1


# 手順

* デプロイ資材の準備

* DjangoのサーバでAPIを起動（テスト）
 * Settings.pyのALLOW_HOSTの設定を変更
 * Djangoサーバを起動
* uwsgi経由でAPIを起動（テスト）
* nginxでAPIを動かす
 * uwsgi_params の準備
 * nginx.confの編集
 * settings.pyの編集
 *nginxの起動と接続確認


## デプロイ資材の準備

　Gitを使ってソースコードなどの資材をサーバに展開します。事前に最新の資材をリモートリポジトリへpushしておきます。

　git clone で資材をサーバにダウンロード

```
$ git clone [リモートリポジトリのURL] 
```



## DjangoのサーバでAPIを起動（テスト）

### Settings.pyのALLOW_HOSTの設定を変更

　プロジェクト名のディレクトリ配下のSettings.pyに`ALLOW_HOST`という項目があり、ここで指定したIPアドレス以外ではdjangoが起動しない仕組みになっています。

　デフォルトではローカルになっているはずなので、VimなどでSettings.pyを開き、インスタンスのグローバルIPに変更します。なお、`ALLOWED_HOSTS = ["*"]`とすればどのIPの環境でも起動できます。


### Djangoサーバを起動

　djangoプロジェクト配下（manage.pyのあるディレクトリ）まで移動してdjangoサーバを起動するコマンドを実行

```
$ python3 manage.py runserver 0.0.0.0:8000
```

　ブラウザ等でアクセスして起動している事を確認します。確認後は CONTROL-C でサーバを停止させておきます。

　ここで、なぜdjangoサーバでもAPIを利用できるのにわざわざnginxと連携させるのか疑問を持ちませんか？　自分の場合はそう思いました。APIを公開するだけならnginx等のwebサーバと連携させなくてもAPIを叩くことが出来ました。
　その上で調べたり、試してみた結果、nginxと連携させる利点は下記の２つかと思います。

* nginxと連携させた方がセキュリティが強い
 * djangoサーバのみで公開するとdjango adminページにもアクセスできるため、パスワード認証を通れば第三者がモデルのデータを変更することが可能なのではと思います。
* リクエストが沢山来た時の処理が早く、安定している（らしい）


## uwsgi経由でAPIを起動（テスト）

　Djangoサーバを起動させる操作をuwsgiに実行させます。
　この時、`--module`で`.wsgi`ファイルを指定します。ファイル名は`[プロジェクト名].wsgi`となります。同じ名前のファイルはディレクトリ内に存在しませんが大丈夫です。

　また、プロトコルとポートを指定する必要があります。ここではプロトコルを`http`、ポートを8000番で指定します。

```
# uwsgiの起動
$ uwsgi --http :8000 --module ami_coupon_api.wsgi
```

　ブラウザ等でアクセスして起動している事を確認します。



## nginxでAPIを動かす

### uwsgi_params の準備

　djangoプロジェクトのディレクトリ配下（manage.py と同じ場所）に `uwsgi_params` ファイルを置く必要があります。`uwsgi_params` ファイルはnginxのディレクトリ配下に格納されているので、コピーするかシンボリックリンクを貼ります。

　なお、中身はデフォルトのままで問題ありませんでした。

```
# nginxのディレクトリに移動して uwsgi_paramsファイルをコピー
$ cd /etc/nginx/
$ sudo cp uwsgi_params [djangoプロジェクトのディレクトリ（manage.py と同じ場所）のパス]
```

### nginx.confの編集

　nginxのディレクトリ配下の `nginx.conf`ファイルを編集します。パスやポートの設定を自分の環境に合わせる必要があります。
　

```
# nginxのディレクトリに移動
$ cd /etc/nginx/

# オリジナルのバックアップ を取っておく
$ sudo cp nginx.conf nginx.conf.org

# Vimでnginx.confを編集
$ sudo vi nginx.conf
```

　編集後の nginx.conf　※内容は環境によって異なります

```nginx.conf

  ### 省略 ###

    upstream django {
        server 0.0.0.0:8001; 　# 編集  djangoに割り当てるポートを設定します
    }


    server {
        listen       8000;   # nginxに割り当てるポート
        server_name  XXX.XXX.XXX.XXX;   # 編集
        root         /usr/share/nginx/html;
        charset      utf-8;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        client_max_body_size 100M;　# 編集

	location /static {
	    alias /home/ec2-user/ami_api/static;
	}
        location / {
        uwsgi_pass django;   # 編集
        include /home/ec2-user/ami_api/uwsgi_params;   # 編集
        }
    }

  ### 省略 ###
```


### settings.pyの編集

　プロジェクト名/settings.py　に`STATIC_ROOT`の設定を追加します。settings.pyをVimなどで開き下記の設定を追加します。

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

　追加したら設定を反映します。

```
# manage.pyのあるディレクトリまで移動してコマンドを実行
$ python3 manage.py collectstatic
```
　成功すると `static` というディレクトリが追加されます。


### nginxの起動と接続確認

```
# nginx の起動
$ sudo systemctl start nginx

# 起動の確認
$ systemctl status nginx
```

　下記のように`active (running)`と表示されていれば起動しています

```
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) 
```

　なお、nginxの停止と再起動のコマンドは下記になります

```
# 停止
$ systemctl stop nginx

# 再起動
$ systemctl restart nginx
```

　ブラウザ等でアクセスして起動してAPIが動作する事を確認します。


以上でサーバへのデプロイは完了です。