Djangoの環境をDocker化する（Docker + Django + Gunicorn + nginx）その１

大好きな`Python3`と`Django`で開発しようと考え、`Docker`を使って環境構築をしました。おおよそ**1ヶ月(ハマりましたorz)**ほどかかりました。そんな惨事を繰り返さないために記録しておきたいと思います。長くなりそうなので、`docker-compose.yml`ファイルを除く初期設定までを記載します。次回は、`docker-compose.yml`を主に記載します。

* 下記のリンクにて公開しています。    
    - [Dockerize Django Environment](https://github.com/amazipangu/docker-django)

------

## 環境の詳細

* OS
    * 下記の仮想化ツールを利用するため、OS差異による動作差分は発生しない想定です。
* 仮想化ツール
    * `Docker`
* 言語
    * `Python3`
* フレームワーク
    * `Django`
* WSGI
    * `Gunicorn`
* Webサーバ
    * `Nginx`
* データベース
    * `PostgreSQL`


依存関係は下記の更新により最新化済み（2021/07/11現在）

* [Updated dependencies](https://github.com/amazipangu/docker-django/commit/c0cf6efaa083dfe66e752ca728f12147610c92fc)

------

## 初期設定

1. プロジェクトディレクトリを作成して、その中に必要なディレクトリ構造とファイルを作成していきます。初期設定時のディレクトリ構造は以下の通りです。

    ```sh
    docker-django/
    ┣nginx
    ┃    ┗nginx.conf
    ┃    ┗mime.types 
    ┃
    ┣web
    ┃  ┗Dockerfile
    ┃  ┗Pipfile
    ┗docker-compose.yml
    ```

1. `Django`コンテナを作成するために`Dockerfile`を作成する。 

```Dockerfile:Dockerfile
FROM python:3.7-alpine
ENV PYTHONUNBUFFERED 1
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY Pipfile /usr/src/app/
RUN apk update \
  && apk add --virtual build-tools gcc python3-dev musl-dev \
  && apk add postgresql-dev
RUN pip install pipenv
RUN pipenv lock --pre
RUN pipenv install --system --dev
RUN pipenv install --system --deploy
RUN apk del build-tools
ADD . /usr/src/app/
```
  
  * `Django`は、`Python`のイメージから作成します。
  * `ENV PYTHONUNBUFFERED 1`は、python環境変数の１つです。
    * バイナリレイヤ下での標準出力とエラー出力を抑制します。（[PYTHONUNBUFFERED](https://docs.python.org/3.5/using/cmdline.html#envvar-PYTHONUNBUFFERED)）
  * アプリケーション用のディレクトリ(`/usr/src/app`)と静的ファイル用のディレクトリ（`/usr/src/app/static`）を作成します。
  * `WORKDIR /usr/src/app`で作業ディレクトリを指定します。以降の処理は指定されたディレクトリ下で実行されます。
  * `COPY Pipfile /usr/src/app/`で`/usr/src/app/`下に`Pipfile`を追加します。
  * 開発環境で必要なライブラリ（テスティングフレームワーク等）は、`Pipfile` に記載し、それらを`RUN pipenv install --system --dev`を実行してライブラリをインストールします。
  * 本番環境で必要なライブラリ（Django本体等）は、`Pipfile` に記載し、それらを`RUN pipenv install --system --deploy`を実行してライブラリをインストールします。


```txt:Pipfile
[dev-packages]
flake8 = ">=3.9.2"
pytest = ">=6.2.4"
pytest-django = ">=4.4.0"
pytest-sugar = ">=0.9.4"
pytest-factoryboy = ">=2.1.0"

[packages]
django = "<=3.2.5"
psycopg2 = "<=2.9.1"
gunicorn = "<=20.10"
```

3.`Nginx.conf`を編集する。

```txt:nginx.conf
worker_processes 1;

user nobody nogroup;
pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
    worker_connections 1024;
    accept_mutex off;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    access_log /tmp/nginx.access.log combined;
    sendfile on;

    upstream app_server {
        server web:8000 fail_timeout=0;
    }

    server {
        listen 80 default;
        client_max_body_size 4G;
        server_name localhost;

        keepalive_timeout 5;

        location /static/ {
               alias /usr/src/app/static/;
        }

        location / {
          try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_redirect off;
            proxy_pass http://app_server;
        }

　　　　　# エラー用の静的ファイルは、適宜変更する。
        error_page 500 502 503 504 /500.html;
        location = /500.html {
            root /path/to/app/current/public;
        }
    }
}
```

* `mime.types`ファイルの読み込みを行う。

   ```txt:mime.types
   types {
      text/html                             html htm shtml;
      text/css                              css;
      text/xml                              xml rss;
      image/gif                             gif;
      image/jpeg                            jpeg jpg;
      application/x-javascript              js;
      text/plain                            txt;

      ===== 以下、省略 =====
   }
   ```
* `upstream`ディレクティブにて、アクセスを振り分けるサーバを定義する。
* `location /static`ディレクティブにて、静的ファイルのパスを定義する。
* `location @proxy_to_app`ディレクティブにて、接続するアプリヶーションサーバを指定する。



次回は、`docker-compose.yml`を主に記載します。