素人基盤エンジニアがDockerでDjangoを触るシリーズ①：DockerでDjangoを作る

# TL;DR
今回はdocker composeのdjango[クイックスタート](http://docs.docker.jp/compose/django.html)をやりつつdjangoと戯れる
[結論]
docker-composeだと`docker-compose run container django-admin.py startproject`でローカルにプロジェクトディレクトリを展開して、`volumes: .:WORKDIR/`でdockerコンテナにマウントしてプロジェクトを起動する。

# 前提
- docker導入済み
- docker-compose導入済み

# ディレクトリ構造(自分で用意する構成)
WORKDIR
∟Dockerfile
∟requirements.txt
∟docker-compose.yaml

# 構築
### ①Dockerfileを書く
#### Dockerfile
```Docker:Dockerfile
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip3 install -r requirements.txt
ADD . /code/
```

#### よくわかる解説
```Docker
FROM python:3.7
```
pythonのコンテナイメージをDocker hubから引っ張ってくる
元のpythonのversionは2.7になってたので、3.7に直す

```Docker
ENV PYTHONUNBUFFERED 1
```
標準出力STDOUT,STDERRのバッファを無効化するものらしい
なんかバッファ有効にしてるとsleep()とかの挙動がおかしくなるとかならんとか

```Docker
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
```
codeというフォルダを作ってワーキングディレクトリに指定してそこに後述するrequirements.txtを置く(ホスト側のファイルを指定の場所に置く）

```Docker
RUN pip3 install -r requirements.txt
```
後述するrequirements.txtに書いたものをpipでインストールする。
python3系への変更に伴いpipも3を使う

```Docker
ADD . /code/
```
カレントディレクトリ以下をコンテナ上のフォルダに置く

#### requirements.txt
```shell:requirements.txt
Django>=2.0,<3.0
psycopg2>=2.7,<3.0
```
#### よくわかる解説
```shell
Django>=2.0,<3.0
```
今回の主旨
bottleやflaskと同じく、pythonのwebフレームワーク
どっかで見たのは、
django　>　flask　>　bottle
大規模向け　⇔　小規模向け
らしい。眉唾。
元のテキストにはバージョン指定が無かったので追加

````shell
psycopg2>=2.7,<3.0
````
PythonのPostgreSQLドライバらしい
今回はDBとしてpostgresのコンテナを上げるのでインストール
元のテキストにはバージョン指定が無かったので追加

### ②docker-compose.yamlを書く
#### docker-compose.yaml
```Docker:docker-compose.yaml
version: '3'
services:
  db:
    image: postgres
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db
```
#### よくわかる解説
```Docker
version: '3'
```
docker-composeのファイルフォーマットのバージョン。
最新が3らしいし最近3をよく見る(2ってオワコンなんですか？)が深入りしないと決めている。違いについては各自調べて優しく教えてください。

```Docker
services:
  db:
    image: postgres
```
一つめのDockerコンテナ。
image句は、Dockerレポジトリから指定のコンテナイメージを引っ張ってきて起動する。
今回はPostgreSQL。しかしこのチュートリアルは意地でもバージョン指定しないな。。
バージョンを指定したいときは後ろにタグをつけてあげる。「postgres:11.2」とか。

```Docker
  web:
    build: .
```
二つめのDockerコンテナ。
image句がない場合、build句で指定したディレクトリにあるDockerfileをビルドして起動する。

```Docker
    command: python manage.py runserver 0.0.0.0:8000
```
コンテナの起動時に実行されるコマンド。いわゆるDockerfileで言うところのCMD句。
CMD句が`docker run -it [container] [command]`のcommandに上書きされてしまうように、docker-compose run でコマンドを指定した場合、ここに書いた命令は上書きされる。
コマンドは、djangoのwebアプリケーションを起動させるコマンド。localhost向けの8000番で待ち受ける。

```Docker
    volumes:
      - .:/code
```
ホストディレクトリをコンテナ上の/codeディレクトリにマウントする。
ググってもググってもこのパターンしか出ないが、こういうのってk8sで実際に構築するときはどうするんでしょうね。DockerfileにCOPYとか書くからいいんですかね。

```Docker
    ports:
      - "8000:8000"
```
ホストの8000ポートをコンテナの8000ポートにフォワーディングする。

```Docker
    depends_on:
      - db
```
dbコンテナが起動してから、このコンテナを起動する。DBが絡む時とかは起動順を気にしてあげなくてはいけない。

### ③docker-compose runの実行
```shell
$ docker-compose run web django-admin.py startproject composeexample .
```
docker-compose runは、デフォルトでdocker runの-itオプションがつくらしい。
つまり、webコンテナを立ち上げて`django-admin.py startproject composeexample .`を実行しているということ。
django-admin startproject [project名] [展開先のディレクトリパス]は、djangoでプロジェクトを作るときのコマンドらしく、テンプレートが指定したディレクトリパス上に展開される。
ここでは先ほど、/codeにホストディレクトリをマウント指定したので、ホストディレクトリ上に展開される。
上記実行後のディレクトリ構成は下記のような感じになるはず。
WORKDIR
∟composeexample
　∟\_\_init\_\_.py
　∟settings.py
　∟urls.py
　∟wsgi.py
∟Dockerfile
∟requirements.txt
∟manage.py
∟docker-compose.yaml

ちなみにlinuxマシンで実行した場合はフォルダの権限がrootになるらしいので、下記を実行する。

```shell
sudo chown -R $USER:$USER .
```
postgreSQLと接続するために、settings.pyの`DATABASES =` 部分を書き換える。

### ④setting.pyの編集

```shell:settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}
```

### ⑤docker-compose upによる実行　…と紆余曲折

```shell
docker-compose up
```
いざ実行。
…動かんじゃないか。ググると同じエラーに遭遇した人が。
なんかDB名とかUSER名とかをdb側の環境変数で指定しないといけないみたい。
あとついでにポート指定しなくてもエラーが出た。

```shell:docker-compose.yaml
version: '3'
services:
  db:
    image: postgres
    ports:
      - "5432"
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db
```

```shell
docker-compose up
```
今度こそ。

```shell
web_1  | Watching for file changes with StatReloader
web_1  | Performing system checks...
web_1  |
web_1  | System check identified no issues (0 silenced).
web_1  |
web_1  | You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
web_1  | Run 'python manage.py migrate' to apply them.
web_1  | April 22, 2020 - 08:14:48
web_1  | Django version 2.2.12, using settings 'composeexample.settings'
web_1  | Starting development server at http://0.0.0.0:8000/
web_1  | Quit the server with CONTROL-C.
```
通った。実はこの作業EC2上でやっていたので、`http://server_ip:8000`にアクセスしてみる。
![django_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369619/3bc87ee6-c0cc-8cb4-90bf-4f8c921465e6.png)

ほう。。
バージョン1.10以降のdjangoではアクセス許可をsetting.pyに書いてやらねばならぬらしい。

```python:setting.py
ALLOWED_HOSTS = ["*"]
```
※本番環境でアスタリスクはやらないように。

```shell
docker-compose up
```
三度目の正直。

![django_success.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369619/7f1d31ac-9aa4-4e1f-c631-95237d7d96c9.png)
ああ。。やっとロケットが打ちあがった。
俺たちの戦いはまだまだこれからだ！

[つづく](https://qiita.com/LittleGreenMen/items/604f5689a506e67e6eb1)