Docker ComposeでDjango+MySQL+Nginxの環境構築

Docker ComposeでDjango+MySQL+Nginxの環境構築

Pythonを使ってWeb開発をやってみたいと思い、せっかくなので最近気になっていたコンテナを使った仮想環境の構築からやってみました。仮想環境上であれば設定内容を変えてみたりと、気にせずやり直しがきくので安心です。もう少し設定内容をきれいに出来そうでしたが、環境構築ばかりやっていても進まないのでこの辺りでメモを残しておきます。

最後にDjangoのDBマイグレーション(DB構築)でハマってしまったので解決方法をメモしておきます。

環境

ホスト環境

Mac OS or Windows 10 1909 64bit
VirtualBox 6.1.4

仮想環境

CentOS Linux release 8.1.1911
Docker version 19.03.8
docker-compose version 1.25.4

コンテナ環境

Python
 python 3.6
 django 2.2.14
 uwsgi 2.0.18
 mysqlclient 1.4.6
MySQL
 MySQL 5.7
Nginx
 nginx 1.17

ディレクトリ構成

django
├── docker-compose.yml
├── mysql
│   ├── Dockerfile
│   └── init.d
│       └── init.sql
├── nginx
│   ├── conf
│   │   └── app_nginx.conf
│   └── uwsgi_params
└── python
    ├── Dockerfile
    └── requirements.txt

docker-compose.ymlの定義内容

docker-compose.ymlの設定内容になります。各内容は後述します。

docker-compose.yml
version: '3.7'

services:
  nginx:
    image: nginx:1.17
    ports:
      - "8000:8000"
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./static:/static
    depends_on:
      - python

  db: 
    build: ./mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      TZ: 'Asia/Tokyo'
    volumes:
      - django.db.volume:/var/lib/mysql
      - ./mysql/init.d:/docker-entrypoint-initdb.d

  python:
    build: ./python
    command: uwsgi --socket :8001 --module mysite.wsgi --logto /tmp/uwsgi.log
    volumes:
      - ./src:/code
      - ./static:/static
    expose:
      - "8001"
    depends_on:
      - db

volumes:
 django.db.volume:
   name: django.db.volume

Python(Django)の環境設定

docker-compose.ymlの設定

commandについて

  • ポート:8001を開いてNginxとリンクします。
  • "[プロジェクト名].wsgi"のファイル名はDjangoのプロジェクト名を指定します。
  • "--py-autoreload 1"はDjangoで何か変更したときにファイルをリロードします。
  • "--logto /tmp/uwsgi.log"はログの出力設定です。

volumesではソースコードが格納されている/codeを/srcにマウントすることでコンテナ外からソースファイルを触れるようにしておきます。

docker-compose.yml (抜粋)
  python: # サービス名(任意の名前)
    build: ./python # Dockerfileを置いている場所を指定
    command: uwsgi --socket :8001 --module mysite.wsgi --logto /tmp/uwsgi.log # コンテナ起動時のコマンド
    volumes:
      - ./src:/code
      - ./static:/static
    expose:
      - "8001"
    depends_on:
      - db # DB起動後にpythonのサービスを立ち上げる為、依存関係を設定

Dockerfileの設定

python(Django)用のDockerfileを作成します。ソースコードをcodeディレクトリに格納しマウントすることでコンテナの外から編集できるようにします。

Dockerfile
FROM python:3.7

RUN mkdir /code
WORKDIR /code

ADD requirements.txt /code/
RUN pip install --upgrade pip \
    && pip install -r requirements.txt

ADD . /code/

requirements.txt

インストールするパッケージを定義します。重要なのがmysqlclient(Django推奨)をインストールしている事です。Djangoの環境構築で良く紹介されていたのがPyMySQLでしたが、Django 2.2ではPyMySQL 0.9.3が未対応なのでDBマイグレーションに失敗します。

requirements.txt
Django==2.2.12
uwsgi==2.0.18
mysqlclient==1.4.6

MySQLの環境設定

docker-compose.yml

データベース用のコンテナではコンテナを停止するとデータが全て破棄されてしまいます。データを永続的に保持しておくためにはローカルのディレクトリと同期する方法とdata volumeコンテナに保存する2つの方法があります。

ここでは、後者の"django.db.volume"というdata volumeを作成し同期させるようにしています。

データベース作成などの初期処理はinit.sqlで設定しているので、ここではroot権限のパスワードとタイムゾーンの設定のみ行います。

docker-compose.yml (抜粋)
services:
  db: 
    build: ./mysql
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      TZ: 'Asia/Tokyo'
    volumes:
      - django.db.volume:/var/lib/mysql
      - ./mysql/init.d:/docker-entrypoint-initdb.d
volumes:
 django.db.volume:
   name: django.db.volume

Dockerfile

イメージの作成情報とbuild時に行う処理を設定します。

Dockerfile
FROM mysql:5.7

# init.dのファイルをコピー
COPY init.d/* /docker-entrypoint-initdb.d/

init.sql

MySQLの初期処理をここで定義します。

init.sql
# データベース作成
CREATE DATABASE IF NOT EXISTS django_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
# ユーザー作成
CREATE USER IF NOT EXISTS 'django_user'@'%' IDENTIFIED BY 'djangopass';
# django_dbへのアクセス権を付与
GRANT ALL PRIVILEGES ON django_db.* TO 'django_user'@'%';
# 権限を反映
FLUSH PRIVILEGES;

nginxの環境設定

docker-compose.yml

docker-compose.yml (抜粋)
nginx:
  image: nginx:1.17
  ports:
    - "8000:8000"
  volumes:
    - ./nginx/conf:/etc/nginx/conf.d
    - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
    - ./static:/static
  depends_on:
    - python

uwsgi_params

Nginxの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;

app_nginx.conf

これでNginxのバージョンが分かると脆弱性をついた攻撃に合う可能性がある為、server_tokensをoffにしバージョン番号を非表示に設定します。

app_nginx.conf
upstream django {
  ip_hash;
  # Pythonの公開ポートを設定
  server python:8001;
}

server {
  # Nginxの公開ポートを設定します。
  listen      8000;
  server_name 127.0.0.1;
  charset     utf-8;

  location /static {
    alias /static;
  }
  
  # max upload size
  client_max_body_size 75M;   # adjust to taste
  
  # 全てのリクエストをdjangoに送るための設定
  location / {
    uwsgi_pass  django;
    include     /etc/nginx/uwsgi_params;
  }
}

# レスポンスヘッダにバージョン番号を出さないための設定(セキュリティのため)
server_tokens off;

Djangoのプロジェクトを作成

# 最後の"mysite"は任意のプロジェクト名です。
$ sudo docker-compose run python django-admin.py startproject mysite .

settings.pyの変更

プロジェクトのディレクトリ内に作成されたsettings.pyを編集します。編集する場所は2か所です。

データベースへの接続設定の"HOST"にはコンテナのサービス名を設定します。

./src/mysite/settings.py
# データベースへの接続設定を変更
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_db',
        'USER': 'django_user',
        'PASSWORD': 'djangopass',
        'PORT': '3306',
        'HOST': 'db',
    }
}


# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = '/static/' # ←追加します

Djangoのデータベース構築と管理者ユーザー作成

# DBマイグレーション
$ sudo docker-compose run python ./manage.py migrate
# 管理者の設定
$ sudo docker-compose run python ./manage.py createsuperuser

静的ファイルをコピー

cssやjavascriptなどの静的ファイルをstaticディレクトリにコピーします。

$ sudo docker-compose run python ./manage.py collectstatic

コンテナを起動

それぞれのコンテナを起動します。初回はNginxがビルドされるので時間が掛かりますが、次回からは数秒で起動します。

# "-d"はバックグラウンドで起動
$ docker-compose up -d

Djangoのトップページにアクセス

http://[CentOS8のアドレス]:8000にアクセスして、この画面が表示されれば無事完了です。

http://[CentOS8のアドレス]:8000/adminでログイン画面が表示されるので、さきほど作成した管理者ユーザーでログインしてみましょう

コンテナを停止

コンテナの停止は以下のコマンドで全て停止します。

$ sudo docker-compose stop

エラー内容

DjangoのDBマイグレーションでエラー

DjangoのDBマイグレーションをした時に以下のエラーが出て数日ハマってました。冷静にエラー内容を見れば分かりますが、初めての環境構築だったのでいろんな要因を考えてドツボにはまりましたが、原因はエラーメッセージに書いてありました。

原因はDjango2.2ではpymysqlは未対応だそうです。mysqlclientのver1.3.13以降のバージョンをインストールする必要があります。

$ sudo docker-compose run python ./manage.py migrate

# エラーの最後を抜粋
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.

アクセスエラー

トップページにアクセスしてみると以下のエラーが表示されました。

# アクセス時にエラーページ
DisallowedHost at /
Invalid HTTP_HOST header: '192.168.64.8:8000'. You may need to add '192.168.64.8' to ALLOWED_HOSTS.

settings.pyの「ALLOWED_HOSTS」にIPアドレスを設定してコンテナを再起動するとトップページにアクセスできました。

./src/mysite/settings.py
ALLOWED_HOSTS = ['192.168.64.8',]

Commentsこの記事のコメント

メールアドレスが公開されることはありません。お気軽にコメントどうぞ。

人気記事