Xserver VPSでDjango + Docker Compose + Nginx + PostgreSQLを本番デプロイする手順

はじめに

本記事では、DjangoアプリケーションをDocker Composeで本番環境にデプロイする構成について解説します。


ディレクトリ構成

今回のプロジェクト構成は以下の通りです。

project/
├── docker-compose.yml
├── containers/
│   └── django/
│       ├── Dockerfile
│       └── entrypoint.sh
├── nginx/
│   ├── Dockerfile
│   └── default.conf
├── app/
│   └── Django project
└── db/

それぞれの役割を順番に解説します。


docker-compose.yml

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

今回の構成では、主に以下の3つのコンテナを定義します。

db      : PostgreSQL
app     : Djangoアプリケーション
nginx   : リバースプロキシ

例は以下です。

version: "3.8"

services:
  db:
    image: postgres:15
    container_name: webapp_db
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./db/backup:/backup
    networks:
      - webapp_network

  app:
    container_name: webapp_app
    build:
      context: .
      dockerfile: ./containers/django/Dockerfile
    environment:
      - DJANGO_SETTINGS_MODULE=project.settings
    env_file:
      - .env
    volumes:
      - ./app:/code
      - static_volume:/code/staticfiles
      - media_volume:/code/media
    expose:
      - "8000"
    command: sh -c "/usr/local/bin/entrypoint.sh"
    depends_on:
      - db
    networks:
      - webapp_network

  nginx:
    container_name: webapp_nginx
    build:
      context: ./nginx
      dockerfile: Dockerfile
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - static_volume:/staticfiles
      - media_volume:/media
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - app
    networks:
      - webapp_network

volumes:
  db_data:
  static_volume:
  media_volume:

networks:
  webapp_network:
    driver: bridge

dbコンテナ

db:
  image: postgres:15

db コンテナではPostgreSQLを使用します。

environment:
  POSTGRES_DB: mydb
  POSTGRES_USER: myuser
  POSTGRES_PASSWORD: password

ここでは、データベース名、ユーザー名、パスワードを指定しています。

本番環境では、これらの値を直接書かず、.env ファイルなどで管理するのがおすすめです。

volumes:
  - db_data:/var/lib/postgresql/data

この設定により、PostgreSQLのデータをDocker volumeに保存します。

コンテナを削除してもDBの中身が消えないようにするため、本番運用では必須の設定です。


appコンテナ

app:
  build:
    context: .
    dockerfile: ./containers/django/Dockerfile

app コンテナではDjangoアプリケーションを動かします。

Django用のDockerfileは containers/django/Dockerfile に分けています。

volumes:
  - ./app:/code
  - static_volume:/code/staticfiles
  - media_volume:/code/media

ここでは、Djangoのソースコード、静的ファイル、アップロードファイルをマウントしています。

expose:
  - "8000"

expose は、Dockerネットワーク内だけでポートを公開する設定です。

本番環境では、Djangoを直接外部公開せず、Nginx経由でアクセスさせるため、ports ではなく expose を使います。


containers/django/Dockerfile

Djangoアプリケーション用のDockerfileです。

FROM python:3.8

ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /code

# 基本ツール
RUN pip install --upgrade pip && pip install pipenv

# Pipfile / Pipfile.lock の反映
COPY ./Pipfile /code/Pipfile
COPY ./Pipfile.lock /code/Pipfile.lock

COPY . /code/

# pipenv install
RUN pipenv install --system --deploy --ignore-pipfile

# エントリーポイントを登録
COPY ./containers/django/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# Gunicornでアプリ起動(runserverではなく)
CMD ["gunicorn", "app.wsgi:application", "--bind", "0.0.0.0:8000"]

今回のアプリでは仮想環境としてpipenvを使用しました!


containers/django/entrypoint.sh

entrypoint.sh は、Djangoコンテナ起動時に実行する処理をまとめたファイルです。

#!/bin/sh

python manage.py migrate
python manage.py collectstatic --noinput

gunicorn project.wsgi:application --bind 0.0.0.0:8000

主な役割は以下です。

1. マイグレーション実行
2. 静的ファイル収集
3. GunicornでDjangoを起動

migrate

python manage.py migrate

DBのマイグレーションを実行します。

collectstatic

python manage.py collectstatic --noinput

CSSやJavaScriptなどの静的ファイルを STATIC_ROOT に集約します。

Gunicorn起動

gunicorn project.wsgi:application --bind 0.0.0.0:8000

本番環境では、Django標準の runserver ではなく、Gunicornを使って起動します。


nginx/Dockerfile

Nginx用のDockerfileです。

FROM nginx:1.25-alpine

COPY default.conf /etc/nginx/conf.d/default.conf

Nginxの公式イメージをベースにし、独自の設定ファイル default.conf を配置しています。


nginx/default.conf

Nginxの設定ファイルです。

upstream django {
    server app:8000;
}

server {
    listen 80;
    server_name example.com www.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    client_max_body_size 20M;

    location /static/ {
        alias /staticfiles/;
    }

    location /media/ {
        alias /media/;
    }

    location / {
        proxy_pass http://django;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

upstream

upstream django {
    server app:8000;
}

Docker Compose内では、サービス名で名前解決できます。

そのため、Djangoコンテナには app:8000 でアクセスできます。

proxy_pass

proxy_pass http://django;

ユーザーからのリクエストをDjangoアプリケーションへ転送します。

static / media

location /static/ {
    alias /staticfiles/;
}

location /media/ {
    alias /media/;
}

静的ファイルやアップロードファイルは、DjangoではなくNginxから配信します。


appディレクトリ

app/
└── Django project

app ディレクトリには、Djangoプロジェクト本体を配置します。

例:

app/
├── manage.py
├── requirements.txt
├── project/
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── application/
    ├── models.py
    ├── views.py
    └── templates/

dbディレクトリ

db/

db ディレクトリは、バックアップファイルなどを置く用途で使用します。

例:

docker compose exec db pg_dump -U myuser mydb > ./db/backup/backup.sql

PostgreSQLの実データは、基本的にDocker volumeで管理します。


起動方法

docker compose up -d --build

マイグレーション、静的ファイル収集、Gunicorn起動まで entrypoint.sh で実行されます。


本番運用で気をつけたこと

Djangoを直接公開しない

Djangoコンテナでは ports を使わず、expose を使います。

expose:
  - "8000"

外部公開するのはNginxだけにします。


DBを永続化する

volumes:
  - db_data:/var/lib/postgresql/data

DBのデータはコンテナ内だけに置かず、volumeに保存します。


static / media はNginxで配信する

Djangoに静的ファイル配信を任せるのではなく、Nginxで配信します。

location /static/ {
    alias /staticfiles/;
}

開発環境を作成する

この記事では、本番環境へのデプロイ方法についてしか触れていませんが実際の環境では、本番にアップする前にdev.example.comのようにして開発環境にアップしてエラーや挙動などを事前に確認することを強くお勧めします。実際にローカルで確認した際にうまく動いてもデプロイしたらうまくいかないということもよくあるので…

Cloudflareの導入

bot対策やDDOS攻撃の対策の為に、Cloudflareを導入することをお勧めします。


まとめ

今回は、以下の構成でDjangoアプリケーションをDocker Composeによりデプロイする方法を解説しました。

Nginx
↓
Django + Gunicorn
↓
PostgreSQL

この構成にすることで、

・Docker Composeで環境を再現しやすい
・Djangoを直接公開せず安全に運用できる
・PostgreSQLのデータを永続化できる
・静的ファイルをNginxで効率よく配信できる

というメリットがあります。

個人開発のDjangoアプリをVPS上で公開する場合、まずはこの構成をベースにすると扱いやすいと思います。

コメント

タイトルとURLをコピーしました