Nginx + Gunicorn + Django + Aurora (MySQL) の構成を図で説明してみる

ゴールは、Django + MySQL で開発したアプリケーションの本稼働用の環境を、Ansible を使って Amazon EC2 上に構成することです。

[前回の記事](https://qiita.com/3244/items/6000822f3a165e71c56d)で Django と Aurora を接続したので、下図のように Django の開発用サーバを使ってクライアントから接続することができるようになりました。

![aws-network-wsgi_runserver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/08f92137-c4cf-f8d4-8c2b-fe7ab1c97024.png)

しかし本稼働用としてリリースするためには、開発用サーバをアプリケーションサーバ／Web サーバと入れ替えて再構成する必要があります。
この構成が思った以上に複雑だったので、今回はまず全体の構成を図で説明してみます。

# 環境

* OS: Amazon Linux 2（Amazon EC2）
* アプリケーションフレームワーク: Django 2.2.6
* アプリケーションサーバ: Gunicorn 19.9
* Web サーバ: Nginx 1.12 (Amazon Extras)
* データベース: Amazon Aurora Serverless（MySQL 5.6.10 互換）
* 構成ツール: Ansible 2.8.5、AWS CloudFormation

※ venv などの仮想環境は導入せず、Django アプリケーション専用の EC2 とします。
※ Nginx と Gunicorn は同一インスタンス上で UNIX ドメインソケットを使って接続します。
※ オンプレミスの CentOS 7 でも同じ構成が可能です。

# 全体の構成
以下、構成要素を少しずつ増やしていきます。

## Django + Aurora (MySQL)
[前回の構成](https://qiita.com/3244/items/6000822f3a165e71c56d)で Django と Aurora を接続して、開発用サーバを使ってクライアントから接続できることを確認しました。

![aws-network-wsgi_runserver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/08f92137-c4cf-f8d4-8c2b-fe7ab1c97024.png)

### 設定
Django で開発したプロジェクトと Aurora の間で、以下の設定値を合わせることで接続できるようになります。

||Django|Aurora (MySQL)|
|:--|:--|:--|
|設定ファイル／リソース|mysite/mysite/settings.py|Type: AWS::RDS::DBCluster|
|設定項目|||
|DBホスト（接続先）|DATABASES: HOST|!GetAtt Endpoint.Address|
|DBユーザ名|DATABASES: USER|Properties: MasterUsername|
|DBユーザパスワード|DATABASES: PASSWORD|Properties: MasterUserPassword|

Django の設定ファイルは、コマンド`django-admin startproject mysite`でプロジェクトを作成した時に自動的に作られるファイル`settings.py`です。

Aurora の設定値は、CloudFormation で作成する`AWS::RDS::DBCluster`型リソースの戻り値（ホスト）とプロパティの値（ユーザ名、パスワード）です。
参考: [AWS リソースおよびプロパティタイプのリファレンス » AWS::RDS::DBCluster](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbcluster.html)

上記は管理者ユーザなので、動作確認が終わったら別途 Aurora で実行用ユーザを作成して Django の設定を変更します。

## Gunicorn + Django + Aurora (MySQL)
開発用サーバをアプリケーションサーバの [Gunicorn](http://docs.gunicorn.org/) と入れ替えてサービスとして起動します。

![aws-network-wsgi_gunicorn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/45a66e42-fe66-0e32-3a15-af563d232059.png)

アプリケーションサーバには Gunicorn 以外に [uWSGI](https://uwsgi-docs.readthedocs.io/) という選択肢もありますが、今回は 
公式ドキュメントに具体的な設定例が掲載されていた Gunicorn を選択しました。
参考: Deploying Gunicorn (Systemd): http://docs.gunicorn.org/en/latest/deploy.html#systemd

### 設定
Gunicorn と Django プロジェクトの間で、以下の設定値を合わせることで接続できるようになります。

||Gunicorn|Django|
|:--|:--|:--|
|設定ファイル|/etc/systemd/system/gunicorn.service||
|設定項目|||
|作業ディレクトリのパス|[Service]<br>WorkingDirectory|mysite/ のパス|
|WSGI設定ファイルのパス|[Service]<br>ExecStart<br>gunicornコマンドのパラメータ<br>**mysite.wsgi**|wsgi.py の相対パス<br>**mysite/wsgi**.py|

Gunicorn のファイルは、Gunicorn をサービスとして起動するために新規作成するユニットファイルです。
`WorkingDirectory`で Django プロジェクトの`BASE_DIR`のフルパスを指定します。

`ExecStart` のコマンドラインパラメータに WSGI 設定ファイルの相対パスを指定します。これは Django でプロジェクトを作成した時に自動的に作られる`mysite/mysite/wsgi.py`です。`WorkingDirectory`で`mysite/`を指定しているので、そこからの相対パスは`mysite/wsgi.py`で、`/`を`.`に変更して拡張子`.py`を省略すると、設定値は`mysite.wsgi`になります。

WSGI 設定ファイル wsgi.py の内容はデフォルトのままで接続できました。

## UNIXドメインソケット
上記の構成に Web サーバを追加した構成がゴールですが、その前に Web サーバと Gunicorn の接続に使用する UNIX ドメインソケットを説明します。

![aws-network-unix_socket.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/d0478da4-9e22-ec90-9886-622d4430a80c.png)

構成要素はソケットとディレクトリです。どちらも Linux の機能を使用するので、ソフトウェアをインストールする必要はありません。

### 設定

||ソケット|ディレクトリ|
|:--|:--|:--|
|設定ファイル|/etc/systemd/system/gunicorn.socket|/etc/tmpfiles.d/gunicorn.conf|
|設定項目|||
|ディレクトリのパス|[Socket]<br>ListenStream=**path**/socket|d **path** 0755 user group -|

ソケットはサービスと同様に systemd に制御してもらうので、新規ユニットファイル`/etc/systemd/system/gunicorn.socket`を作成します。[公式ドキュメントの設定例](http://docs.gunicorn.org/en/latest/deploy.html#systemd)でソケットを生成するディレクトリが`/run/専用ディレクトリ/`になっているので、ディレクトリ生成のために以下の設定ファイルを作成します。ソケットを`/run/`の直下や不揮発領域に生成する場合は、以下の設定は不要です。

`/run/`は tmpfs（RAM ディスク）なので、OS が終了するたびに中身が初期化されてしまいます。そのため、OS 起動時に専用ディレクトリを systemd-tmpfiles に生成してもらうための設定ファイル（/etc/tmpfiles.d/gunicorn.conf）を作成します。

上記両方のファイルのパス（表内の **path**）を同じにすることで、専用ディレクトリとソケットが自動的に生成されるようになります。

## Nginx + UNIXドメインソケット + Gunicorn + Django + Aurora (MySQL)
Web サーバとソケットを追加した完成形です。
Web サーバには Nginx 以外に Apache HTTPD という選択肢もありますが、多数のクライアントからの接続を効率よく処理できる Nginx を選択しました。

![aws-network-wsgi_all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/c3e4afad-38cf-faf5-ee1d-853aede24edc.png)

### 設定
公式の設定例では nginx.conf を直接変更していますが、今回は仮想サーバとして専用の設定ファイル（/etc/nginx/sites-available/site.conf）を作成しました。

||ソケット|ディレクトリ|Nginx|Gunicorn|
|:--|:--|:--|:--|:--|
|設定ファイル|/etc/systemd/system/gunicorn.socket|/etc/tmpfiles.d/gunicorn.conf|/etc/nginx/sites-available/site.conf|/etc/systemd/system/gunicorn.service|
|設定項目||||
|ソケット用ディレクトリのパス|[Socket]<br>ListenStream=**path**/socket|d **path** 0755 user group -|proxy_pass http\://unix:**path**/socket;|[Service]<br>ExecStart<br>gunicornコマンドのオプション<br>--bind unix:**path**/socket|

上記 4つの設定ファイルに同じパスを設定することで、Nginx と Gunicorn を UNIX ドメインソケットで接続できるようになります。

# 次の課題
以上の構成を Ansible で自動化します。

## 記事一覧
この記事には前回と次回があります。

* 1/4: [DjangoをAmazon Linux 2にAnsibleで構成する、Aurora (MySQL) をCloudFormationでUTF-8に構成する、そして接続するまでが地味に大変だったこと](https://qiita.com/3244/items/6000822f3a165e71c56d)
* 2/4: Nginx + Gunicorn + Django + Aurora (MySQL) の構成を図で説明してみる
* 3/4: [Nginx + Gunicorn + Django + Aurora (MySQL) の本番環境をAnsible Playbookで構成する](https://qiita.com/3244/items/0d2ae54ccaf72a3b3559)
* 4/4: [NginxとGunicornの接続をソケットからHTTPに変更した](https://qiita.com/3244/items/8cdcfae9d2ee52f2638d)