docker-composeでdjango環境を作ってみる(MariaDB + Nginx + uWSGI)

## はじめに

qiita初投稿です。
何か間違っているところがあったら、コメントしていただけると嬉しいです〜
2019/12/18に一部修正

* mac mojave
* docker-engine 19.03.5
* compose 1.24.1
* python3.8
* Django 3
* mariadb 10.1
* nginx 1.13

Pycharm使ってます。

## ディレクトリ


ディレクトリはこんな感じ

```
.
├── db
│   ├── data
│   └── sql
│       └── init.sql
├── docker-compose.yml
├── nginx
│   ├── conf
│   │   └── app_nginx.conf
│   ├── log
│   └── uwsgi_params
├── python
│   ├── Dockerfile
│   └── requirements.txt
├── src
│   └── project
│       ├── app
│       ├── project
│       └── manage.py
│       └── uwsgi.ini
└── static
```


## データベース

MariaDBを使います。
dbディレクトリのなかにdataフォルダと初期化用のinit.sql作成。

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

FLUSH PRIVILEGES;
```

githubにあげる時は、.gitigonreに入れるなどしてpassword等は見えないように。<br>
dockerfileは作らず、docker-compose.ymlの方に書きます。


## Nginx

画像等の静的ファイルをリバースプロキシサーバーであるnginxに飛ばします。

app_nginx.conf

```
upstream django {
    ip_hash;
    server python:8001;
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8000;
    # the domain name it will serve for
    server_name 127.0.0.1; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    location /static {
        alias /static;
    }


    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
    }
}

```

まずはアクセスがあったらnginxの8000番ポートで受け止め、静的ファイル static　にアクセスがあったときはリバースプロキシサーバーであるnginxのstaticに飛ばし、他は8001ポートのアプリケーションサーバ（uWSGI）に飛ばします。


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;
```

paramsはコピペでオッケ


## python

Dockerfile/python

```Dockerfile

# The first instruction is what image we want to base our container on
# We Use an official Python runtime as a parent image
FROM python:3.8-alpine
# The enviroment variable ensures that the python output is set straight
# to the terminal with out buffering it first
ENV PYTHONUNBUFFERED 1
ENV PYTHONIOENCODING utf-8
ENV APP_PATH  code
# Set the working directory to /code
WORKDIR /$APP_PATH
COPY ./requirements.txt /$APP_PATH
# Install any needed packages specified in requirements.txt
RUN apk add --no-cache --virtual .build-deps bash gcc musl-dev libffi-dev \
 g++ libgcc libstdc++ libxml2-dev libxslt-dev openssl-dev curl \
 && apk add --no-cache --virtual --update-cache\
 jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \
 mariadb-dev mariadb-connector-c-dev \
 && pip install cython && pip install -U pip && pip install -r requirements.txt
 # Make alpine lighter
RUN rm -rf /var/cache/apk/* && \
    rm -rf /tmp/*
```

apk add しているところで改行や区切り方次第でインストールエラー多発
動いたので現状これでよしということで、、、

```txt
Django==3.0
flake8==3.7.9
ipython==7.10.1
mysqlclient==1.4.6
Pillow==6.2.1
uWSGI==2.0.18
```

コードチェックしてくれるflake8とpythonのインタプリタを強力にしたipythonも入れておきます。
データベース接続はいくつもドライバーがありますが、公式推奨のmysqlclientを使います。
https://docs.djangoproject.com/ja/3.0/ref/databases/#mysql-db-api-drivers


## docker-compose.yml

```yaml

version: '3.4'
services:
  nginx:
    image: nginx:1.13
    container_name: app_nginx
    ports:
      - "8000:8000"
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./static:/static
      - ./nginx/log:/var/log/nginx
    depends_on:
      - python
  db:  # the same name as 'host' in DATABASES setting, otherwise Django won't find the database
    image: mariadb:10.1
    container_name: app_mariadb
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: app
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: root
      TZ: 'Asia/Tokyo'
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/sql:/docker-entrypoint-initdb.d
  python:
    build: ./python
    image: python_blog
    container_name: app_python
    command: uwsgi --ini /code/project/uwsgi.ini
    volumes:
      - ./src:/code
      - ./static:/static
    expose:
      - "8001"
    depends_on:
      - db 

```

サービス名のdbとdjangoのsettings.pyのdb設定のhostのdbを一致させる。


---

ここまでいったら
docker-compose.ymlのあるところで

```shell
$ docker-compose up -d --build
```

コンテナを立ち上げてみます。
nginxとpythonコンテナが立ち上がりません　嗚呼

```yml

command: uwsgi --ini /code/project/uwsgi.ini
```
uwsgi.iniがまだないのでdjangoプロジェクトを作り配置します。


```
$ docker-compose run --rm python django-admin.py startproject project
```

djangoプロジェクトを作成する。
pythonの部分はdocker-compose.ymlのサービス名
--rmをつけないとpythonコンテナがたくさんできてしまうのでrunさせるごとに消してく。

```ini
[uwsgi]
socket = :8001
module = project.wsgi
wsgi-file = /code/project/project/wsgi.py
logto = /code/project/project/uwsgi.log
chdir=/code/project
py-autoreload = 1
```

uwsgi.iniを配置して

```
$ docker-compose down
$ docker-compose up -d
$ docker ps -a
```
全コンテナが立ち上がっていると思われる


```shell
$ docker exec -it app_python /bin/bash
$ cd ./project/
$ python manage.py startapp app
```
python環境はalpine linuxで作っていて、alpineにbashは入っていないから /bin/sh　か /bin/ash。
今回はbashを入れてるのでbash
djangoのアプリを作る



## django内の設定

settings.py

```python

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app.apps.AppConfig',  # 追加


# 変更
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'db',
        'PORT': '3306',
        'OPTIONS': {
            'charset': 'utf8mb4',
        },
    }

#　お好みで変更
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'


STATIC_ROOT = './../../../static/'  # 本番環境用の配信用の大元
STATIC_URL = '/static/'  # 本番環境で使われるurl

# Custom Useer
AUTH_USER_MODEL = 'app.User'
```

migrateをする前に公式によるととりあえず、カスタムユーザー作れってあるから
カスタムユーザーを作る
https://docs.djangoproject.com/ja/3.0/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project

models.py

```python
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
	pass
```

admin.py

```python
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .models import User

admin.site.register(User, UserAdmin)
```

dockerコンテナ内で

```shell
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
```
http://localhost:8000/
djangoが立ち上がっているはず
サーバー内エラーが起きていたらdocker-compose up　し直してみる

migrateしたらdjango admin用のユーザーを作る
http://localhost:8000/admin/ 　djangoadminにアクセス
uwsgiを使っているのでcssが効いてないので

```shell

$ python manage.py collectstatic
```
django adminにもcssがきく

これで開発をできるようになるはず。。。

## 参考
[Docker-ComposeでPython3.6+NGINX+MariaDB10+uWSGIのDjango環境欲張りセット](https://qiita.com/NickelCreate/items/bed3dc9d088b57127ba7)