Docker-composeでDjango開発環境構築+Nginx+uWSGI+MariaDB（macOS編）

# ローカル開発環境はDockerが便利

Python+Djangoの開発環境構築ではpyenvなどでバージョンを変えるなどできますが、チームで開発や別のマシンで開発の際にも構築するという時に面倒なので、仮想環境を構築するDockerを使って作りたいと思います。

Dockerとは、コンテナ技術というものを使ってミドルウェアやアプリを動かすプラットフォームです。VMwareなどの仮想マシンとはちょっと違います。とにかく便利なので使ってみたほうがよいです。wordpressなどのwebサーバーをローカルに構築するなども可能です。

参考：https://knowledge.sakura.ad.jp/13265/

また、macOSでの統合開発環境には[PyCharm](https://www.jetbrains.com/ja-jp/pycharm/)を使おうと思います。

# 環境
| ソフト | バージョン |
|---|---|
| OS  | macOS Catalina バージョン 10.15.4  |
| Docker desktop | 2.2.0.5 |
| Docker Engine | 19.03.8 |
| Docker Compose | 1.25.4 |
| Nginx  | 1.16.1  |
| uWSGI  | 2.0.18  |
| MariaDB | 10.4.12 |
| Python | 3.8.2 |
| Django | 3.0 |
| PyCharm | 2020.1 |

# Docker for mac のインストール
Dockerの公式サイトからDocker for macを[ダウンロード](https://hub.docker.com/editions/community/docker-ce-desktop-mac/)してインストールします。

Dockerにサインインする必要がありますので、こちらからアカウントを作成します。
https://hub.docker.com/

<img width="344" alt="スクリーンショット 2020-04-11 16.15.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434973/336e58f8-9760-7fd7-6398-5420e5c6cad8.png">

Docker for mac を起動するとIDとパスワードを求められるので、先ほど作成したアカウントでログインします。

Dockerのコマンドを使う際にはDocker for macを起動して、サインインしておく必要があります。

参考：https://docs.docker.com/docker-for-mac/

# Docker-composeで環境の構築
DockerはNginx、CentOS、MariaDB（MySQL）などのそれぞれのコンテナを立ち上げてコンテナ同士の接続をするという作業が必要です。これらの構成を記述して、複数のコンテナを一気に立ち上げてくれるのがDocker-composeです。

## プロジェクトの作成
プロジェクトを作成するためDocument配下などにプロジェクトのディレクトリを作成します。

## Docker-compose構成ファイルを作成
Dockerコンテナの構成をファイルに記述します。

```yml:docker-compose.yml
version: "3.7"

volumes:
  app.db.volume:

services:
  uwsgi:
    build: ./docker
    container_name: app.uwsgi
    command: uwsgi --socket :8081 --module userapp.wsgi --logto /code/userapp/uwsgi.log --py-autoreload 1
    volumes:
      - ./src:/code
      - ./static:/static
    expose:
      - "8081"
    depends_on:
      - db

  nginx:
    image: nginx:1.16
    container_name: app.nginx
    ports:
      - 8080:80
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./static:/static
    depends_on:
      - uwsgi

  db:
    image: mariadb:10.4
    container_name: app.db
    ports:
      - 3316:3306
    environment:
      MYSQL_ROOT_PASSWORD: dbtest5928
      TZ: 'Asia/Tokyo'
    volumes:
      - app.db.volume:/var/lib/mysql
      - ./sql:/docker-entrypoint-initdb.d
```
- `version: "3.7"`: docker-composeの記述バージョン
- `volumes:`: データベースの内容は保存します。

### uWSGIのコンテナの解説
```yml:docker-compose.yml
  uwsgi:
    build: ./docker
    container_name: app.uwsgi
    command: uwsgi --socket :8081 --module userapp.wsgi --logto /code/userapp/uwsgi.log --py-autoreload 1
    volumes:
      - ./src:/code
      - ./static:/static
    expose:
      - "8081"
    depends_on:
      - db
```
- `uwsgi:`: pythonをuWSGIで実行します。
- `command:`のコマンドでDjangoを起動します。
    - 8081ポートで待ち受けます。
    - `--module userapp.wsgi`は、Djangoで生成するプロジェクト名（userapp）と同じ名前にしてください。
    - ログファイルを設定
    - オートリロードを有効

### Nginxのコンテナの解説
```yml:docker-compose.yml
  nginx:
    image: nginx:1.16
    container_name: app.nginx
    ports:
      - 8080:80
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./static:/static
    depends_on:
      - uwsgi
```
- `ports:`: 外部からのアクセスは8080ポート、コンテナ内は80ポートでアクセス
- `volumes:`: Nginxのコンフィグファイルを`./nginx/conf.d`に後ほど作成します。

### MariaDBのコンテナの解説
```yml:docker-compose.yml
  db:
    image: mariadb:10.4
    container_name: app.db
    ports:
      - 3316:3306
    environment:
      MYSQL_ROOT_PASSWORD: dbtest5928
      TZ: 'Asia/Tokyo'
    volumes:
      - app.db.volume:/var/lib/mysql
      - ./sql:/docker-entrypoint-initdb.d
```
- `ports:`: 外部からのアクセスは3316ポート、コンテナ内は3306ポートでアクセス
- `environment:`: rootのパスワードとタイムゾーンを設定
- `volumes:`: データベース構築時の初期SQLを`./sql`に後ほど作成します。

## Dokcerfileの作成
Dockerのイメージを作る際に起動するスクリプトです。

```dockerfile:./docker/Dockerfile
FROM python:3.8
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/
```
- `FROM python:3.8`: pythonイメージをベースに起動します。
- コマンドラインへの出力バッファーの無効化です。時間差での表示などで意図しない結果になるためバッファー無効にした方がデバッグにも向いてます。
- `WORKDIR /code`: 実行ディレクトリ
- `RUN pip install -r requirements.txt`: Dockerに必要なライブラリを`requirements.txt`に記載してインストールします。

##requirements.txtを作成
Dockerの起動において、pythonの実行に必要なライブラリを記載します。

```text:./docker/requirements.txt
Django==3.0
uwsgi==2.0.18
PyMySQL==0.9
```
- `Django`は、フレームワーク
- `uwsgi`は、webでのpythonの実行サーバー
- `PyMySQL`は、pythonからMySQL（MariaDB）に接続するためのライブラリ

## MariaDBの初期スクリプト
DB操作のユーザーとデータベースを作成します。

| 項目 | 値 |
|:--|:--|
| User  | appuser  |
| Password  | apppass  |
| DB  | app  |

```sql:./sql/init.sql
CREATE DATABASE IF NOT EXISTS app CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER IF NOT EXISTS 'appuser'@'%' IDENTIFIED BY 'apppass';
GRANT ALL PRIVILEGES ON app.* TO 'appuser'@'%';

FLUSH PRIVILEGES;
```
- `app`データベースを作成し、`appuser`でアクセスできるように設定します。

## NginxのuWSGIコンフィグ

```zsh
% curl https://raw.githubusercontent.com/nginx/nginx/master/conf/uwsgi_params > ./nginx/uwsgi_params
```

uWSGIのNginx用コンフィグは[こちら](https://github.com/nginx/nginx/blob/master/conf/uwsgi_params)からダウンロードできます。


```conf:./nginx/uwsgi_params
uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;
```
uWSGIの実行に必要なパラメーターが記載されています。おまじないだと思ってそのまま使ってください。

##Nginxのコンフィグ
```conf:nginx/conf.d/app.conf
upstream django {
        server uwsgi:8081;
}       

server {
        listen 80;
        server_name localhost;
        charset utf-8;

        # Static file
        location /static {
                alias /static;
        }

        # Non media 
        location / {
                uwsgi_pass django;
                include /etc/nginx/uwsgi_params;
        }
}
```
- uWSGIのポートが8081なので、`upstream django`に設定します。
- `listen 80`: コンテナ内のアクセスは80ポート
- `location /static`: 画像、JavaScript、CSSはNginxで返します。
- `location /`: それ以外のアクセスはすべてuWSGIで処理します。

# プロジェクトの起動と初期構築
## Djangoプロジェクトの構築
```zsh
% docker-compose build
```
Docker-composeで定義されたイメージのダウンロードとビルドが行われます。

```zsh
% docker-compose run uwsgi django-admin.py startproject userapp . 
```
Djanagoのプロジェクトをビルドします。最後の"."を忘れないようにしてください。

```zsh
% tree
.
├── docker
│   ├── Dockerfile
│   └── requirements.txt
├── docker-compose.yml
├── nginx
│   ├── conf.d
│   │   └── app.conf
│   └── uwsgi_params
├── sql
│   └── init.sql
├── src
│   └── userapp
│       ├── manage.py
│       └── userapp
│           ├── __init__.py
│           ├── asgi.py
│           ├── settings.py
│           ├── urls.py
│           └── wsgi.py
└── static
```
Djangoプロジェクトが正常に構築されるプロジェクトディレクトリはこのようになります。

## Djangoの実行
```zsh
% docker-compose up -d
Starting app.db ... done
Starting app.uwsgi ... done
Starting app.nginx ... done
```
- `-d`オプションはデーモン（バックグラウンド）で実行です。

http://127.0.0.1:8080
にアクセスしてみます。
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434973/e2dec307-237e-2d89-ec18-9a7553cdc2e2.png)
このような画面が表示されれば成功です。

```zsh
% docker-compose down
Stopping app.nginx ... done
Stopping app.uwsgi ... done
Stopping app.db    ... done
Removing userapp_uwsgi_run_ec11989e36c3 ... done
Removing app.nginx                      ... done
Removing app.uwsgi                      ... done
Removing app.db                         ... done
Removing network userapp_default

```
終了するときは、このコマンドでダウンします。

```zsh
% docker-compose ps  
Name   Command   State   Ports
------------------------------
```
Dockerの稼働状況をハックするコマンドを実行して、何も表示されなければdown成功です。

# 統合開発環境PyCharmのインストール

Django、Pythonの開発にはJetBeans社から出ている[PyCharm](https://www.jetbrains.com/ja-jp/pycharm/)が便利です。Webアプリを開発するのであれば、JavascriptやCSS、HTMLも編集できる有料のProfessional版が便利ですが、最初は無料のCommunity版で十分です。

## PyCharmのダウンロードとインストール
PyCharmの公式サイトからソフトをダウンロードします。
[PyCharm公式サイト](https://www.jetbrains.com/ja-jp/pycharm/)

ダウンロードしたらアプリフォルダに入れてインストール完了です。

## プロジェクトの読み込み
[File] > [Open]を選択してプロジェクトのルートフォルダを選びます。

<img width="344" alt="スクリーンショット 2020-04-18 12.51.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434973/f1bc1ab4-d0c6-27d3-792d-f8e131308a28.png">
開くとこのような形でプロジェクトが表示されます。

## DockerのPythonコンテナとPyCharmの接続
PyCharmからDockerのPythonコンテナを利用して実行するためには、Professional版が必要です。
とりあえずライトに開発をするのであれば、PyCharmをエディタとして利用するということでよいかと思います。

以上です。

参考：
https://nmmmk.hatenablog.com/entry/2018/05/01/101126
https://qiita.com/NickelCreate/items/bed3dc9d088b57127ba7