はじめに
- 参考文献にある記事を参考に環境構築を進めていました
- しかしながら、AppleシリコンのMacBookとWindows環境の両方で動作するには少し修正が必要かもしれないと思い、本記事を投稿します
- 参考した記事に含まれる内容も多く含まれますが、ご了承ください
前提
- フレームワークはDjango
- DBはPostgreSQL
- アプリケーションサーバはGunicorn
- WebサーバはNginx
- DB側のコンテナ名はdb、django側のコンテナ名はapp、nginx側はwebにします
- 開発用はdocker-compose.yml(Django+PostgreSQL)として作成します
- 本番用はdocker-compose.prod.yml(Django+PostgreSQL+Nginx)として作成します
- 作成するプロジェクト名はdjangopjにしていますが別のプロジェクト名で作成する際はdjangopjと置き換えて作成してください
ディレクトリ構成
初回作成時のディレクトリ構成は以下の通りです
containersフォルダを作成し、その中にdjango、nginxのフォルダを作成してください
また、nginxのフォルダの中にconf.dフォルダも作成します
tree
.
├── containers
│ ├── django
│ │ └── Dockerfile
│ └── nginx
│ ├── Dockerfile
│ └── conf.d
│ └── default.conf
├── .gitignore
├── .env
├── .env.prod
├── entrypoint.sh
├── docker-compose.prod.yml
├── docker-compose.yml
└── requirements.txt
各ファイルに必要なコードを記入しよう
Dockerfile(django)
# syntax=docker/dockerfile:1
# Pythonのイメージを指定
FROM python:3.9
# PYTHONDONTWRITEBYTECODEとPYTHONUNBUFFEREDはオプション
# pycファイル(および__pycache__)の生成を行わないようにする
ENV PYTHONDONTWRITEBYTECODE=1
# 標準出力・標準エラーのストリームのバッファリングを行わない
ENV PYTHONUNBUFFERED=1
# コンテナのワークディレクトリを/codeに指定
WORKDIR /code
# ローカルのrequirements.txtをコンテナの/codeフォルダ直下に置く
COPY ../../requirements.txt /code/
# コンテナ内でpipをアップグレード
RUN pip install --upgrade pip
# pip install -r requirements.txtを実行
RUN pip install -r requirements.txt
# ソースコードをコンテナにコピー
COPY ../.. /code/
# ヒストリを保存するディレクトリを作成
RUN mkdir /root/myhistory
# ヒストリファイルを作成
RUN touch /root/myhistory/.history
# ヒストリファイルのパーミッションを変更
RUN chmod 600 /root/myhistory/.history
nginx
FROM nginx:1.21-alpine
# ローカルのdefault.confをコンテナにコピー
COPY containers/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
default.conf(Nginx用の設定ファイル)
# Django(Gunicorn)の8000番ポートとつなぐ
upstream django {
# サーバにDjangoのコンテナ名を指定。今回はapp
# ポートはDjangoのコンテナの8000番ポート
server app:8000;
}
server {
# HTTPの80番ポートを指定
listen 80;
server_name 0.0.0.0;
# プロキシ設定
# 実際はNginxのコンテナにアクセスしてるのをDjangoにアクセスしてるかのようにみせる
location / {
proxy_pass http://django;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
# djangoの静的ファイル(HTML、CSS、Javascriptなど)を管理
location /static/ {
alias /static/;
}
}
docker-compose.yml(開発用)
services:
db:
image: postgres
ports:
- "5432:5432"
# M1チップでも動くように
# Intel Macの場合あってもなくても動く
platform: linux/x86_64
volumes:
- db_data:/var/lib/postgresql/data
env_file:
- .env
app:
build:
context: .
dockerfile: containers/django/Dockerfile
# ボリュームを指定
# ローカルのカレントディレクトリをコンテナの/codeにマウントする
# ローカルの/staticをコンテナの/staticにマウントする
# ローカルのapp-bashhistoryをコンテナの/root/myhistoryにマウントする
volumes:
- .:/code
- ./polls/static:/static
- app-bashhistory:/root/myhistory
ports:
- "8000:8000"
env_file:
- .env
depends_on:
- db
# シェルスクリプトを実行
command: >
sh -c "
python manage.py makemigrations --noinput &&
python manage.py migrate --noinput &&
python manage.py collectstatic --noinput &&
if [ \$DEBUG = 'True' ]; then
python manage.py runserver 0.0.0.0:8000;
else
gunicorn djangopj.wsgi:application --bind 0.0.0.0:8000;
fi
"
volumes:
db_data:
static:
app-bashhistory:
docker-compose.prod.yml(本番用)
services:
db:
image: postgres
ports:
- "5432:5432"
# M1チップでも動くように
# Intel Macの場合あってもなくても動く
platform: linux/x86_64
volumes:
- db_data:/var/lib/postgresql/data
env_file:
- .env.prod
app:
build:
context: .
dockerfile: containers/django/Dockerfile
# ボリュームを指定
# ローカルのカレントディレクトリをコンテナの/codeにマウントする
# ローカルの/staticをコンテナの/staticにマウントする
# ローカルのapp-bashhistoryをコンテナの/root/myhistoryにマウントする
volumes:
- .:/code
- ./polls/static:/static
- app-bashhistory:/root/myhistory
# 8000番ポートをNginx側が接続できるよう開く
expose:
- "8000"
env_file:
- .env.prod
depends_on:
- db
# シェルスクリプトを実行
command: >
sh -c "
python manage.py makemigrations --noinput &&
python manage.py migrate --noinput &&
python manage.py collectstatic --noinput &&
if [ \$DEBUG = 'True' ]; then
python manage.py runserver 0.0.0.0:8000;
else
gunicorn djangopj.wsgi:application --bind 0.0.0.0:8000;
fi
"
web:
# コンテナ名をwebに指定
container_name: web
# NginxのDockerfileをビルドする
build:
# ビルドコンテキストはカレントディレクトリ
context: .
dockerfile: containers/nginx/Dockerfile
# ボリュームを指定
# ローカルの/staticをコンテナの/staticにマウントする
volumes:
- ./polls/static:/static
# ローカルの80番ボートをコンテナの80番ポートとつなぐ
ports:
- "80:80"
# 先にappを起動してからwebを起動する
depends_on:
- app
volumes:
db_data:
static:
app-bashhistory:
requirements.txt
- Django
- psycopg2
- Gunicorn
をDjangoのコンテナにインストールするので記載します
Django>=3.0,<4.0
psycopg2>=2.8
gunicorn>=19.9.0,<20.0
.env(開発用)
PostgreSQLのrootユーザのパスワードなどをdocker-compose.ymlやDjangoのsettings.pyに書くのは危険なので.envファイルを使います
今回は開発用のためDEBUG=Trueにする必要があります
.gitignore(後述)があることで.envファイルはGitHubに上がることはありません
今回は以下のような内容にします
POSTGRES_DB=postgres
POSTGRES_NAME=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
# SECRET_KEYに任意の値を入力
SECRET_KEY=django
ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
# 開発環境のためTrue
DEBUG=True
.env.prod(本番用)
続いて本番環境用の環境変数を設定します
実際に本番環境の環境変数はAWSのパラメータストアなどに保存しますが今回は.env.prodを使います
本番用のためDEBUG=Falseにする必要があります
POSTGRES_DB=postgres
POSTGRES_NAME=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
# SECRET_KEYについては本番環境では推測されない値に変更しておきましょう
SECRET_KEY=xdmjx=9l@x)-jitznpb^%yjn6h=7g)$%e8_+1s)o+8o79csa4d
ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
# 開発環境のためFalse
DEBUG=False
.gitignore
GitHubの公式サイトからPythonの.gitignoreを作成します
.envと.env.prodは.gitignoreにないため追記します
また、migrationとstaticは実際の開発ではGitの管理下に置く必要がないため、こちらも.gitignoreに追記します(migrattionとstaticはプログラムによって生成されることがある)
.env
.env.prod
# ignore static files
static/
# ignore migration files
migrations/
docker-composeでDocker imageを作成しよう(初回)
プロジェクトを新規作成する際はプロジェクト名と作成するディレクトリを指定して以下のコマンドを実行します
今回はdjangopjのプロジェクトをカレントディレクトリに作成します
docker-compose -f docker-compose.prod.yml run app django-admin startproject djangopj .
実行するとローカルのディレクトリ構成は以下のようになります
data/db内はファイルが非常に多いので省略します
tree
.
├── containers
│ ├── django
│ │ └── Dockerfile
│ └── nginx
│ ├── Dockerfile
│ └── conf.d
│ └── default.conf
├── djangopj
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── .env
├── .env.prod
├── .gitignore
├── docker-compose.prod.yml
├── docker-compose.yml
├── manage.py
├── requirements.txt
└── static
settings.pyのDATABASESを変更
DjangoのデフォルトのDBはSQliteのため、PostgreSQLに変更する必要がある
from pathlib import Path
# osのモジュールをインポート
import os
# [・・・]
# SECRET_KEYを.envから取得
SECRET_KEY = os.environ.get("SECRET_KEY")
# DEBUGを.envから取得
# envファイルにTrue、Falseと書くとDjangoがString型と認識してしまいます
# os.environ.get("DEBUG") == "True"を満たすとboolean型のTrueになり、
# env内のDEBUGがTrue以外ならFalseになります
DEBUG = os.environ.get("DEBUG") == "True"
# ALLOWED_HOSTSを.envから取得
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS").split(" ")
# [・・・]
# PostgreSQLのパラメータを.envから取得
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('POSTGRES_NAME'),
'USER': os.environ.get('POSTGRES_USER'),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
'HOST': 'db',
'PORT': 5432,
}
}
# [・・・]
# 言語を日本語に設定
LANGUAGE_CODE = "ja"
# タイムゾーンをAsia/Tokyoに設定
TIME_ZONE = "Asia/Tokyo"
# [・・・]
# STATIC_ROOTを設定
# Djangoの管理者画面にHTML、CSS、Javascriptが適用されます
STATIC_ROOT = "/static/"
STATIC_URL = "/static/"
ローカル環境起動
docker compose -f docker-compose.prod.yml up --build -d
- -f オプションをつけると指定したファイルを使ってコンテナを起動します
- --build オプションをつけるとイメージを再ビルドします
- -d オプションをつけるとバックグラウンドで起動します
localhostにアクセスしてみよう
ホストからNginxのポートに接続します
ブラウザにアクセスし、NginxのポートからDjangoのポートへアクセスできます
以下のページが表示されます
このようにNot Foundと表示される場合はルーティングの設定をしてないからでコンテナ自体はうまく起動しています
localhost/adminにアクセスし、以下の画面が出たら成功です
DEBUG=Trueに設定した場合は下記の画像が表示されます
参考文献