Django本番環境をNginx+GunicornでDocker上に構築

Djangoのrunserverコマンドで動かすサーバーは、あくまで簡易的な開発用サーバーです。本番運用する場合にはセキュリティやパフォーマンスの観点からWebサーバー（リバースプロキシ）とApplicationサーバー（WSGIサーバー）を別途構築する必要があります。
今回、WebサーバーにはNginxを、アプリケーションサーバーにはGunicornを用いてDocker上に構築します。

# 環境

| ソフト | バージョン |
| ---- | ---- |
| OS | MacOS Catalina 10.15.7 |
| docker engine | 20.10.5 |
| docker compose | 1.29.0 |
| Django | 3.2.5 |
| Gruncorn |20.1.0 |
| Nginx | 1.17.7 |



# ディレクトリ構成
```
.
├── app           # Djangoアプリ
├── config        # Django設定関係
│   ├── local_settings.py
│   ├── settings.py
│ 
├── Dockerfile
├── docker-compose.yml
├── docker.env
├── gunicorn.conf
├── manage.py
└── requirements.txt
```

# Django設定ファイル
開発環境用にlocal_settings.pyファイルを作成し、'SECRET_KEY'、'DEBUG'、'ALLOWED_HOSTS'のデフォルト設定はそちらに移行。
開発用サーバーを起動する際には、settingsオプションで'local_settings.py'ファイルを指定する。


``` config/local_settings.py
from .settings import *

SECRET_KEY = 'XXXXX'
DEBUG = True
ALLOWED_HOSTS = []

# 開発用サーバ起動コマンド
# python manage.py runserver --settings config.local_settings
```
settings.pyファイルには本番環境設定を記述する。'SECRET_KEY'、'ALLOWED_HOSTS'は環境変数からの引用とし、'DEBUG'はFalseにする。

``` config/settings.py
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
DEBUG = False
ALLOWED_HOSTS = list(os.environ.get('ALLOWED_HOSTS','localhost').split(' '))

STATIC_URL = '/static/'
STATICFILES_DIRS = [str(BASE_DIR / 'static')]
STATIC_ROOT = str(BASE_DIR / 'static_root')
```

# Dockerファイルの設定

Dockerコンテナ内にpython環境を構築し、requirements.txtで定義したDjangoとGunicornをインストール。
gunicornコマンドでGunicornを起動し、config/wisg.pyを読み込み、UNIXドメインソケットをバインドする。

``` .dockerfile:Dockerfile
FROM python:3.9.6-alpine

WORKDIR /usr/src/app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt
RUN mkdir -p /var/run/gunicorn

CMD ["gunicorn", "config.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]
```

``` requirements.txt
Django==3.2.5
gunicorn==20.1.0
```


# Docker Composeファイルの設定
gunicornイメージはDockerファイルからbuildし、nginxイメージはDocker Hubからダウンロードする。
gunicornコンテナ、nginxコンテナの双方でgunicronボリュームにマウントし、UNIXソケット通信を行う。
コンテナ内のnginx設定ファイル（default.conf）をホスト側で作成したgunicorn.confファイルにマウントする。
Djangoアプリの'DJANGO_SECRET_KEY'と'ALLOWED_HOSTS'は環境変数として設定する。

``` docker-compose.yml
version: '3'

services:
  gunicorn:
    build: .
    image: gunicorn:20.1.0
    container_name: gunicorn
    volumes:
      - .:/usr/src/app/
      - gunicorn:/var/run/gunicorn
    env_file: docker.env
    stdin_open: true
    tty: true

  nginx:
    image: nginx:1.17.7
    container_name: nginx
    depends_on:
      - gunicorn
    ports:
      - "80:80"
    volumes:
      - ./gunicorn.conf:/etc/nginx/conf.d/default.conf
      - ./static_root:/var/www/mysite/static
      - gunicorn:/var/run/gunicorn
volumes:
  gunicorn:
    driver: local
```

``` gunicorn.conf
upstream gunicorn-django {
    server unix:///var/run/gunicorn/gunicorn.sock;
}
server {
    listen 80;
    server_name localhost;
    location / {
        try_files $uri @gunicorn;
    }
    location /static/ {
        root /var/www/mysite;
    }
    location @gunicorn {
        proxy_pass http://gunicorn-django;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

``` docker.env
DJANGO_SECRET_KEY='XXXXX'
ALLOWED_HOSTS='example.com'
# 開発環境の場合は、ALLOWED_HOSTS='localhost 127.0.0.1'
```

# 起動コマンド
docker-compose upコマンドでコンテナを生成・起動する。
`localhost`にアクセスし画面が出ればOK

```
$ docker-compose up -d
```
![スクリーンショット 2021-07-24 9.01.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128611/2b20b8eb-c7a1-f98d-d9a6-36a2f7c6b13d.png)
![スクリーンショット 2021-07-24 9.02.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128611/cdee16b3-e700-56c8-186c-5ebb66f7d8bc.png)

# 参考
 - [DockerでPythonのWebアプリケーションをNginx + Gunicorn で起動する](https://qiita.com/str416yb/items/7324b99b9f05b9089b80)