🔥

TiDBローカル環境構築④

に公開

TiDBのローカル環境の構築を4回目行なっていきます。
前回DB環境の構築が出来たましたが、今回はDockerでTiDBに繋いだDjango環境を行っていきます。

コンテナ環境のツリー構成

今回追加するのはwwwフォルダ配下とルートのdocker-compose.ymlにAPI用のコンテナ操作を追加します。
ツリー構成は以下になります。

.
├── docker-compose.yml
├── .env.api
└── src
    ├── infra
    │   └── db
    │       ├── Dockerfile
    │       ├── data
    │       │   └── test_db.sql
    │       ├── docker-compose.yml
    │       └── entrypoint.sh
    └── www
        └── api
            ├── Dockerfile
            ├── docker-compose.yml
            ├── project
            │   ├── manage.py
            │   └── project
            │       ├── __init__.py
            │       ├── __pycache__
            │       │   ├── __init__.cpython-311.pyc
            │       │   ├── settings.cpython-311.pyc
            │       │   ├── urls.cpython-311.pyc
            │       │   └── wsgi.cpython-311.pyc
            │       ├── asgi.py
            │       ├── settings.py
            │       ├── urls.py
            │       └── wsgi.py
            └── requirements.txt

ソースコードを作成

ルートのdocker-compose.ymlを編集します。

docker-compose.yml
services:
  tiup-playground:
    extends:
      file: ./src/infra/db/docker-compose.yml
      service: tiup-playground
  api:
    extends:
      file: ./src/www/api/docker-compose.yml
      service: api
    depends_on:
      - tiup-playground

networks:
  tidb-net:
    name: tidb-net
    driver: bridge



APIコンテナ用のdocker-compose.ymlの作成を行います。
TiDBのコンテナと連携しています。

./src/www/api/docker-compose.yml
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    env_file:
      - ../../../.env.api

    container_name: ubuntu-dev-container
    stdin_open: true
    tty: true
    ports:
      - "8000:8000"
    depends_on:
      - tiup-playground
    volumes:
      - .:/app
      - /app/node_modules
    command: python manage.py runserver 0.0.0.0:8000
    networks:
      - tidb-net

networks:
  tidb-net:
    external: true



API環境設定ファイルを作成します。

./.env.api
DJANGO_SECRET_KEY=devsecret
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=*

DB_ENGINE=django.db.backends.mysql
DB_NAME=test
DB_USER=root
DB_PASSWORD=
DB_HOST=tiup-playground
DB_PORT=4000



python環境でDjangoコンテナ環境を構築します。

./src/www/api/Dockerfile
FROM python:3.11-slim

RUN apt-get update && apt-get install -y \
    default-libmysqlclient-dev \
    gcc \
    pkg-config \
    default-mysql-client

WORKDIR /app/project

COPY requirements.txt .

RUN python -m venv /venv

RUN /venv/bin/pip install --no-cache-dir -r requirements.txt

ENV PATH="/venv/bin:$PATH"

COPY . .

CMD ["/bin/bash"]



Django 4.2系でREST APIを構築し、PyMySQLでMySQL/TiDB接続、python-dotenvで環境変数を管理するための依存パッケージ定義します。

./src/www/api/requirements.txt
Django>=4.2,<4.3
djangorestframework>=3.15
PyMySQL>=1.1
python-dotenv>=1.0



Djangoプロジェクトの manage.py ファイルで、環境変数を設定してからコマンドライン経由で管理コマンドを実行するエントリーポイントを作成します。

./src/www/api/project/manage.py
import os
import sys


def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()



PyMySQLをMySQLdb互換としてインストールし、DjangoなどMySQLdb依存のコードでも利用できるようにする初期化スクリプトを作成します。

./src/www/api/project/project/__init__.py
import pymysql
pymysql.install_as_MySQLdb()



DjangoアプリをASGIサーバーで動作させるためのエントリーポイントを定義し、project.settings を読み込む初期設定を行うスクリプトを作成します。

./src/www/api/project/project/asgi.py
import os
import sys

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()



Djangoプロジェクト全体の設定(データベース、アプリケーション、ミドルウェア、テンプレート、静的ファイルなど)を管理する構成ファイルを作成します。

./src/www/api/project/project/settings.py
from pathlib import Path
import os

from django.db.backends.base.base import BaseDatabaseWrapper
BaseDatabaseWrapper.check_database_version_supported = lambda self: None

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = 'django-insecure-7$hqwi26z!jmm9-omhajuwy3rop&i)+&@4keai-*y9hzndz4ls'

DEBUG = True

ALLOWED_HOSTS = []

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'project.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'project.wsgi.application'

DATABASES = {
  "default": {
    "ENGINE": "django.db.backends.mysql",
    "NAME": os.getenv("DB_NAME"),
    "USER": os.getenv("DB_USER"),
    "PASSWORD": os.getenv("DB_PASSWORD"),
    "HOST": os.getenv("DB_HOST","tiup-playground"),
    "PORT": os.getenv("DB_PORT","4000"),
    "OPTIONS": {
      "charset": "utf8mb4",
      "init_command": "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci'",
    },
  }
}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_TZ = True

STATIC_URL = 'static/'

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'



DjangoアプリのURLルーティングを定義し、/admin/ にアクセスした際に管理サイトへ接続できるようにする設定ファイルを作成します。

./src/www/api/project/project/urls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]



DjangoアプリをWSGI互換サーバーで動作させるためのエントリーポイントを定義するファイルの作成を行います。

./src/www/api/project/project/wsgi.py
import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = get_wsgi_application()



この状態で以下のコマンドを実行します。

docker-compose up -d



以下のURLにアクセスするとDjangoのトップページが確認できました。
http://127.0.0.1:8000/

詰まった箇所

settings.pyでDatebase設定をする際に、charsetをutf8mb4で定義化していないとエラーになりました。
エラー内容が以下になります。

django.db.utils.OperationalError: (1115, "Unknown character set: 'utf8mb3'")

まとめ

今回は TiDB ローカル環境構築の第4回 として、前回構築した DB 環境に加えて Docker 上で TiDB と接続する Django API 環境 を構築しました。
ルート docker-compose.yml に API サービスを追加し、www/api 配下に Django プロジェクトと関連ファイルを作成。
Django 4.2 系・DRF・PyMySQL・python-dotenv を利用して TiDB に接続し、API 実行環境を整備しました。
起動後に http://127.0.0.1:8000/ にアクセスすると Django のトップページが表示されます。
構築中、settings.py の DB 設定で charset を utf8mb4 にしないとエラーになる 点に注意が必要です。
続きは次回行います。

コラボスタイル Developers

Discussion