crashRT のブログ

技術とかそれ以外とか

Django+PostgreSQL+nginx+Dockerでwebアプリを作った話

Django, PostgreSQL, nginx, Docker を使ってwebアプリを作ったので、備忘録も兼ねて環境構築周りの話とか残しておきます。

作ったのはこんな感じのサイトで、MVみたいな映像作品を映像作家と紐づけて管理するためのシステムを作りました。

isle4db.crashrt.work

ディレクトリ構成

.
├── .env
├── docker-compose.prod.yml # 本番環境用のcomposeファイル
├── docker-compose.yml      # 開発環境用のcomposeファイル
├── Dockerfile
├── gunicorn.conf
├── README.md
├── requirements.txt
└── src                     # Djangoのソースコード

開発環境、本番環境を共にDockerコンテナで動かしています。 それぞれ構成が異なるため、composeファイルを分けています。 使用するファイルの指定は -f オプションでできるので、 docker compose -f docker-compose.prod.yml up -d のようにすると本番環境が立ち上がります。 docker-compose.yml の場合は指定は必要ありません。

開発環境

開発環境ではDjango用のコンテナとPostgreSQL用のコンテナの2つを起動します。 composeファイルはこんな感じ。

version: "3"
services:
  db:
    image: postgres:16.0
    env_file: .env
    ports:
      - "54320:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    volumes:
      - db-data:/var/lib/postgresql/data/
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    env_file: .env
    environment:
      - ENV="dev"
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      db:
        condition: service_healthy

volumes:
  db-data:

.envPOSTGRES_USERPOSTGRES_PASSWORを定義しておきます。

注意すべきなのはhealthcheckの部分です。 普通に書くだけだと PostgreSQL が立ち上がる前に Django がDBに接続しようとしてしまうので、ヘルスチェックで立ち上がったことを確認するようにすることが必要です。

また、Djangosettings.py で次のように記述すると、開発環境でのみ DEBUG=True になります。

ENV = os.getenv('ENV')
if ENV == "dev":
    DEBUG=True
else:
    DEBUG=False

composeファイルで環境変数 ENV を設定しているのでそれを読み出し、'dev' の場合はデバッグを有効にしています。 pythonos.getenv() などはstr型を返すので、 DEBUG=True のようにして直接ブール値を設定することはできません。

Dockerfile はこんな感じです。

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code
RUN pip install -r requirements.txt
ADD . /code/
WORKDIR  /code/src
RUN mkdir -p /var/run/gunicorn
RUN python3 manage.py collectstatic --noinput
CMD ["gunicorn", "app.wsgi", "--bind=unix:/var/run/gunicorn/gunicorn.sock"]

CMD に本番環境用のコマンドがありますが、composeファイルで開発サーバー起動のコマンド(python manage.py runserver 0.0.0.0:8000)に上書きされます。

本番環境

本番環境ではDjango用コンテナ、PostgreSQL用konntena、nginx用コンテナの3つを起動します。 composeファイルはこんな感じ。

version: '3'
version: '3'

services:
  db:
    image: postgres:16.0
    container_name: god-movie-collection-db
    env_file: .prod.env
    ports:
      - "54320:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    volumes:
      - db-data:/var/lib/postgresql/data/
    restart: always
  web:
    build: .
    container_name: god-movie-collection-web
    env_file: .env
    volumes:
      - .:/code
      - gunicorn:/var/run/gunicorn
    restart: always
    depends_on:
      db:
        condition: service_healthy
  nginx:
    image: nginx:1.17.7
    container_name: god-movie-collection-nginx
    depends_on:
      - web
    ports:
      - "80:80"
    volumes:
      - ./gunicorn.conf:/etc/nginx/conf.d/default.conf
      - gunicorn:/var/run/gunicorn
    restart: always
volumes:
  db-data:
  gunicorn:
    driver: local

webサーバーとDjangoの間のインターフェースとしてgunicornを使うので requirements.txt に追記します。 僕のはこんな感じです。

Django==5.0
psycopg2==2.9.9
django-bootstrap4==23.2
gunicorn==21.2.0

gunicorn.confにはこのように書きます。

upstream gunicorn-django {
    server unix:///var/run/gunicorn/gunicorn.sock;
}
server {
    listen 80;
    server_name localhost;
    location / {
        try_files $uri @gunicorn;
    }
    location @gunicorn {
        proxy_pass http://gunicorn-django;
    }
}

おまけ

webアプリを外部に公開するのはCloudflare Tunnel を使うと便利だったのでおすすめです。

www.cloudflare.com

最後に

Djangoでwebアプリを開発するときの環境構築について忘れないうちにまとめておこうと思って書いてみました。 少しでも力になれたら嬉しいです。