構築環境
docker: 19.03.1
nginx: 1.17.3
python: 3.7.4
django: 2.2.4
uwsgi: 2.0.18
mysql: 8.0.17
構築準備
作業フォルダ
docker にて環境構築を行う前に以下の構成を作成しておく
[C:\work\app]
├─db/ …
│ └─init.sql … 初期化用のSQL
├─logs/ … ログ出力フォルダ
│ ├─test/ … アプリケーション用
│ └─web/ … web サーバ用
├─test/ … 作成するアプリケーション
│ ├─Dockerfile … アプリケーション構築定義
│ └─requirements.txt … pip インストール対象を記載
└─web/ …
├─static/ … 静的コンテンツの配置場所
└─nginx.conf … nginx 設定ファイル
CREATE DATABASE IF NOT EXISTS manage CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER IF NOT EXISTS 'admin'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON manage.* TO 'admin'@'%';
FLUSH PRIVILEGES;
データベース名を manage 、ユーザ/パスワードを admin/password で初期化を行う
FROM python:3.7.4
MAINTAINER Planaria Inc.
ENV PYTHONUNBUFFERED 1
ENV LANG C.UTF-8
ENV TZ Asia/Tokyo
RUN mkdir /app
RUN mkdir /app/logs
WORKDIR /app
ADD requirements.txt /app/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY . /app/
文字コードやタイムゾーンを設定しつつ、作業フォルダとログ出力フォルダを作成
ローカルの requirements.txt をコピーし、pip にてインストールを行う
Django==2.2.4
uwsgi==2.0.18
mysqlclient==1.4.2.post1
インストール対象は上記の通り
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
#include /etc/nginx/conf.d/*.conf;
server {
listen 80;
server_name localhost;
charset utf-8;
location /static {
alias /static;
}
location / {
include uwsgi_params;
uwsgi_pass test:8001;
}
}
server_tokens off;
}
デフォルトの nginx.conf を元に conf のインクルードをコメントアウトし、 server{} 、server_tokens を追加
nginx のコンテナ作成後、上記ファイルで上書きされる
初期構築
django のプロジェクトを作成するための初期構築を行う
version: '3'
services:
test:
build: ./test
ports:
- "8000:8000"
volumes:
- ./test:/app
- ./logs/test:/app/logs
command: python manage.py runserver 0.0.0.0:8000
初期構築用の docker-compose.ymlを C:\work\app に配置する
構築された結果の設定ファイルを変更するため、コンテナ上のフォルダをローカルフォルダとリンクさせる
docker-compose run --rm test django-admin.py startproject config .
プロジェクト作成を docker-compose から実行
rm オプションをつけないと一時的に利用したコンテナが残り続けるので、個人的には必須
実行後、 test フォルダが次の構成となっている
[C:\work\app]
└─test/
├─config/
│ ├─__ init__.py
│ ├─settings.py
│ ├─urls.py
│ └─wsgi.py
├─logs/
├─Dockerfile
├─manage.py
└─requirements.txt
作成された settings.py を2か所修正
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'manage',
'USER': 'admin',
'PASSWORD': 'password',
'HOST': 'app_db',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
},
}
}
アプリケーションからのデータベース接続定義を修正する
ライブラリをデフォルトの sqlite から mysql に
データベース名、ユーザ/パスワードは init.sql で定義した内容を設定
ホスト名は mysql コンテナ名を設定している※後述
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/static'
静的コンテンツの設定を修正
環境構築定義
version: '3'
volumes:
db.volume:
services:
db:
image: mysql:8.0.17
container_name: app_db
ports:
- 3306:3306
volumes:
- db.volume:/var/lib/mysql
- ./db:/docker-entrypoint-initdb.d
command: mysqld --default-authentication-plugin=mysql_native_password --skip-mysqlx
environment:
MYSQL_ROOT_PASSWORD: mysql_password
MYSQL_DATABASE: manage
TZ: 'Asia/Tokyo'
test:
build: ./test
container_name: app_test
volumes:
- ./test:/app
- ./web/static:/static
- ./logs/test:/app/logs
command: uwsgi --socket :8001 --module config.wsgi --logto /app/logs/uwsgi.log
expose:
- "8001"
depends_on:
- db
web:
image: nginx:1.17.3
container_name: app_web
ports:
- "8080:80"
volumes:
- ./web/nginx.conf:/etc/nginx/nginx.conf:ro
- ./web/static:/static
- ./logs/web:/var/log/nginx
depends_on:
- test
環境構築用の docker-compose.ymlを C:\work\app に配置する
volumes:
db.volume:
データベースの永続化するために db.volume を作成、mysql の volumes に設定される
db:
image: mysql:8.0.17
container_name: app_db
ports:
- 3306:3306
volumes:
- db.volume:/var/lib/mysql
- ./db:/docker-entrypoint-initdb.d
command: mysqld --default-authentication-plugin=mysql_native_password --skip-mysqlx
environment:
MYSQL_ROOT_PASSWORD: mysql_password
MYSQL_DATABASE: manage
TZ: 'Asia/Tokyo'
container_name は settings.py の HOST に設定(サービス名 db でも認識される)
volumes として db.volume を指定、データを永続化する
init.sql(初期化用のSQL)を有効にするため、docker-entrypoint-initdb.d とリンクする
command で mysql の認証方式を mysql_native_password にするよう設定
※ mysql 8 を利用する場合のみ必要、mysql 5.7 の場合はデフォルトでnative password 方式なので不要
environment として MYSQL_ROOT_PASSWORD は必須(同意の設定が、何れか必要ぽい)
test:
build: ./test
container_name: app_test
volumes:
- ./test:/app
- ./web/static:/static
- ./logs/test:/app/logs
command: uwsgi --socket :8001 --module config.wsgi --logto /app/logs/application/uwsgi.log
expose:
- "8001"
depends_on:
- db
uwsgi 経由での設定とする為 command で設定
web:
image: nginx:1.17.3
container_name: app_web
ports:
- "8080:80"
volumes:
- ./web/nginx.conf:/etc/nginx/nginx.conf:ro
- ./web/static:/static
- ./logs/web:/var/log/nginx
depends_on:
- test
コンテナ 80 port を 8080 port として公開
nginx.conf(nginx 設定ファイル)を有効にするため、コンテナの nginx.conf とリンク
静的コンテンツの配置場所もリンクさせる
ココまでで構築準備が完了
環境構築
データベースの構築と接続設定
docker-compose run --rm test ./manage.py makemigrations
migrate の定義を作成する
test コンテナと db コンテナが依存関係であるため、まず db コンテナの作成が行われる
Can't connect to MySQL server on 'app_db' (115)
db コンテナの生成は正常終了するが、後続の makemigrations が上記エラーとなる
migrate の定義は作成されている模様
念のため、以下を順に確認していく(省略可
確認1:db コンテナの作成時のログを確認
docker logs app_db
mysql のバージョンによって出力結果は全然異なるので注意
別用途で作成した volume を参照してしまっていた場合は、読み込みエラーになる場合がある
(何度か実行していたので、以前作成した volume を消し忘れたときに発生した)
確認2:ユーザと認証方式
docker-compose exec db mysql -uroot -pmysql_password
mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user | host | plugin |
+------------------+-----------+-----------------------+
| admin | % | mysql_native_password |
| root | % | mysql_native_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session | localhost | caching_sha2_password |
| mysql.sys | localhost | caching_sha2_password |
| root | localhost | mysql_native_password |
+------------------+-----------+-----------------------+
6 rows in set (0.00 sec)
mysql 8 の場合の追加確認:root と admin が mysql_native_password になっていることを確認
確認3:接続先データベースの確認
docker-compose exec db mysql -uroot -pmysql_password
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| manage |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
manage が存在する
問題がなければ migrate を実行する
docker-compose run --rm test ./manage.py migrate
成功していれば、mysql にテーブルが作成されている
docker-compose exec db mysql -uroot -pmysql_password
mysql> show tables from manage;
+----------------------------+
| Tables_in_manage |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
10 rows in set (0.01 sec)
管理ユーザの作成
docker-compose run --rm test ./manage.py createsuperuser
Starting app_db ... done Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password:
Password (again):
The password is too similar to the username.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
パスワードが単純すぎますと怒られてるのはスルー
静的コンテンツの配置
管理画面で使用する静的コンテンツを配置する
docker-compose run --rm test ./manage.py collectstatic
C:\work\app\web\static 以下に展開される
動作確認
docker-compose up -d
上記でサーバ起動
ブラウザで localhost:8080 にアクセスすることで、django の初期ページが表示される
管理画面は localhost:8080/admin/
管理画面からユーザ(test)を追加した場合、mysql にデータが登録されている
mysql> select id, username, email from manage.auth_user;
+----+----------+-------------------+
| id | username | email |
+----+----------+-------------------+
| 1 | admin | admin@example.com |
| 2 | test | |
+----+----------+-------------------+
2 rows in set (0.00 sec)
アプリケーションの追加
はじめての Django アプリ作成を参考にアプリケーションの追加を行う
アプリケーションの作成
docker-compose run --rm test ./manage.py startapp polls
test 内に追加するため、docker-compose から startapp でアプリケーションを追加する
以降はチュートリアル通りに作成できる
チュートリアルでのアプリ作成中に実行するコマンドは docker-compose 経由ですべて実行可能
# アプリケーションの追加
docker-compose run --rm test ./manage.py startapp polls
# コンテナの再起動
docker-compose restart test
# マイグレーション
docker-compose run --rm test ./manage.py makemigrations polls
docker-compose run --rm test ./manage.py migrate
# 静的コンテンツの配置
docker-compose run --rm test ./manage.py collectstatic
# インタプリタ実行
docker-compose run --rm test ./manage.py shell
# テスト実行
docker-compose run --rm test ./manage.py test polls
# テンプレート配置
docker-compose exec test python -c "import django; print(django.__path__)"
docker-compose exec test cp /usr/local/lib/python3.7/site-packages/django/contrib/admin/templates/admin/base_site.html /app/polls/templates/admin/
自動テスト実行の設定変更
C:\work\app>docker-compose run --rm test ./manage.py test polls
Starting app_db ... done
Creating test database for alias 'default'...
Got an error creating the test database: (1044, "Access denied for user 'admin'@'%' to database 'test_manage'")
admin ユーザに自動テスト用のデータベースを作成する権限がないため発生する
テスト用のデータベースを作成し、自動テスト実行時にそれを参照するよう設定する
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'manage',
'USER': 'admin',
'PASSWORD': 'password',
'HOST': 'app_db',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
},
'TEST': {
'NAME': 'test_manage',
},
}
}
作成したテストデータベースへの権限付与
docker-compose exec db mysql -uroot -pmysql_password
mysql> GRANT ALL PRIVILEGES ON test_manage.* TO 'admin'@'%';
python と django と MySQL
今回の環境構築では python 3.7.4 、django 2.2.4と mysql 8.0.17 を利用しているが、環境構築に至るまでに紆余曲折があったため、備忘録として記載する
原因としては2つ、
-
django 2.2 が PyMySQL に対応できていない こと
-
mysql 8 のデフォルト認証方式が mysql 5.7 までの mysql_native_password から caching_sha2_password に変更されたこと
なので、シンプルに構成する場合は python 3 、django 2.1 、 mysql 5.7 とすれば良い(はず...)
django 2.2 を利用して MySQL に接続する
django 2.2 から PyMySQL で MySQL に接続すると以下エラーが発生する
mysqlclient 1.3.13 or newer is required
公式ドキュメントにもある通り、django 2.2 では mysqlclient の利用が推奨されているのでそちらを利用する
MySQL 8 を利用する
認証方式が caching_sha2_password の場合に MySQL に接続すると以下エラーが発生する
caching_sha2_password.so: cannot open shared object file: No such file or directory
フォーラム等も色々覗いてみたが、現状 caching_sha2_password への対応は現実的ではない模様
なので、認証方式を旧式の mysql_native_password に戻してあげる必要がある
現状確認として以下 SQL を実行してみる
SELECT user, host, plugin FROM mysql.user;
plugin の結果が MySQL にログインするユーザの認証方式となる
caching_sha2_password と表示されている場合は、先のエラーが発生する
今回の環境構築では MySQL のコンテナ作成時にパラメータを指定することで認証方式の変更を行っている
command: mysqld --default-authentication-plugin=mysql_native_password --skip-mysqlx
mysql 起動時に認証方式が mysql_native_password に指定されるため、init.sql で作成されたユーザの認証方式は mysql_native_password となる
もし、すでに作成されているユーザに対して認証方式を変更したい場合は、以下SQLを実行
ALTER USER 'admin' IDENTIFIED WITH mysql_native_password BY 'password';
※上記はあくまで例なので admin/password は変更してネ
リンク
http://docs.docker.jp/
http://docs.docker.jp/engine/reference/commandline/index.html
http://docs.docker.jp/compose/toc.html
https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
https://docs.djangoproject.com/ja/2.2/