DockerでDjangoの開発環境を構築！（Docker-compose/Django/postgreSQL/nginx）

再構築してみました！
[DockerでDjangoの開発環境を再構築！！！！](https://qiita.com/nokonoko_1203/items/e345f899ac9ac700d6a8)

# はじめに

チーム開発…環境構築がめっちゃめんどくさいっすよね…そんな時はDockerを使いましょう（雑）

この記事はqrunchにあげたものの再投稿になります！[元記事](https://nokonoko1203.qrunch.io/entries/FkptWClUcr0a5KoD)

チームでの開発に非常に便利なDockerとDocker-composeを利用してDjango/postgreSQL/gunicorn/nginxの開発環境を構築していきましょう！！

全体の流れはほぼ以下のサイトを参考にさせてもらっていますが、設定ファイル等のほぼ全行にコメントを挟むなど理解しやすいような記事にしたつもりです！
[Dockerizing Django with Postgres, Gunicorn, and Nginx](https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/)

結構長い記事になってしまったので淡々と進めます！

# この記事でやること
- DockerとDocker-composeのインストール
- pipenvのインストールと仮想環境構築
- Dockerコンテナの構築
  - Django
  - postgres
  - gunicorn
  - nginx
静的ファイルの処理

## DockerとDocker-composeのインストール

皆さんはご存知かと思いますが、Dockerは簡単に言えば（簡単にしか言えない）お手持ちのPCのなかに仮想的に別のOSを入れて動かせる上にその環境を丸ごとコピーして他の人に渡せるツールのことです！

詳しくは以下のページなどを参考ください！
[Docker入門（第一回）～Dockerとは何か、何が良いのか～](https://knowledge.sakura.ad.jp/13265/)

なにはともあれDockerをインストールしましょう！

### docker

[https://docs.docker.com/docker-for-mac/install/](https://docs.docker.com/docker-for-mac/install/)からインストール

### docker-compose

```
$ curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

$ chmod +x /usr/local/bin/docker-compose
```
### バージョンの確認

以下のコマンドでバージョンが表示されればインストール完了です！

```
$ docker --version
Docker version 18.09.2

$ docker-compose --version
docker-compose version 1.23.2
```

## Django用のディレクトリを作成

次にDjangoプロジェクトを作成するためのディレクトリを作成しましょう

```
<!-- プロジェクト用のディレクトリを作成（appディレクトリがdjangoプロジェクトのルートディレクトリ） -->
$ mkdir docker-demo-with-django && cd docker-demo-with-django
$ mkdir app && cd app
```

## pipenvでホスト側の環境構築

pipenvは最近開発されたvenvやvenv-virtuarenvなどと比較されるpythonの仮想環境構築ツールです

pipとvirtuarenvが合体した様な機能を持っており、`pipfile`および`Pipfile.lock`の2種類のファイルで仮想環境およびパッケージのバージョンを管理できる優れものです

DockerでDjangoの環境を構築する際には上記2ファイルをコピーしてpipenvを実行するだけで同様のpython開発環境を作成できますのでこれを使用していきましょう

`docker-demo-with-django/app/`にPipfileを用意して`packages`の欄に`Django = "==2.2.3"`を入れるのを忘れない様にしましょう  

`docker-demo-with-django/app/Pipfile`

```
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
Django = "==2.2.3"

[requires]
python_version = "3.7"
```

`pipfile`を作成したら同ディレクトリで以下のコマンドを入力しますy

```
<!-- pipenv本体のインストール -->
:app$ pip install pipenv

<!-- Pipfileから仮想環境構築 -->
:app$ pipenv install

<!-- 仮想環境に入る -->
:app$ pipenv shell

<!-- Djangoプロジェクトのスタート -->
(app) :app$ django-admin.py startproject django_demo .

<!-- モデルの内容をデータベースに適用 -->
(app) :app$ python manage.py migrate

<!-- 開発用サーバーの起動 -->
(app) :app$ python manage.py runserver
```

[http://localhost:8000/](http://localhost:8000/)にアクセスしてみてください。Djangoのwelcome screenが表示されるはずです

### 現在のディレクトリ構成
```
:docker-demo-with-django$ tree
.
└── app
    ├── Pipfile
    ├── Pipfile.lock
    ├── db.sqlite3
    ├── django_demo
    │ ├── __init__.py
    │ ├── settings.py
    │ ├── urls.py
    │ └── wsgi.py
    └── manage.py

```

## Dockerコンテナの構築

### Django

appディレクトリに以下の様な`Dockerfile`を追加します  
今回は最低限の環境を作るのが目的のため、公式からインストールするDockerイメージはすべて軽量な`alpine linux`を利用します  
これによってubuntu等のイメージと比較して1/10程度の容量で環境を構築できます

`docker-demo-with-django/app/Dockerfile`

```
# 公式からpython3.7 on alpine linuxイメージをpull
FROM python:3.7-alpine

# 作業ディレクトリを設定
WORKDIR /usr/src/app

# 環境変数を設定
# Pythonがpyc filesとdiscへ書き込むことを防ぐ
ENV PYTHONDONTWRITEBYTECODE 1
# Pythonが標準入出力をバッファリングすることを防ぐ
ENV PYTHONUNBUFFERED 1

# Pipenvをインストール
RUN pip install --upgrade pip \
&& pip install pipenv

# ホストのpipfileをコンテナの作業ディレクトリにコピー
COPY ./Pipfile /usr/src/app/Pipfile

# pipfileからパッケージをインストールしてDjango環境を構築
RUN pipenv install --skip-lock --system --dev

# ホストのカレントディレクトリ（現在はappディレクトリ）を作業ディレクトリにコピー
COPY . /usr/src/app/
```

次にプロジェクトのルート（`docker-demo-with-django`）に`docker-compose.yml`を追加

```
version: '3.7'

services:
    # サービス名は自由に設定
    django:
        # appディレクトリの中から`Dockerfile`を探す
        build: ./app
        # サービス起動後に入力されるコマンドを設定
        command: python manage.py runserver 0.0.0.0:8000
        # データを永続化させる場合の設定。`host:container`でパスを記載
        volumes:
            - ./app/:/usr/src/app/
        # 開放するポートを指定。`host:container`でポートを記載
        ports:
            - 8000:8000
        # 環境変数を指定
        environment:
            # 1ならデバックモード
            - DEBUG=1
　　　　# setting.pyに記載されているSECRET_KEYを記入
            - SECRET_KEY=hoge
```

djangoプロジェクトのsetting.pyを修正します。
修正項目は`SECRET_KEY`,`DEBUG`,`ALLOWED_HOSTS`の3項目

```
# 環境変数からSECRET_KEYを取得する設定
SECRET_KEY = os.environ.get('SECRET_KEY')

# 環境変数からDEBUGを取得。デフォルトはTrue（本番環境モード）
DEBUG = int(os.environ.get('DEBUG', default=0))

# 許可するホストを記載
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
```

修正が終わったら`docker-compose up -d --build`コマンドでビルドと起動を同時に行います  

`-d`オプションはバックグランドで起動することを意味します

[http://localhost:8000/](http://localhost:8000/)に接続してwelcome screenが表示されれば成功です

### Postgres

psotgresを追加するには`docker-compose.yml`に新しいサービスを追加します  
同時に、djangoサービスにデータベースの設定をする必要があります

```
version: '3.7'

services:
    # サービス名は自由に設定
    django:
        # appディレクトリの中から`Dockerfile`を探す
        build: ./app
        # サービス起動後に入力されるコマンドを設定
        command: python manage.py runserver 0.0.0.0:8000
        # データを永続化させる場合の設定。`host:container`でパスを記載
        volumes:
            - ./app/:/usr/src/app/
        # 開放するポートを指定。`host:container`でポートを記載
        ports:
            - 8000:8000
        # 環境変数を指定
        environment:
            # 1ならデバックモード
            - DEBUG=1
            - SECRET_KEY=hoge
            - DATABASE_ENGINE=django.db.backends.postgresql
            - DATABASE_DB=django_db
            - DATABASE_USER=django_db_user
            - DATABASE_PASSWORD=password1234
            - DATABASE_HOST=postgres
            - DATABASE_PORT=5432
        # 接続するサービスを指定
        depends_on:
            - postgres

    postgres:
        # 公式からイメージをpull
        image: postgres:11.4-alpine
        # データベースの永続化
        # ホストのディレクトリにマウントしない様に、先頭に`./`をつけない
        volumes:
            - postgres_data:/var/lib/postgresql/data
        # su権限を持つ、指定されたユーザーと同じ名前のデータベースを作成
        # valueはdjangoサービスで指定したものと同じ様に記載する
        environment:
            - POSTGRES_USER=django_db_user
            - POSTGRES_PASSWORD=password1234
            - POSTGRES_DB=django_db

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる
volumes:
    postgres_data:
```

次にsetting.pyの`DATABASES`の項目を書き換えます

```
DATABASES = {
    'default': {
        'ENGINE': os.environ.get('DATABASE_ENGINE', 'django.db.backends.sqlite3'),
        'NAME': os.environ.get('DATABASE_DB', os.path.join(BASE_DIR, 'db.sqlite3')),
        'USER': os.environ.get('DATABASE_USER', 'user'),
        'PASSWORD': os.environ.get('DATABASE_PASSWORD', 'password'),
        'HOST': os.environ.get('DATABASE_HOST', 'localhost'),
        'PORT': os.environ.get('DATABASE_PORT', '5432'),
    }
}
```

djangoからpostgresに接続するためにはドライバーを利用する必要があります  
今回はもっともメジャーなドライバーである`psycopg2`を利用するために`docker-demo-with-django/app/Dockerfile`を修正しましょう  
Dockerfileは以下の様になります  
alpine linuxのパッケージマネージャーである`apk`を利用しで依存関係をインストールした後に`pip`から`psycopg2`をインストールしましょう

```
# 公式からpython3.7 on alpine linuxイメージをpull
FROM python:3.7-alpine

# 作業ディレクトリを設定
WORKDIR /usr/src/app

# 環境変数を設定
# Pythonがpyc filesとdiscへ書き込むことを防ぐ
ENV PYTHONDONTWRITEBYTECODE 1
# Pythonが標準入出力をバッファリングすることを防ぐ
ENV PYTHONUNBUFFERED 1

# psycopg2のインストール
RUN apk update \
    && apk add --virtual build-deps gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2 \
    && apk del build-deps

# Pipenvをインストール
RUN pip install --upgrade pip \
&& pip install pipenv

# ホストのpipfileをコンテナの作業ディレクトリにコピー
COPY ./Pipfile /usr/src/app/Pipfile

# pipfileからパッケージをインストールしてDjango環境を構築
RUN pipenv install --skip-lock --system --dev

# ホストのカレントディレクトリ（現在はappディレクトリ）を作業ディレクトリにコピー
COPY . /usr/src/app/
```

先ほど起動していたコンテナ群を`docker-compose down -v`でいったん停止させて、再度`docker-compose up -d --build`を入力してコンテナを再起動させましょう  
`-v`オプションはボリュームの削除を表します

```
<!-- コンテナの停止 -->
$ docker-compose down -v

<!-- コンテナの起動 -->
$ docker-compose up -d --build

<!-- マイグレーション -->
<!-- $ docker-compose exec <service_name> python manage.py migrate --noinput -->
$ docker-compose exec django python manage.py migrate --noinput
```

何度か同じ様なことをやっていると稀にDjangoとpostgresが接続されず、postgresコンテナが停止します  
その際にはlogを確認してみましょう
おそらく`postgres_1  | initdb: directory "/var/lib/postgresql/data" exists but is not empty
`  
の様な記載が確認できると思いますのでホスト側の`docker-demo-with-django/postgres_data`を削除してあげましょう  

`docker-compose ps`コマンドで以下の様に両方のコンテナが起動中（StateがUp）なことを確認します

```
$ docker-compose ps
               Name                             Command               State           Ports         
----------------------------------------------------------------------------------------------------
docker-demo-with-django_django_1     python manage.py runserver ...   Up      0.0.0.0:8000->8000/tcp
docker-demo-with-django_postgres_1   docker-entrypoint.sh postgres    Up      5432/tcp 
```

次にデータベースに指定したデータベースが作成されていることも確認してください

```
$docker-compose exec postgres psql --username=django_db_user --dbname=django_db
psql (11.4)
Type "help" for help.

django_db=# \l
                                          List of databases
   Name    |     Owner      | Encoding |  Collate   |   Ctype    |         Access privileges         
-----------+----------------+----------+------------+------------+-----------------------------------
 django_db | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 | 
 postgres  | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 | 
 template0 | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 | =c/django_db_user                +
           |                |          |            |            | django_db_user=CTc/django_db_user
 template1 | django_db_user | UTF8     | en_US.utf8 | en_US.utf8 | =c/django_db_user                +
           |                |          |            |            | django_db_user=CTc/django_db_user
(4 rows)

django_db=# \dt
                      List of relations
 Schema |            Name            | Type  |     Owner      
--------+----------------------------+-------+----------------
 public | auth_group                 | table | django_db_user
 public | auth_group_permissions     | table | django_db_user
 public | auth_permission            | table | django_db_user
 public | auth_user                  | table | django_db_user
 public | auth_user_groups           | table | django_db_user
 public | auth_user_user_permissions | table | django_db_user
 public | django_admin_log           | table | django_db_user
 public | django_content_type        | table | django_db_user
 public | django_migrations          | table | django_db_user
 public | django_session             | table | django_db_user
(10 rows)

django_db=# \q
```

確認できたら、postgresへの接続を確認した後にマイグレーションを自動で行うために`entrypoint.sh`をappディレクトリに追加します

```
#!/bin/sh

if [ "$DATABASE" = "postgres" ]
then
    echo "Waiting for postgres..."

    while ! nc -z $DATABASE_HOST $DATABASE_PORT; do
      sleep 0.1
    done

    echo "PostgreSQL started"
fi

python manage.py flush --no-input
python manage.py migrate

exec "$@"
```

追加後に`chmod +x app/entrypoint.sh`コマンドで実行権限を付与します  
最後に`entrypoint.sh`を実行するためにDockerfileを修正し、環境変数`$DATABASE`を追加します

```
# 公式からpython3.7 on alpine linuxイメージをpull
FROM python:3.7-alpine

# 作業ディレクトリを設定
WORKDIR /usr/src/app

# 環境変数を設定
# Pythonがpyc filesとdiscへ書き込むことを防ぐ
ENV PYTHONDONTWRITEBYTECODE 1
# Pythonが標準入出力をバッファリングすることを防ぐ
ENV PYTHONUNBUFFERED 1

# psycopg2のインストール
RUN apk update \
    && apk add --virtual build-deps gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2 \
    && apk del build-deps

# Pipenvをインストール
RUN pip install --upgrade pip \
&& pip install pipenv

# ホストのpipfileをコンテナの作業ディレクトリにコピー
COPY ./Pipfile /usr/src/app/Pipfile

# pipfileからパッケージをインストールしてDjango環境を構築
RUN pipenv install --skip-lock --system --dev

# entrypoint.shをコピー
COPY ./entrypoint.sh /usr/src/app/entrypoint.sh

# ホストのカレントディレクトリ（現在はappディレクトリ）を作業ディレクトリにコピー
COPY . /usr/src/app/

# entrypoint.shを実行
ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
```

少し待って起動完了後に[http://localhost:8000/](http://localhost:8000/)に接続できれば完了です

ひとまずこれで個人開発環境は整いました  
しかし本番環境の場合、環境変数を非公開にしなければいけません。また、`python manage.py runserver`では機能が不足していたり、セキュリティ面で問題があるため別の方法でサーバーを起動させる必要もあります。
そのためにアプリケーションとWebサーバーのインターフェイスである`gunicorn`（WSGIサーバー）のインストールや`env_file`の設定、静的ファイルを処理するために`gunicorn`のリバースプロキシとして動作する`nginx`の設定などを行わなければなりません  
次はそれらを行なっていきます

### Gunicornのインストールと本番用設定ファイルの作成

pipfileにgunicornを追記しましょう

```
[[source]]

url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"


[packages]

django = "==2.2"
gunicorn= "==19.9.0"


[dev-packages]



[requires]

python_version = "3.7"
```

さらに`docker-compose.yml`と同じディレクトリに`docker-compose.prod.yml`を追記し、本番環境用の設定を記載します

```
version: '3.7'

services:
    # サービス名は自由に設定
    django:
        # appディレクトリの中から`Dockerfile`を探す
        build: ./app
        # サービス起動後に入力されるコマンドを設定
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000
        # データを永続化させる場合の設定。`host:container`でパスを記載
        volumes:
            - ./app/:/usr/src/app/
        # 開放するポートを指定。`host:container`でポートを記載
        ports:
            - 8000:8000
        # 環境変数を指定
        env_file: .env
        # 接続するサービスを指定
        depends_on:
            - postgres

    postgres:
        # 公式からイメージをpull
        image: postgres:11.4-alpine
        # データベースの永続化
        volumes:
            - postgres_data:/var/lib/postgresql/data
        env_file: .env.db

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる
volumes:
  postgres_data:
```

開発環境の設定と比較すると`environment:`が`env_file:`に変わっています。これにより本番環境用の設定を直接ymlに書かなくてよくなります  
また、djangoサービスの`command:`に`gunicorn`の起動に関するコマンドを指定して、`runserver`ではなく`gunicorn`を起動させています

`env_file`は`docker-compose.prod.yml`と同様のディレクトリに置いて、以下の様に記載しましょう  
この際に、`.env`の`DEBAG=0`にするのを忘れない様にしましょう（`DEBAG=0`でデバッグモードをオフ）

`/docker-demo-with-django/.env`

```
DEBUG=0
SECRET_KEY=hoge
DATABASE_ENGINE=django.db.backends.postgresql
DATABASE_DB=django_db
DATABASE_USER=django_db_user
DATABASE_PASSWORD=password1234
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE=postgres
```

`/docker-demo-with-django/.env.db`

```
POSTGRES_USER=django_db_user
POSTGRES_PASSWORD=password1234
POSTGRES_DB=django_db
```

加えて、現段階ではコンテナが起動するたびにmigrateが実行されてしまいますので`entrypoint.sh`も本番環境用に`entrypoint.prod.sh`を作成しましょう

`/docker-demo-with-django/app/entrypoint.prod.sh`

```
#!/bin/sh

if [ "$DATABASE" = "postgres" ]
then
    echo "Waiting for postgres..."

    while ! nc -z $DATABASE_HOST $DATABASE_PORT; do
      sleep 0.1
    done

    echo "PostgreSQL started"
fi

exec "$@"
```

Dockerfileも本番用のものを作成します

`/docker-demo-with-django/app/Dockerfile.prod`

```
# 公式からpython3.7 on alpine linuxイメージをpull
FROM python:3.7-alpine

# 作業ディレクトリを設定
WORKDIR /usr/src/app

# 環境変数を設定
# Pythonがpyc filesとdiscへ書き込むことを防ぐ
ENV PYTHONDONTWRITEBYTECODE 1
# Pythonが標準入出力をバッファリングすることを防ぐ
ENV PYTHONUNBUFFERED 1

# psycopg2のインストール
RUN apk update \
    && apk add --virtual build-deps gcc python3-dev musl-dev \
    && apk add postgresql-dev \
    && pip install psycopg2 \
    && apk del build-deps

# Pipenvをインストール
RUN pip install --upgrade pip \
&& pip install pipenv

# ホストのpipfileをコンテナの作業ディレクトリにコピー
COPY ./Pipfile /usr/src/app/Pipfile

# pipfileからパッケージをインストールしてDjango環境を構築
RUN pipenv install --skip-lock --system --dev

# entrypoint.shをコピー
COPY ./entrypoint.prod.sh /usr/src/app/entrypoint.prod.sh

# ホストのカレントディレクトリ（現在はappディレクトリ）を作業ディレクトリにコピー
COPY . /usr/src/app/

# entrypoint.shを実行
ENTRYPOINT ["/usr/src/app/entrypoint.prod.sh"]
```

当然、`docker-compose.prod.yml`も本番用のファイルを読み込む様に書き換えます

```
version: '3.7'

services:
    # サービス名は自由に設定
    django:
        build:
            # 読み込むファイル名が`Dockerfile`ではない場合contextに相対パス、dockerfileにファイル名を記載
            context: ./app
            dockerfile: Dockerfile.prod
        # サービス起動後に入力されるコマンドを設定
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000
        # データを永続化させる場合の設定。`host:container`でパスを記載
        volumes:
            - ./app/:/usr/src/app/
        # 開放するポートを指定。`host:container`でポートを記載
        ports:
            - 8000:8000
        # 環境変数を指定
        env_file: .env
        # 接続するサービスを指定
        depends_on:
            - postgres

    postgres:
        # 公式からイメージをpull
        image: postgres:11.4-alpine
        # データベースの永続化
        volumes:
            - postgres_data:/var/lib/postgresql/data
        env_file: .env.db

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる
volumes:
    postgres_data:
```

設定が終わったら再度コンテナを起動させてみましょう

```
$ docker-compose down -v

<!-- -fオプションでdocker-compose.prod.ymlを指定 -->
$ docker-compose -f docker-compose.prod.yml up -d --build

<!-- entrypoint.prod.shではmigrateをしていないので手動で実行 -->
$ docker-compose -f docker-compose.prod.yml exec django python manage.py migrate --noinput
```

起動したら[http://localhost:8000/admin](http://localhost:8000/admin)にアクセスしてみましょう  
接続されてdjangoの管理用のログイン画面が表示されると思いますが、静的ファイル（CSS等）が表示されていないはずです  
これはデバッグモードをオフにしたため静的ファイルが読み込まれなくなっています  
また、設定通りGunicornが起動しましたがGunicorn静的ファイルの配信に対応しておらずアプリケーション（今回であればdjango）を提供するだけですので、上記2つの設定を修正しなければ静的ファイルの配信はできません 
具体的には`python manage.py collectstatic`で静的ファイルを1箇所に集めてそれを読み込む様にすることと、nginxなどのwebサーバーをgunicornのリバースプロキシとして利用することです  

まずはnginxをサービスに追加しましょう

### nginxを追加

プロジェクトのルート(`/docker-demo-with-django/`)にnginxディレクトリを作成し、その中に`Dockerfile`と`nginx.conf`を追加します

`/docker-demo-with-django/nginx/Dockerfile`

```
FROM nginx:1.15.12-alpine

# デフォルトのconfを消して、別の設定を追加
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
```

`/docker-demo-with-django/nginx/nginx.conf`

```
upstream config {
    # コンテナのサービス名を指定すると名前解決してくれる
    server django:8000;
}

server {
    # 80ポートで待ち受け
    listen 80;

    location / {
        proxy_pass http://config;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

}
```

それから`docker-compose.prod.yml`にnginxを追記します

```
version: '3.7'

services:
    # サービス名は自由に設定
    django:
        build:
            # 読み込むファイル名が`Dockerfile`ではない場合contextに相対パス、dockerfileにファイル名を記載
            context: ./app
            dockerfile: Dockerfile.prod
        # サービス起動後に入力されるコマンドを設定
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000
        # データを永続化させる場合の設定。`host:container`でパスを記載
        volumes:
            - ./app/:/usr/src/app/
        # 指定されたポートは接続されたサービスからアクセス可能
        expose:
            - 8000
        # 環境変数を指定
        env_file: .env
        # 接続するサービスを指定
        depends_on:
            - postgres

    postgres:
        # 公式からイメージをpull
        image: postgres:11.4-alpine
        # データベースの永続化
        volumes:
            - postgres_data:/var/lib/postgresql/data
        env_file: .env.db

    nginx:
        build: ./nginx
        ports:
            - 1337:80
        depends_on:
            - django

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる
volumes:
    postgres_data:
```

djangoサービスにホストOSから直接リクエストすることはなくなるので`ports:`を`expose:`に変えましょう  
これで指定されたポートはホストOSには公開されませんが、リンクされたサービスからは接続できる様になります  

先ほどと同様にサービスを再起動します

```
$ docker-compose -f docker-compose.prod.yml down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
$ docker-compose -f docker-compose.prod.yml exec djnago python manage.py migrate --noinput
```

[http://localhost:1337/admin/](http://localhost:1337/admin/)に接続してみましょう。管理画面が表示されるはずです

これでnginxとの接続が完了しました。現段階でのディレクトリ構成は以下の通りです

```
$tree
.
├── app
│ ├── Dockerfile
│ ├── Dockerfile.prod
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── django_demo
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── entrypoint.prod.sh
│ ├── entrypoint.sh
│ └── manage.py
├── docker-compose.prod.yml
├── docker-compose.yml
└── nginx
    ├── Dockerfile
    └── nginx.conf
```

### 静的ファイルの処理

次に静的ファイルを処理する設定です。djangoプロジェクトの`setting.py`を末尾を修正し、`entrypoint.sh`に追記します

`/docker-demo-with-django/app/django_demo/settings.py`

```
STATIC_URL = '/staticfiles/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
```

`/docker-demo-with-django/app/entrypoint.sh`

```
#!/bin/sh

if [ "$DATABASE" = "postgres" ]
then
    echo "Waiting for postgres..."

    while ! nc -z $DATABASE_HOST $DATABASE_PORT; do
      sleep 0.1
    done

    echo "PostgreSQL started"
fi

python manage.py flush --no-input
python manage.py migrate
python manage.py collectstatic --no-input --clear

exec "$@"
```

これで`python manage.py collectstatic`によって静的ファイルがSTATIC_ROOTで指定したパスに集められ、さらにそこに`staticfiles`ディレクトリに存在する静的ファイルを配信する様になります  

次に、`docker-compose.prod.yml`のdjangoとnginxのボリュームに同じものを設定し、nginxコンテナにdjangoプロジェクトを接続したのちに静的ファイルのリクエストを`staticfiles`ディレクトリにルーティングする様に設定しましょう  

`/docker-demo-with-django/docker-compose.prod.yml`

```
version: '3.7'

services:
    # サービス名は自由に設定
    django:
        build:
            # 読み込むファイル名が`Dockerfile`ではない場合contextに相対パス、dockerfileにファイル名を記載
            context: ./app
            dockerfile: Dockerfile.prod
        # サービス起動後に入力されるコマンドを設定
        command: gunicorn django_demo.wsgi:application --bind 0.0.0.0:8000
        # データを永続化させる場合の設定。`host:container`でパスを記載
        volumes:
            - static_volume:/usr/src/app/staticfiles
        # 指定されたポートは接続されたサービスからアクセス可能
        expose:
            - 8000
        # 環境変数を指定
        env_file: .env
        # 接続するサービスを指定
        depends_on:
            - postgres

    postgres:
        # 公式からイメージをpull
        image: postgres:11.4-alpine
        # データベースの永続化
        volumes:
            - postgres_data:/var/lib/postgresql/data
        env_file: .env.db

    nginx:
        build: ./nginx
        volumes:
            - static_volume:/usr/src/app/staticfiles
        ports:
            - 1337:80
        depends_on:
            - django

# トップレベルに書く「名前付きvolumes」は複数サービスから参照できる
volumes:
    postgres_data:
    static_volume:
```

`/docker-demo-with-django/nginx/nginx.conf`

```
upstream config {
    # コンテナのサービス名を指定すると名前解決してくれる
    server django:8000;
}

server {
    # 80ポートで待ち受け
    listen 80;

    location / {
        proxy_pass http://config;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

    # 静的ファイルの要求をstaticfilesにルーティング
    location /staticfiles/ {
        alias /usr/src/app/staticfiles/;
    }

}
```

これですべての設定が完了しました！  
改めてコンテナを起動してみましょう！

```
$ docker-compose -f docker-compose.prod.yml down -v
$ docker-compose -f docker-compose.prod.yml up -d --build
$ docker-compose -f docker-compose.prod.yml exec django python manage.py migrate --noinput
$ docker-compose -f docker-compose.prod.yml exec django python manage.py collectstatic --no-input --clear
```

起動を確認したら[http://localhost:1337/admin](http://localhost:1337/admin)に接続してみてください。管理画面にCSSが設定されているはずです！あとはお好きな様にDjangoプロジェクトを改修してください！  

お疲れ様でした！！