言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Docker辞典

  1. トップページ
  2. Docker辞典
  3. compose 構成例: Django + PostgreSQL

compose 構成例: Django + PostgreSQL

Django アプリと PostgreSQL を Docker Compose で連携させる構成です。db サービスが完全に起動してからマイグレーションを実行する必要があるため、depends_oncommand の組み合わせ、または別途エントリーポイントスクリプトでタイミングを制御します。

構文

# -----------------------------------------------
#  Python(Django)+ PostgreSQL の基本構成
# -----------------------------------------------

# services:
#   db:                         → PostgreSQL サービスの定義
#     image: postgres:16        → 使用する PostgreSQL のバージョン
#     environment:              → 環境変数でデータベースを初期設定します
#       POSTGRES_DB: appdb
#       POSTGRES_USER: appuser
#       POSTGRES_PASSWORD: secret
#     volumes:
#       - db_data:/var/lib/postgresql/data  → データを名前付きボリュームに永続化します
#     healthcheck:              → PostgreSQL の準備完了を確認するヘルスチェック
#       test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
#       interval: 5s
#       retries: 5
#
#   web:                        → Django アプリサービスの定義
#     build: .                  → カレントディレクトリの Dockerfile からビルドします
#     command: >
#       sh -c "python manage.py migrate &&
#              python manage.py runserver 0.0.0.0:8000"
#       → マイグレーション実行後に開発サーバーを起動します
#     environment:
#       DATABASE_URL: postgres://appuser:secret@db:5432/appdb
#     ports:
#       - "8000:8000"
#     volumes:
#       - .:/app                → ホストのソースコードをコンテナにマウントします
#     depends_on:
#       db:
#         condition: service_healthy  → db のヘルスチェックが通過してから起動します
#
# volumes:
#   db_data:                    → PostgreSQL データ永続化用の名前付きボリューム

構文一覧

構文/設定キー概要
image: postgres:16Docker Hub 公式の PostgreSQL バージョン 16 イメージを使用します。
POSTGRES_DBコンテナ初回起動時に自動作成するデータベース名を指定します。
POSTGRES_USERPostgreSQL のスーパーユーザー名を指定します。
POSTGRES_PASSWORD上記ユーザーのパスワードを指定します。本番環境では secrets または .env ファイルで管理します。
healthcheckサービスの準備状態を定期的にチェックします。pg_isready を使うことで PostgreSQL が接続を受け付けるまで待機できます。
depends_on: condition: service_healthy依存サービスのヘルスチェックが成功してから自サービスを起動します。単純な depends_on ではコンテナ起動順のみ制御でき、DB の準備完了は保証されません。
commandDockerfile の CMD を上書きして実行するコマンドを指定します。マイグレーションと起動をまとめて記述できます。
python manage.py migrate未適用のマイグレーションをすべて実行します。初回起動時や models.py 変更後に必要です。
DATABASE_URLdjango-environdj-database-url で読み込むデータベース接続文字列です。postgres://user:password@host:port/dbname の形式で記述します。
volumes: - .:/appホストのプロジェクトディレクトリをコンテナの /app にマウントします。コードを変更すると即座に反映されます。
名前付きボリューム(db_dataコンテナを削除・再作成してもデータベースのデータが消えません。docker compose down -v で明示的に削除できます。

サンプルコード

docker-compose.yml — Django + PostgreSQL の構成
# -----------------------------------------------
#  docker-compose.yml
#  Django アプリ(web)と PostgreSQL(db)の構成
# -----------------------------------------------

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: nerv_db
      POSTGRES_USER: ikari_gendo
      POSTGRES_PASSWORD: seele0001
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ikari_gendo -d nerv_db"]
      interval: 5s
      timeout: 5s
      retries: 5

  web:
    build: .
    command: >
      sh -c "python manage.py migrate &&
             python manage.py runserver 0.0.0.0:8000"
    environment:
      DATABASE_URL: postgres://ikari_gendo:seele0001@db:5432/nerv_db
      DJANGO_SECRET_KEY: evangelion-secret-key
      DEBUG: "True"
    ports:
      - "8000:8000"
    volumes:
      - .:/app
    depends_on:
      db:
        condition: service_healthy

volumes:
  db_data:
Dockerfile — Django アプリ用
# -----------------------------------------------
#  Dockerfile
#  Python 3.12 ベースの Django アプリイメージ
# -----------------------------------------------

FROM python:3.12-slim

# 作業ディレクトリを /app に設定します
WORKDIR /app

# 依存ライブラリを先にコピーしてキャッシュを活かします
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーション全体をコピーします
COPY . .
requirements.txt
# -----------------------------------------------
#  requirements.txt
# -----------------------------------------------

Django==5.0.4
psycopg2-binary==2.9.9   # PostgreSQL ドライバです
dj-database-url==2.1.0   # DATABASE_URL 環境変数を自動解析します
Django settings.py — データベース設定の抜粋
# -----------------------------------------------
#  settings.py(抜粋)
#  dj-database-url で DATABASE_URL を読み込みます
# -----------------------------------------------

import dj_database_url
import os

DATABASES = {
    'default': dj_database_url.config(
        default=os.environ.get('DATABASE_URL'),
        conn_max_age=600,
    )
}
起動・マイグレーション実行・動作確認
# -----------------------------------------------
#  コンテナを起動してマイグレーションを実行します
# -----------------------------------------------

# イメージをビルドしてバックグラウンドで起動します
docker compose up --build -d

# web サービスのログを確認します(マイグレーション完了を確認)
docker compose logs web

# PostgreSQL に直接接続して確認します(パスワード: seele0001)
docker compose exec db psql -U ikari_gendo -d nerv_db -c "\dt"

# コンテナを停止します(ボリュームは残ります)
docker compose down
$ docker compose up --build -d
[+] Building 12.3s (10/10) FINISHED
[+] Running 3/3
 ✔ Network nerv_default  Created
 ✔ Container nerv-db-1   Healthy
 ✔ Container nerv-web-1  Started
$ docker compose logs web
nerv-web-1  | Operations to perform:
nerv-web-1  |   Apply all migrations: admin, auth, contenttypes, sessions
nerv-web-1  | Running migrations:
nerv-web-1  |   Applying contenttypes.0001_initial... OK
nerv-web-1  |   Applying auth.0001_initial... OK
nerv-web-1  |   Applying admin.0001_initial... OK
nerv-web-1  |   Applying sessions.0001_initial... OK
nerv-web-1  | Watching for file changes with StatReloader
nerv-web-1  | Performing system checks...
nerv-web-1  | System check identified no issues (0 silenced).
nerv-web-1  | Django version 5.0.4, using settings 'config.settings'
nerv-web-1  | Starting development server at http://0.0.0.0:8000/
nerv-web-1  | Quit the server with CONTROL-C.
$ docker compose exec db psql -U ikari_gendo -d nerv_db -c "\dt"
                  List of relations
 Schema |            Name            | Type  |    Owner
--------+----------------------------+-------+-------------
 public | auth_group                 | table | ikari_gendo
 public | auth_permission            | table | ikari_gendo
 public | auth_user                  | table | ikari_gendo
 public | django_admin_log           | table | ikari_gendo
 public | django_content_type        | table | ikari_gendo
 public | django_migrations          | table | ikari_gendo
 public | django_session             | table | ikari_gendo
(7 rows)
独自モデルを追加してマイグレーションする
# -----------------------------------------------
#  models.py にモデルを追加してマイグレーションします
# -----------------------------------------------

# ホスト側でモデルを定義します(volumes マウントで即反映されます)
# models.py に以下を追記します:
#
#   from django.db import models
#
#   class Pilot(models.Model):
#       name = models.CharField(max_length=100)
#       unit = models.IntegerField()
#
#       def __str__(self):
#           return self.name

# マイグレーションファイルを生成します
docker compose exec web python manage.py makemigrations

# マイグレーションを実行します
docker compose exec web python manage.py migrate

# Django シェルでデータを登録して確認します
docker compose exec web python manage.py shell
$ docker compose exec web python manage.py makemigrations
Migrations for 'pilots':
  pilots/migrations/0001_initial.py
    - Create model Pilot
$ docker compose exec web python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, pilots, sessions
Running migrations:
  Applying pilots.0001_initial... OK
$ docker compose exec web python manage.py shell
Python 3.12.3 (main, Apr  9 2024, 08:09:14) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from pilots.models import Pilot
>>> Pilot.objects.create(name='碇シンジ', unit=1)
<Pilot: 碇シンジ>
>>> Pilot.objects.create(name='綾波レイ', unit=0)
<Pilot: 綾波レイ>
>>> Pilot.objects.create(name='惣流アスカ', unit=2)
<Pilot: 惣流アスカ>
>>> Pilot.objects.all()
<QuerySet [<Pilot: 碇シンジ>, <Pilot: 綾波レイ>, <Pilot: 惣流アスカ>]>

概要

Django と PostgreSQL を Docker Compose で連携させる際の最重要ポイントは、マイグレーションのタイミング制御です。depends_on だけではコンテナの起動順のみを保証し、PostgreSQL が接続を受け付ける状態になったかどうかは保証しません。healthcheckpg_isready を設定し、depends_on: condition: service_healthy と組み合わせることで、DB の準備が完了してから Django が起動するフローを確実に実現できます。

開発環境では commandpython manage.py migrate && python manage.py runserver をまとめて書く方法が手軽ですが、本番環境では Dockerfile の ENTRYPOINT スクリプトに分離してマイグレーションとサーバー起動を明示的に管理するのが一般的です。また、POSTGRES_PASSWORD などの機密情報は .env ファイルや Docker Secrets で管理し、docker-compose.yml にハードコードしない方法が一般的です。

関連ページ: Docker Compose の基本 / ボリュームとデータ永続化 / 環境変数と .env ファイル

よくあるミス

healthcheck なしで depends_on を使い DB 接続に失敗する

depends_on: condition: service_started(短縮構文含む)はコンテナプロセスの起動のみを確認します。PostgreSQL の初期化が完了する前に Django がマイグレーションを実行しようとして接続エラーになるケースがよくあります。pg_isready を使った healthcheckcondition: service_healthy を組み合わせて対応します。

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ikari_gendo -d nerv_db"]
      interval: 5s
      retries: 5
  web:
    depends_on:
      db:
        condition: service_healthy

volumes マウントにより Dockerfile の COPY が上書きされる

volumes: - .:/app でホストのディレクトリをコンテナにマウントすると、Dockerfile で COPY . . した内容がマウントで上書きされます。ローカルに node_modules__pycache__ などが存在する場合、コンテナ内に不要なファイルが混入することがあります。.dockerignore で除外するか、サブパスのみをマウントします。

マイグレーションを実行し忘れてテーブルが存在しない

commandpython manage.py migrate を含めていない場合、コンテナ起動後もテーブルが作成されていない状態になります。本番では ENTRYPOINT スクリプトでマイグレーションを明示的に実行するか、command に含めます。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。