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の設定内容になります。各内容は後述します。
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にマウントすることでコンテナ外からソースファイルを触れるようにしておきます。
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ディレクトリに格納しマウントすることでコンテナの外から編集できるようにします。
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マイグレーションに失敗します。
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権限のパスワードとタイムゾーンの設定のみ行います。
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時に行う処理を設定します。
FROM mysql:5.7 # init.dのファイルをコピー COPY init.d/* /docker-entrypoint-initdb.d/
init.sql
MySQLの初期処理をここで定義します。
# データベース作成 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
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_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にしバージョン番号を非表示に設定します。
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"にはコンテナのサービス名を設定します。
# データベースへの接続設定を変更 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アドレスを設定してコンテナを再起動するとトップページにアクセスできました。
ALLOWED_HOSTS = ['192.168.64.8',]
Commentsこの記事のコメント
メールアドレスが公開されることはありません。お気軽にコメントどうぞ。