helloworlds

not a noun, it's a verb

【Docker】Django + REST framework + mysql with docker-compose

今回は、DjangoのREST frameworkのローカル環境に挑戦してみました。

初めてPythonフレームワークを触るので、浅はかなところもあるかと思いますがご了承下さい!

ゴールは、ブランチでこれが表示されるところまでいきます。

f:id:o21o21:20181119195642p:plain

docker-composeを使用するので、インストールしてない方はインストールしましょう。 この記事ではインストールの説明は割愛します。(「docker for mac インストール」で検索すれば出てくると思います。)

さて、さっそく取り掛かっていきます。

※ 私の環境は、macOS

作業場作成

適当に今回のプロジェクトを作成するディレクトリに移動します。

$ cd /path/to/project
or
$ mkdir project_name

任意ですが、一応README.mdを作成。

初期データ、ディレクトリ作成

今回、DB(mysql)と接続するために、初期データを置いておく必要があります。

$ mkdir mysql
$ cd mysql
$ mkdir init

上記を作成して、mysql/init/というディレクトリを作成します。 このディレクトリ配下に、.sqlをファイルを置いて置きます。

※ この.sqlファイルですが、既存の運用で使用されているDBのdumpファイルでも可能です。 ちょっと試したいというかたは、テーブルを作成するsql文などを適当に記述してもOKです。

ここまでのプロジェクト構成は以下です。

<project_name>
├── README.md
├── mysql
│   └── init
│      └── test.sql

主要なファイルを作成

次にDockerfiledocker-compose.ymlをプロジェクトディレクトリで作成します。

$ touch Dockerfile
$ touch docker-compose.yml

重要なのは、この2つのファイルの記述です。

  • Dockerfile
FROM python:3.5

ENV PYTHONUNBUFFERED 1

RUN mkdir /code

WORKDIR /code

# install Libraries
COPY requirements.txt /code/
RUN pip install -r requirements.txt

# to be consistent versions of between "Django and PyMySQL"
RUN pip install -U PyMySQL

ADD . /code/
  • docker-compose.yml
version: '3.0'
services:
  db:
    image: "mysql:latest"
    container_name: <container_name>
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_DATABASE: <db_name>
      MYSQL_ROOT_PASSWORD: <db_root_pwd>
      MYSQL_USER: <db_user>
      MYSQL_PASSWORD: <db_pwd>
    ports:
      - 3306:3306
    volumes:
      - type: bind
        # source: ./docker/db/init.sql
        source: ./mysql/init/<file_name>.sql
        target: /docker-entrypoint-initdb.d/<file_name>.sql
      - type: bind
        source: /tmp/docker/tmpdata
        target: /tmp/tmpdata
  web:
    container_name: <container_name>
    build: .
    command: python manage.py runserver 0.0.0.0:8080
    volumes:
      - .:/code
    ports:
      - 8080:8080
    environment:
      DJANGO_SECRET_KEY: $DJANGO_SECRET_KEY
      DJANGO_DEBUBG: $DJANGO_DEBUBG
    depends_on:
      - db

ほとんど公式と同じ記述です。 ただ、公式では、PostgreSqlの場合ですし、先程作成したDBの記述はありません。 シンプルに詳細を確認したい人は、一読するといいと思います!

クイックスタート・ガイド:Docker Compose と Django — Docker-docs-ja 17.06.Beta ドキュメント

Dockerfile

こちらはシンプルですね。python ^3.5をベースにpipでrequirements.txtを元にライブラリをインストールさせています。 ここで一番注目したいのは、PyMySQLアップグレードです。

私が環境構築をしている時に、何時間もつまったのがこれ。 なんてことないエラーでしたが、どうもうまくいかなかった...

知っていればなんてことないことなのですが、どうやらDjangoのversionが2.0以上になって発生するエラーのようでした。 色々ググってみると、色んなやり方で解決されているみたいでしたが、アップグレード1発で解決されるのが一番シンプルですよね!

docker-compose.yml

こちらもいたって普通の記述です。 初期データを必要とすることで、volumesの設定(特にtarget)が重要です。

docker-entrypoint-initdb.dに注目します。 これは、mysqlのイメージがコンテナの起動時に自動的に初期データを読み込んでくれる機能を使用しています。

下のbindは、初期データがなかった場合に代用できる記述です。

ここまで作成して、足りないファイルが2つあります。

requirements.txt

このプロジェクトで必要なPythonのライブラリを記述します。

Django==2.1.1
djangorestframework
django-filter
psycopg2
psycopg2-binary

.env

docker-compose.ymlで使用されている変数があると思います。($DJANGO_SECRET_KEY) これは、Djangoのプロジェクト作成時に発行されるキーなのですが、環境変数に設定しようかと思って普通に記述すると、以下のエラーが発生。 (何故環境変数に、DjangoのSECRET_KEYを設定しようと思ったかというと、毎回毎回Docker環境を作成しては削除していると、Githubでのヴァージョン管理の際に、settings.pyは管理したいけど、SECRET_KEYは別で管理したいということになりました。)

ERROR: Invalid interpolation format for "environment" option in service "web": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

どうやらエスケープしなければならない記号が入っていて、environmentに直接値を記述できませんでした。(やり方はあるが面倒くさい..w) なので、.envというファイルを作成し、これをヴァージョン管理。

え、ヴァージョン管理するの?って思うかもしれませんが、これには理由があります。 1つ目としては、他の人に同じ.envを提供できる。(= 同様な環境構築を目指す。)また、開発環境においては他の値で代用できたりしますね。 2つ目は、STGや本番では、OSの環境変数を見に行くので大丈夫ということ。ただし、これはあくまで、本番サーバーでDockerを稼働させないことになります。 まあ、とりあえずローカル環境はDockerで!みたいな人向けになってしまいますが...(でも会社とかで作業していると、新機能作成で本番はDockerじゃないけど、機能追加のためにDocker流行ってるし今回のローカル環境はDockerにしよう!って人も多いのでは?w)

それはさておき、.envには以下を記述

DJANGO_SECRET_KEY=-<今は適当な値をとりあえずいれておく>
DJANGO_DEBUBG=True

ProjectとAppを作成

コマンドを実行してプロジェクトとアプリを作成します。

プロジェクトルートにて、以下を実行。

# プロジェクトを作成
$ docker-compose run web django-admin.py startproject settings .
# アプリケーションを作成
$ docker-compose run web python manage.py startapp src

これを実行すると、プロジェクト構成は以下のようになるかと思います。

<project_name>
├── Dockerfile
├── README.md
├── db.sqlite3
├── docker-compose.yml
├── manage.py
├── mysql
│   └── init
│       └── <file_name>.sql
├── requirements.txt
├── settings
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-35.pyc
│   │   ├── settings.cpython-35.pyc
│   │   ├── urls.cpython-35.pyc
│   │   └── wsgi.cpython-35.pyc
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── src
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

一気にファイルやらディレクトリが作成されたかと思います。 今回、プロジェクトとアプリの作成コマンドの名前をsettingsとsrcにしました。 これは任意です。ディレクトリ名としてわかりやすいものでいいと思います。

mysqlとの接続

/settings/settings.pyを編集します。

  • DBの設定
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql,
        'NAME': <db_name>,
        'USER': '<user_name>,
        'HOST': 'db',
        'PORT': 3306,
    }
}
  • PyMySqlの読込 同ファイル、任意の箇所に以下を記述
import pymysql

pymysql.install_as_MySQLdb()

また、このファイルにSECRET_KEYが記述されているかと思います。 この値を.envのDJANGO_SECRET_KEYに追記し、 settings.pyのSECRET_KEYの部分に、os.environ.get('DJANGO_SECRET_KEY')を代わりに書いて、OSの環境変数を参照するようにします。

合わせて、DEBUGもos.environ.get('DJANGO_DEBUBG')として書き換えます。

その他、プロジェクトに合わせて、LANGUAGE_CODEとTIME_ZONEも合わせます。 (体外が、jaAsia/Tokyoでしょうか。)

INSTALLED_APPSにもこちらを追記します。'rest_framework'

DBとのシンクのために、マイグレーションファイルを作成します。

models.pyに対象のテーブル(class)を記載して実行すると、src/migrations/ディレクトリが作成され、 0001_initial.pyのようなファイルが作成されます。 はじめにmodels.pyが空の状態でmigrateをしても大丈夫ですが、src/migrations/配下にはini.pyしか作成されません。

# ホストからマイグレーションファイルををDBに適用
$ docker-compose exec web python manage.py makemigrations

# Dockerコンテナの中に入ってマイグレーションファイルをDBに適用
$ python manage.py makemigrations

# ホストからmodels.pyに書いたclassを読み込んでマイグレーションファイルを作成
$ docker-compose exec web python manage.py makemigrations <app_name>

# models.pyに書いたclassを読み込んでマイグレーションファイルを作成
$ python manage.py makemigrations <app_name>

ググってみるとほとんど記事が新規でお試しテーブルみたいなかんじでmodels.pyに記述していますが、 既存のdumpファイルから作成されたDBから以下のコマンドで、models.pyに自動でclassを作成してくれます。

$ python manage.py inspectdb tabbel_name >> models.py

実行

最後に実行です。

$ docker-compose up

.
.
.
<container_name> | You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
<container_name> | Run 'python manage.py migrate' to apply them.
<container_name> | November 19, 2018 - 19:51:55
<container_name> | Django version 2.1.1, using settings 'settings.settings'
<container_name> | Starting development server at http://0.0.0.0:8080/
<container_name> | Quit the server with CONTROL-C.

このような出力されればOKでしょう。

http://localhost:8080/

ブラウザでlocalhostにアクセスし、Welcomeページが表示されていれば、あとはもう開発を開始できます!

まとめ

だいぶ駆け足で書きました。 うまくいかない場合は、もちろん公式を参照してみて下さい。

確認としては、Dockerのコンテナにはいることで解消できる場合もあります。($ docker exec -i -t コンテナ名 bash) また、Dockerのコンテナは何度削除しても大丈夫です!($ docker rm <CONTAINER ID or NAMES>)

1からのやり直しも簡単にできるのはDockerのいいところでもあるのかなと思います。 今回私は、初期データとして、5GBのdumpファイルを用いましたが(ちょっと最初のイメージコンテナ作成時時間はかかりますが、)問題なく動作しています。

後先考えると環境構築はほんとに大変... ですが、とりあえず構築させていくことが大事ですね!

以上.