DockerだけでDjango開発環境を作る(Python・MySQLインストール不要)【AIと一緒に作るDjangoブログ入門 #1】

DockerだけでDjango開発環境を作る(Python・MySQLインストール不要)【AIと一緒に作るDjangoブログ入門 #1】

DockerとDjangoでブログサイトをゼロから作る環境構築編。MacにPythonもMySQLもインストールせず、Dockerだけで開発環境を整える手順を解説します。

はじめに

この記事では、DockerDjangoを使ってブログサイトをゼロから作ります。

「Pythonのインストールは?MySQLは?」と思ったかもしれませんが、必要ありません
Dockerさえあれば、Macに何もインストールしなくても開発環境が作れます。

この章で用意するもの:

  • Docker Desktop(無料)
  • VS Code(無料)
ココココ

このシリーズについて:このブログのコードと記事は、人間と AI(Claude)がペアで設計・実装・検証して作ったものだよ。AIに「とりあえずコード書いて」じゃなく、設計の選択肢を比べて選ぶ → 実装 → ハマる → 一緒に潰す を繰り返した実走記録なんだ。だから「AIに丸投げで動かない」じゃなく、「人とAIで対話しながら作ると、こういう完成度になる」サンプルとして読んでね


Dockerとは?なぜ使うのか

Dockerは「仮想の箱」を作るツールです。

たとえばPythonのプログラムを動かすには、普通はMacにPythonをインストールする必要があります。しかしDockerを使うと「Pythonが入った箱」を用意するだけでOKです。Macには何も入れません。

メリット:

  • MacにPython・MySQL・Nginxをインストールしなくていい
  • 他の人のMacでも同じ環境が再現できる
  • 本番サーバーでも同じ環境で動かせる

今回はこの3つの「箱」を用意します:

コンテナ名 役割
nginx ブラウザからのリクエストを受け取る
web(Django) ページを生成する
db(MySQL) データを保存する

完成形のフォルダ構成

作業前に完成形を確認しておきましょう。

~/myblog/
├ myblog/          ← プロジェクト設定(settings.py・urls.py等)
├ docker/          ← Nginx設定ファイル
│  └ nginx.dev.conf
├ staticfiles/     ← 静的ファイル置き場(CSS・JS・画像)
├ media/           ← アップロード画像置き場
├ manage.py        ← Djangoの操作コマンド
├ .env             ← 環境変数(GitHubにあげない)
├ .dockerignore    ← Dockerに含めないファイルの設定
├ Dockerfile       ← Dockerの設定
├ docker-compose.yml
└ requirements.txt

Step1. フォルダを作る

ターミナルを開いて以下を実行してください。

mkdir -p ~/myblog
mkdir -p ~/myblog/docker
mkdir -p ~/myblog/staticfiles
mkdir -p ~/myblog/media
cd ~/myblog

mkdir -p は「フォルダを作る」コマンドです。-p オプションをつけると、途中のフォルダも一緒に作ってくれます。


Step2. .dockerignoreを作る

touch ~/myblog/.dockerignore

中身:

.env
.git
__pycache__
*.pyc

.dockerignore は「Dockerのイメージに含めないファイル」を指定するファイルです。

特に .envパスワードが書いてあるので、絶対にDockerイメージに含めてはいけません。


Step3. Dockerfileを作る

touch ~/myblog/Dockerfile

中身:

# ベースイメージにPython 3.12を使用
FROM python:3.12

# コンテナ内の作業ディレクトリを/app に設定
WORKDIR /app

RUN groupadd --gid 1000 app && \
    useradd --uid 1000 --gid app --shell /bin/bash --create-home app

# requirements.txtをコンテナにコピー
COPY requirements.txt .

# パッケージをインストール(キャッシュなしで軽量化)
RUN pip install --no-cache-dir -r requirements.txt

# プロジェクトのコードを全てコンテナにコピー
# --chown でコピー時に所有権を app ユーザーに変更
COPY --chown=app:app . .

# 書き込みが必要なディレクトリを app 所有で用意
RUN mkdir -p /app/logs /app/staticfiles /app/media && \
    chown -R app:app /app

# 以降のコマンドは非rootの app ユーザーで実行
# gunicorn は 0.0.0.0:8000 で listen するが、ポート >1024 なので非rootでOK
USER app

DockerfileはDockerの「設計図」です。「Python 3.12の環境を作って、必要なパッケージをインストールして、コードをコピーする」という手順を書いています。


Step4. requirements.txtを作る

touch ~/myblog/requirements.txt

中身:

# Django本体
django==6.0.5

# 本番用Webサーバー
gunicorn==26.0.0

# MySQLクライアント
mysqlclient==2.2.8

# 環境変数管理
django-environ==0.13.0

# 静的ファイル配信
whitenoise==6.12.0

# 画像処理
Pillow==12.2.0

# Markdown → HTML 変換(第6章で使用)
markdown==3.10.2

# コードハイライト(第6章で使用)
pygments==2.20.0

# 画像の自動リサイズ・サムネイル生成(第6章で使用)
django-imagekit==6.1.0

requirements.txt はPythonの「買い物リスト」です。pip install でインストールするパッケージを列挙します。

なぜバージョンを == で固定するのか

django だけ書けば最新が入って良くない?」と思うかもしれませんが、これには 2つの落とし穴 があります。

問題 何が起きるか
半年後に読者が動かせない Django 7.0 が出たら django 指定だと新版が入る → 記事のコードと食い違って動かない
あなたのローカルと本番がズレる 来月 docker compose build し直した時に別バージョンが入ってバグる

実務でも 本番用 requirements は == で完全固定 が定石です。今回も読者全員が同じ環境を再現できるように、執筆時点で動作確認した版で固定しておきます。

ココココ

バージョン更新はどうするの?って思うよね。実は GitHub の Dependabot ってボットが自動で更新PRを作ってくれるんだ。今は固定しておいて、後で安全に上げる方が安心だよ!

ちなみにこの章ではまだ markdown pygments django-imagekit は使いません。第6章で記事本文のリッチ化をするときに使います。ただ後から追加すると docker compose build をやり直す手間が増えるので、最初からまとめて入れてしまうのがオススメです。


Step5. .envを作る

touch ~/myblog/.env

中身:

# Django設定
SECRET_KEY=django-insecure-your-secret-key-here
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1

# データベース設定
MYSQL_DATABASE=myblog
MYSQL_USER=blog_user
MYSQL_PASSWORD=blogpassword
MYSQL_ROOT_PASSWORD=rootpassword
DATABASE_URL=mysql://blog_user:blogpassword@db:3306/myblog

なぜ.envを使うのか?

パスワードをコードに直接書くと、GitHubにあげたときに全世界に公開されてしまいます。.env に書いておけば .dockerignore.gitignore で除外できます。

MYSQL_PASSWORD などのパスワードは好きな文字列に変えてOKです。


Step6. docker-compose.ymlを作る

touch ~/myblog/docker-compose.yml

中身:

services:

  # Nginx(Webサーバー)
  nginx:
    image: nginx:latest
    volumes:
      - ./docker/nginx.dev.conf:/etc/nginx/nginx.conf:ro
      - ./staticfiles:/app/staticfiles
      - ./media:/app/media
    ports:
      - "80:80"
    depends_on:
      web:
        condition: service_started   # webが起動したらNginxも起動する

  # Django(アプリケーションサーバー)
  web:
    build: .
    command: gunicorn myblog.wsgi:application --bind 0.0.0.0:8000 --reload
    volumes:
      - .:/app
      - ./staticfiles:/app/staticfiles
      - ./media:/app/media
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy   # dbがhealthyになるまで待つ

  # MySQL(データベース)
  db:
    image: mysql:8.0
    env_file:
      - .env
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      retries: 5
      timeout: 5s

volumes:
  mysql_data:

docker-compose.yml は3つのコンテナをまとめて管理するファイルです。

web の起動コマンドの --reload

web コンテナの起動コマンドに --reload を付けています。

command: gunicorn myblog.wsgi:application --bind 0.0.0.0:8000 --reload

--reload.py ファイルを保存すると gunicorn が自動で再起動してくれるオプションです。これがないと、views.py などを編集しても docker compose restart web するまで反映されず、「直したのに変わらない!」とハマります。開発中はこれがあると快適です。

ココココ

--reload開発専用だよ。ファイルを監視し続けるぶん動作が重くて不安定だから、本番では絶対に使わないの。本番は第10章で別の docker-compose.prod.yml--reload なし・--workers 付き)を作るよ。「開発用と本番用でcomposeを分ける」ようにするよ。

💡 .py の変更は --reload で即反映されますが、CSSや画像(static)は別です。変更後に collectstatic(第3章で解説)が必要になります。

起動順序の制御

depends_on で起動順序を制御しています。

db が起動(healthy)
 ↓
web が起動(started)
 ↓
nginx が起動

この順番を守らないとNginxがDjangoに接続できずエラーになります。

condition の2種類

種類 意味 使い分け
service_started コンテナが起動した瞬間にOK webやnginxはこれで十分
service_healthy healthcheck が通った状態を待つ DBは「起動済み ≠ 接続可能」なので必須

MySQLは「コンテナ起動」と「クエリ受付可能」にタイムラグがあるため service_healthy で待ちます。一方 Django・Nginx は起動直後から動けるので service_started で十分です。


Step7. nginx.dev.confを作る

touch ~/myblog/docker/nginx.dev.conf

中身:

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server {
        listen 80;
        server_name localhost;

        # 静的ファイル(CSS・JS・画像)の配信
        location /static/ {
            alias /app/staticfiles/;
        }

        # アップロードされた画像の配信
        location /media/ {
            alias /app/media/;
        }

        # それ以外のリクエストはDjangoに転送
        location / {
            proxy_pass http://web:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

Nginxはブラウザからのリクエストを受け取り、CSSや画像は自分で配信し、それ以外はDjangoに転送します。


Step8. Djangoプロジェクトを作る

cd ~/myblog
docker compose run --rm web django-admin startproject myblog .

このコマンドの意味:

  • docker compose run --rm web → webコンテナの中でコマンドを実行(終わったら削除)
  • django-admin startproject myblog .myblog という名前のプロジェクトを現在のフォルダに作る

末尾の . が重要です。これがないと余分なフォルダができてしまいます。

実行後のフォルダ構成:

~/myblog/
├ myblog/      ← プロジェクト設定フォルダ(自動生成)
├ manage.py    ← Djangoの操作コマンド(自動生成)
└ ...

Step9. settings.pyを設定する

~/myblog/myblog/settings.py を開いて修正します。

django-environの設定

ファイルの先頭部分を以下に書き換えます:

from pathlib import Path
import environ

# BASE_DIRを先に定義する
BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env()
environ.Env.read_env(BASE_DIR / '.env')

SECRET_KEY・DEBUG・ALLOWED_HOSTSを.envから読み込む

SECRET_KEY = env('SECRET_KEY')
DEBUG = env.bool('DEBUG')
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')

日本語・タイムゾーンの設定

LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'

DATABASESをMySQLに変更

DATABASES = {
    'default': env.db()
}

STATIC_ROOTの追加

STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

Step10. Dockerイメージをビルドする

cd ~/myblog
docker compose build

Dockerfileの内容をもとにイメージを作ります。初回は数分かかります。

確認コマンド:

docker compose run --rm web pip list | grep -E "django-environ|mysqlclient"

Step11. マイグレーションを実行する

cd ~/myblog
docker compose run --rm web python manage.py migrate

マイグレーションはDjangoがデータベースにテーブルを作る作業です。Djangoが内部で使うテーブル(ユーザー管理・セッション等)が作成されます。


Step12. 起動確認

cd ~/myblog
docker compose up

以下のようなログが流れたらDjangoが起動しています:

web-1 | [INFO] Starting gunicorn 26.0.0
web-1 | [INFO] Listening at: http://0.0.0.0:8000

ブラウザで http://localhost を開いてDjangoのデフォルト画面(ロケット🚀のウェルカムページ)が表示されれば成功です!

止めるときは別タブで:

cd ~/myblog
docker compose down

ハマりポイント

「ブラウザで localhost が ERR_CONNECTION_REFUSED になる」

docker compose ps -a でコンテナの状態を確認:

docker compose ps -a
症状 原因 対処
nginxCreated のまま webの起動待ちで止まってる docker compose logs web でエラー確認
nginxExited (1) nginx.dev.conf の文法エラー docker compose logs nginx でエラー確認
webExited settings.py の編集ミス docker compose logs web でPythonエラー確認
80番ポートが既に使われてる 他のサーバーが動いてる lsof -i :80 で犯人探し

docker compose build でパッケージが見つからないエラー」

ERROR: Could not find a version that satisfies the requirement xxx==1.2.3

→ requirements.txt のバージョンにタイポがあるか、そのバージョンがまだ存在しない可能性。pip index versions <パッケージ名> で実在バージョンを確認しましょう。

dbunhealthy のまま web が起動しない」

MySQLは初回起動時にデータ初期化で時間がかかります。1〜2分待ってから再度 docker compose up を試してください。それでもダメな場合:

docker compose down -v   # ← -v でボリュームも消す(DBデータも消える)
docker compose up

次章ではblogアプリを作って、自分のページを表示させます。

関連記事

Markdownで記事を綺麗に表示する — コードハイライトと目次【AIと一緒に作るDjangoブログ入門 #6.1】
Django
Markdownで記事を綺麗に表示する — コードハイライトと目次【AIと一緒に作るDjangoブログ入門 #6.1】

第5章まで素のテキストだった記事本文を、Markdownで整形して表示できるようにする章。Python-Markdow…

Django記事詳細・カテゴリ・キーワード検索ページの実装【AIと一緒に作るDjangoブログ入門 #5】
Django
Django記事詳細・カテゴリ・キーワード検索ページの実装【AIと一緒に作るDjangoブログ入門 #5】

slug方式の記事詳細ページ、カテゴリ別の一覧、キーワード検索を実装。base.htmlでの共通レイアウト化、F()式…

Djangoのモデル設計とfixtureでブログのデータベースを作る【AIと一緒に作るDjangoブログ入門 #4】
Django
Djangoのモデル設計とfixtureでブログのデータベースを作る【AIと一緒に作るDjangoブログ入門 #4】

Post・Category・DisplaySlotの3つのモデルを設計し、管理画面で記事を管理できるようにする章。fi…

目次

About

AIと一緒にDjangoでブログを作る過程を記事にしています。初心者の方でもDockerさえあれば同じ環境が作れます。

よく読まれている記事

カテゴリ

  1. Django
  2. Docker
  3. Python