Docker Compose で Django + MySQL 環境を構築する

概要

Django の開発環境をチームで共有しようとすると、「自分の環境では動くのに」という問題が必ず出てきます。Python のバージョン、MySQL のバージョン、ローカルにインストールしたパッケージの差分──これらを揃えるのは地味に面倒です。

本記事では Docker Compose を使って、Django + MySQL の開発環境を再現性のある形で構築します。docker compose up ひとつで、誰の環境でも同じ状態が立ち上がる状態を目指します。対象読者は「Docker はなんとなく触ったことがあるが、Compose で複数コンテナをつなぐのは不安」というレベルを想定しています。

環境

  • Docker Engine 24 以降(Compose V2 の docker compose コマンドが使える版)
  • Django 5.0 / Python 3.12
  • MySQL 8.0

Compose V1 の docker-compose(ハイフン区切り)ではなく、V2 の docker compose(スペース区切り)を前提とします。

完成形のディレクトリ構成

django-mysql-docker/
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
├── .env
├── .dockerignore
└── app/
    └── (manage.py などプロジェクト本体)

ステップ1: Dockerfile を書く

まず Django を動かす Python コンテナのイメージを定義します。

FROM python:3.12-slim

ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /code

# mysqlclient のビルドに必要なパッケージ
RUN apt-get update && apt-get install -y --no-install-recommends \
    default-libmysqlclient-dev \
    build-essential \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*

# 依存だけ先にコピーしてレイヤーキャッシュを効かせる
COPY requirements.txt /code/
RUN pip install --no-cache-dir -r requirements.txt

COPY ./app /code/

ポイントは2つです。1つ目は requirements.txt を先にコピーしてから pip install している点で、ソースコードを後からコピーすることでコード変更時にビルドキャッシュが効きます。2つ目は default-libmysqlclient-dev 等を入れている点で、mysqlclient は C 拡張をビルドするため、これらが無いと pip install で失敗します。

ステップ2: requirements.txt

Django==5.0.*
mysqlclient==2.2.*

mysqlclient はビルドの手間がありますが MySQL 公式 C API を使うため高速です。ビルドで詰まりたくない場合は純 Python の PyMySQL も選択肢です。本記事では標準的な mysqlclient を採用します。

ステップ3: docker-compose.yml を書く

ここが本記事の中心です。

services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 5s
      retries: 10

  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - ./app:/code
    ports:
      - "8000:8000"
    environment:
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_HOST: db
      MYSQL_PORT: 3306
    depends_on:
      db:
        condition: service_healthy

volumes:
  mysql_data:

volumes によるデータ永続化

mysql_data:/var/lib/mysql という名前付きボリュームを使っています。これが無いと、コンテナを down するたびに MySQL のデータが消えます。名前付きボリュームにしておけば、コンテナを作り直してもデータは残ります。

healthcheck と depends_on の組み合わせ

初学者がハマる最大のポイントです。depends_on に db を指定しただけでは、「db コンテナが起動した」ことしか保証されません。MySQL はコンテナ起動後、実際に接続を受け付けるまで数秒かかります。この間に Django が接続しにいくと Can't connect to MySQL server で落ちます。

そこで healthcheck で mysqladmin ping が通ることを確認し、depends_on に condition: service_healthy を指定します。これで「MySQL が本当に接続を受け付けられる状態になってから web を起動する」が実現できます。

サービス名がホスト名になる

web から MySQL に接続するときのホストは localhost ではなく db(サービス名)です。Compose は同一ネットワーク内でサービス名による名前解決を自動で提供します。

ステップ4: 環境変数ファイル

接続情報をコードに直書きせず、.env に切り出します。

# .env (リポジトリにコミットしない)
MYSQL_DATABASE=myapp
MYSQL_USER=myapp_user
MYSQL_PASSWORD=change_me
MYSQL_ROOT_PASSWORD=change_me_root

.env は必ず .gitignore に追加してください。キーだけ書いた .env.example をコミットしておくとチームメンバーに親切です。

# .gitignore
.env

# .dockerignore
.env
.git
__pycache__/
*.pyc

.dockerignore を置くことで、.env が誤ってイメージに焼き込まれるのを防げます。

ステップ5: Django の settings を MySQL 向けに書き換える

import os

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": os.environ["MYSQL_DATABASE"],
        "USER": os.environ["MYSQL_USER"],
        "PASSWORD": os.environ["MYSQL_PASSWORD"],
        "HOST": os.environ["MYSQL_HOST"],
        "PORT": os.environ["MYSQL_PORT"],
        "OPTIONS": {"charset": "utf8mb4"},
    }
}

charset を utf8mb4 にしておくのを忘れないでください。utf8(3バイト)だと絵文字など一部の文字が保存できません。

ステップ6: 起動と初期化

docker compose build
docker compose run --rm web django-admin startproject myproject .
# settings.py を書き換えてから
docker compose up -d
docker compose exec web python manage.py migrate
docker compose exec web python manage.py createsuperuser

確認方法

ブラウザで http://localhost:8000/ にアクセスし、Django のウェルカム画面が表示されれば web コンテナは正常です。MySQL への接続は以下で確認できます。

docker compose exec db mysql -u myapp_user -p myapp -e "SHOW TABLES;"
docker compose logs web
docker compose logs db

django_migrations などのテーブルが表示されれば、マイグレーションが MySQL に適用され、接続が成立していることが確認できます。

注意点

  • 初回起動で web が落ちる場合: mysqlclient 側の接続リトライが足りず初回だけ落ちることがあります。もう一度 docker compose up を叩けば成功します。恒久対応はエントリポイントで接続待ちループを入れる方法です。
  • ポート競合: ホスト側で 3306 が使われている場合は "13306:3306" のようにずらします。web→db は内部ネットワーク経由なので ports 公開は必須ではありません。
  • 本番にそのまま持っていかない: runserver は開発サーバーです。本番では Gunicorn 等の WSGI サーバー構成に置き換え、パスワードはシークレット管理に乗せてください。

まとめ

Docker Compose で Django + MySQL を組むときの勘所は3点です。名前付きボリュームでデータを永続化する、healthcheck + condition: service_healthy で DB の起動完了を待ってから web を起動する、接続先ホストはサービス名にして接続情報は .env に外出しする。この3点を押さえれば、典型的なハマりどころを回避し、docker compose up ひとつで誰の環境でも同じ状態が立ち上がる開発環境を手に入れられます。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。