docker-composeでDjango開発環境を構築する
はじめに
docker-composeを使って単一ホストにサービス別コンテナをたて、DjangoによるWebアプリケーション開発環境を構築してみる。ホストはローカル(AWS EC2でも可)を想定している。
docker-composeは単一ホストでしか使用できず、状況に応じて動的リソースの割り当ての必要が生じる可能性のあるプロダクション環境には使用が推奨されていない。別途AWS ECS・EKSなどのコンテナ基盤を使うべきだが、それに関しては本記事には記載しない。
本記事の対象読者としては以下を想定している。
- pipenvを使った開発をしたことがある
- Djangoの公式チュートリアルは実施済み。
- Docker、docker-composeの基本的な知識は持っている。
目指すゴール
key | value |
---|---|
Host | Docker、docker-composeをインストールする。システムに必要なすべてのコンテナを格納する。ローカル環境/AWS EC2等を想定。 |
Container: web server | Gunicorn(WSGI HTTP Server)のリバースプロキシとして機能し、クライアントリクエストを処理する。ホストの1337番をコンテナの80番にポートフォワーディング。 |
Container: application server | Gunicorn(WSGI HTTP Server)上でDjangoアプリケーションを動かす。コンテナの8000番ポートをLISTEN状態にする。ホスト側の任意のフォルダをコンテナ側にマウントし、静的ファイルとメディアファイルの永続化を実現する。 |
Container: db server | PostgreSQLを動かす。コンテナの5432番ポートをLISTEN状態にする。ホスト側の任意のフォルダをコンテナ側にマウントし、DBデータの永続化を実現する。 |
ディレクトリ構成
django-on-docker
├── .env.development
├── docker-compose.development.yml
├── app <- application server container source
│ ├── Dockerfile.development
│ ├── entrypoint.development.sh
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── manage.py
│ ├── app <- Django project setting dir
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── uploads <- Django file upload app dir
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── upload.html
│ ├── tests.py
│ └── views.py
└── nginx <- web server container source
├── Dockerfile.development
└── nginx.development.conf
参考
- 本記事はこちらの記事(Dockerizing Django with Postgres, Gunicorn, and Nginx)を参考に内容をカスタマイズしている。
使用する技術
- Docker 19.03.x
- docker-compose 1.27.x
- Python 3.8.x
- Django 3.x
- Gunicorn 20.0.x
- Nginx 1.19.x
- PostgreSQL 12.x
- pipenv version 2018.11.26
プロジェクトのセットアップ
開発を行う環境の任意のディレクトリにプロジェクトを作成する。
パッケージ管理と仮想環境構築は pipenv で行う。
$ mkdir django-on-docker && cd django-on-docker
$ mkdir app && cd app
$ pipenv --python 3.8
$ ls
Pipfile
仮想環境に入って必要なパッケージをインストールする。
/app
ディレクトリで以下を実行。
$ pipenv shell
(app)$ pipenv install django gunicorn psycopg2-binary
(app)$ ls
Pipfile Pipfile.lock
Djangoプロジェクトを作成する。
/app
ディレクトリで以下を実行。
(app)$ django-admin.py startproject app .
マイグレーションファイルの生成と、生成されたマイグレーションファイルからDjangoモデルのDBテーブルを作成。
/app/
ディレクトリで以下を実行。
(app)$ python manage.py makemigrations
(app)$ python manage.py migrate
Django付属の開発サーバーを起動し、サイトにアクセス。
/app/
ディレクトリで以下を実行。
(app) $ python manage.py runserver
...
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
...
http://localhost:8000/ にアクセスすると、Djangoサイトのwelcomeページが表示される。
Django付属の開発サーバーを使うと、Dockerやwebサーバ(Nginx)、アプリケーションサーバ(Gunicorn/uWSGI)を使用しなくても開発が可能である。その場合、データベースは接続設定すればローカルにインストールしたPostgreSQLも使えるが、初期設定ではSQLite3が使われる。
マイグレーションを実行済みであれば app
に db.sqlite3
というデータファイルが生成されているはずなので、一旦 Ctrl+C
で開発サーバを停止し、SQLite3のデータファイルを確認してみる。
(app) $ ls
Pipfile Pipfile.lock app db.sqlite3 manage.py
db.sqlite3
ファイルを削除して再度マイグレーションをやり直せばデータのリセットが可能である。
本記事では、一歩進んでよりプロダクション環境のシステム構成に近づけるため、役割別の複数のコンテナを立てながら開発できる環境の構築を目指す。
アプリデータは別途たてるDBコンテナに登録するため、不要となるdb.sqlite3
を削除しておく。
現状のディレクトリ構成:
$ tree
.
└── app
├── Pipfile
├── Pipfile.lock
├── manage.py
└── app
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
Dockerの導入
applicationサーバのコンテナを構築するDockerfile
システムの中心となるapplicationサーバのコンテナのDockerfileを/app/Dockerfile.development
に作成する。Dockerfileは環境別に用意する必要があるため、ファイル名にサフィックスを付けている。
# pull official base image
FROM python:3.8.2-alpine
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# create Django directory for the app user
ENV APP_HOME=/usr/src/app
RUN mkdir -p $APP_HOME && \
mkdir $APP_HOME/staticfiles && \
mkdir $APP_HOME/mediafiles
# create the app user
RUN addgroup -S app && adduser -S app -G app
# set work directory
WORKDIR $APP_HOME
# install dependencies
COPY ./Pipfile .
COPY ./Pipfile.lock .
RUN apk update && \
apk add postgresql-dev gcc python3-dev musl-dev libpq && \
pip install --upgrade pip && \
pip install pipenv && \
pipenv install --dev --system
# copy entrypoint shell file
COPY ./entrypoint.development.sh $APP_HOME
# copy project
COPY . $APP_HOME
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
# run entrypoint shell file
ENTRYPOINT ["/usr/src/app/entrypoint.development.sh"]
Dockerイメージには、デプロイ速度向上や不要プロセスの発生防止・メモリ節約という理由によって不要なものを含めないことが推奨されている。そのため、イメージには軽量なPythonのalpineを使用する。
本記事では扱わないが、イメージの軽量化にはビルド環境とランタイム環境を分離できるDockerのマルチステージビルドなどの方法も存在する。
イメージレイヤーの増加に比例してイメージサイズも増加するため、イメージレイヤーを追加するRUN
、COPY
、ADD
コマンドの使用には注意する。無駄に増やさないようにするには、RUN
コマンドは&&
でつなげる、レイヤーキャッシュを有効活用するために変更がありそうな記述を後方に設定するなどの工夫が必要になる。
また、Dockerはデフォルトではコンテナプロセスをコンテナ内のrootユーザとして実行する。この状態では、もしコンテナへの攻撃者がコンテナから抜け出すことに成功した場合にホストのrootにアクセスできてしまう。そのため、コンテナ内のデフォルトユーザを新規作成し、そちらで操作するように変更している。
entrypoint.shの導入
Dockerはコンテナ起動時に実行するコマンドを一つだけ指定できる。これを利用すれば、シェルファイルに任意の処理を記載してENTRYPOINT
やCMD
コマンドで実行指定することにより、コンテナ起動時に任意の処理をまとめて実行させることができる。
ここでは、Dockerfile.development
のENTRYPOINT
コマンドで実行する/app/entrypoint.development.sh
を作成する。
#!/bin/sh
if ["$DATABASE" = "postgres"
then
echo "Waiting for postgres..."
while ! nc -z $DB_HOST $DB_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
# ex. Here is a list of commands you want to run after PostgreSQL is started.
# python manage.py flush --no-input
# python manage.py migrate
# python manage.py init_data
exec "$@"
(コメントアウトしているが)上記は、PostgreSQLの起動が確認できた後にDBデータ初期化とマイグレーションを実行し、新しい初期データを登録するDjangoのカスタムコマンドを実行している例である(init_data
はカスタム作成コマンド)。
ただしここに記載するコマンドはコンテナの起動時に都度実行されるので、それを考えた上で実行コマンドを選ぶ必要がある。例えば、docker-compose restart
の実行ごとのデータ初期化は不要(データの永続化をしたい)のであれば上記コマンドの記載は不要である。
docker-composeが実行できるようにパーミッションを変更しておく。
(app) $ chmod +x entrypoint.development.sh
現状のディレクトリ構成:
$ tree
.
└── app
├── Dockerfile.development
├── entrypoint.development.sh
├── Pipfile
├── Pipfile.lock
├── manage.py
└── app
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
webサーバのコンテナを構築するDockerfile
次に、Nginxのコンテナを追加する。これはapplicationサーバであるGunicornのリバースプロキシとして機能し、クライアントリクエストを処理し、静的ファイルを提供する。
/nginx/nginx.development.conf
upstream my_app {
server app:8000;
}
server {
listen 80;
location / {
proxy_pass http://my_app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /staticfiles/ {
alias /usr/src/app/staticfiles/;
}
location /mediafiles/ {
alias /usr/src/app/mediafiles/;
}
}
リダイレクト設定
アクセス元 | リダイレクト先 |
---|---|
webサーバコンテナの80番ポートで受け付けたhttpリクエスト | applicationサーバのコンテナの8000番ポート |
/staticfiles/ へのリクエスト | デザイン関連の静的ファイルが格納されている/usr/src/app/staticfiles/ |
/mediafiles/ へのリクエスト | アップロードファイルが格納されている/usr/src/app/mediafiles/ |
applicationサーバの/usr/src/app/staticfiles/
と/usr/src/app/mediafiles/
はapplicationサーバのコンテナのDockerfileで作成している。
後述するDjangoの設定により、静的ファイルとアップロードファイルは上記ディレクトリに格納される。
/nginx/Dockerfile.development
FROM nginx:1.19.0-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.development.conf /etc/nginx/conf.d
デフォルト設定ファイルを削除し、ホストの/nginx/nginx.development.conf
をコンテナにCOPYして使う。
現状のディレクトリ構成:
$ tree
.
├── app
│ ├── Dockerfile.development
│ ├── entrypoint.development.sh
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── manage.py
│ └── app
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── nginx
├── Dockerfile.development
└── nginx.development.conf
DBサーバのコンテナを構築するDockerfile
イメージはDockerHubのpostgresをそのまま使用するため、Dockerfileは不要。
環境変数の定義
Dockerの各コンテナで参照する環境変数を一元管理するために、環境別の専用の設定ファイルを定義する。
プロジェクトルートの直下に/.env.development
ファイルを作成する。環境変数は開発環境とプロダクション環境では値も種類も異なる可能性がある。そのため、読み込む環境をファイル名で識別できるように開発環境を表現するサフィックス.development
という値を付与しておく。
ここで定義された環境変数は、後述のdocker-compose設定ファイルに指定することによって各コンテナから参照できるようになる。
#########
# for app
#########
# 0:not debug mode / 1: debug mode
DEBUG=0
# Django unique secret key value
SECRET_KEY=foo
# Host names to allow access to django apps
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
# Database process name
DATABASE=postgres
# database connection settings
DB_ENGINE=django.db.backends.postgresql
DB_NAME=app
DB_USER=app
DB_PASSWORD=password
DB_HOST=db
DB_PORT=5432
#########
# for db
#########
POSTGRES_USER=app
POSTGRES_PASSWORD=password
POSTGRES_DB=app
Djangoの基本設定
/app/app/settings.py
を修正、追記する。
/.env.development
で設定している環境変数を参照していることに注目する。
import os
DEBUG = int(os.environ.get("DEBUG", default=0))
SECRET_KEY = os.environ.get("SECRET_KEY", "foo")
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(" ")
DATABASES = {
"default": {
"ENGINE": os.environ.get("DB_ENGINE"),
"NAME": os.environ.get("DB_NAME"),
"USER": os.environ.get("DB_USER"),
"PASSWORD": os.environ.get("DB_PASSWORD"),
"HOST": os.environ.get("DB_HOST"),
"PORT": os.environ.get("DB_PORT"),
}
}
# set static files directory and url path
STATIC_URL = '/staticfiles/'
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
# set upload files directory and url path
MEDIA_URL = "/mediafiles/"
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")
DjangoのURL設定
/app/app/urls.py
を以下に修正する。
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
]
if bool(settings.DEBUG):
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
docker-composeの設定
/docker-compose.development.yml
に以下を記載する。
version: "3.7"
services:
app:
build:
context: ./app
dockerfile: Dockerfile.development
command: gunicorn app.wsgi:application --bind 0.0.0.0:8000
working_dir: /usr/src/app/
volumes:
- ./app:/usr/src/app
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
expose:
- 8000
env_file:
- ./.env.development
depends_on:
- db
networks:
- front
- back
db:
image: postgres:12.0-alpine
volumes:
- db_data:/var/lib/postgresql/data/
env_file:
- ./.env.development
networks:
- back
nginx:
build:
context: ./nginx
dockerfile: Dockerfile.development
volumes:
- static_volume:/usr/src/app/staticfiles
- media_volume:/usr/src/app/mediafiles
ports:
- 1337:80
depends_on:
- app
networks:
- front
volumes:
db_data:
static_volume:
media_volume:
networks:
front:
external: false
back:
external: false
いままで作成してきたDockerファイルを各サービスに指定し、ポートやボリューム、ネットワークの設定をしている。以下詳細。
-
service.app
- buildコンテキストとDockerfileの指定。
- WSGIサーバの起動。
- 静的ファイル・メディアファイルの永続化のため、ホスト側の
volumes
設定ディレクトリをコンテナにマウントする。 - 開発コードをコンテナ内のコードと同期させるためのボリューム設定。
- applicationサーバコンテナを8000番ポートで受け付け。
- 環境変数ファイルの読み込み。
-
service.db
コンテナを起動させてからservice.app
コンテナを起動。ただし、service.db
の準備が整うまでは待たない。そのため、上記で記載したようにentorypoint.sh
でPostgreSQLの準備が整うまで待ってから実行する処理を記載する。 -
front
、back
の両方のネットワークに属する。
-
service.nginx
- buildコンテキストとDockerfileの指定。
- 静的ファイル・メディアファイルの永続化のため、ホスト側の
volumes
設定ディレクトリをコンテナにマウントする。 - httpリクエストを1337ポートで受け付け、webサーバコンテナを80番ポートで受け付け。
-
service.app
コンテナを起動させてからservice.nginx
コンテナを起動。 -
front
ネットワークに属する。
-
service.db
- buildイメージにDockerHubのpostgresを指定。
- DBデータの永続化のため、ホスト側のvolumes設定ディレクトリをコンテナにマウントする。
- 環境変数ファイルの読み込み。
-
back
ネットワークに属する。
-
volumes
- ホスト側にボリューム領域を作成する。
volumeについての補足
作成されたvolume
は以下で確認できる。
$ docker volume ls
DRIVER VOLUME NAME
local django-on-docker_db_data
local django-on-docker_media_volume
local django-on-docker_static_volume
$ docker volume inspect django-on-docker_db_data
[
{
"CreatedAt": "2020-10-24T04:20:27Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "django-on-docker",
"com.docker.compose.version": "1.27.4",
"com.docker.compose.volume": "db_data"
},
"Mountpoint": "/var/lib/docker/volumes/django-on-docker_db_data/_data",
"Name": "django-on-docker_db_data",
"Options": null,
"Scope": "local"
}
]
ただし、基本的にはホスト側のボリュームがどこに作成されているのかは気にする必要はない。
ボリュームのデータは永続化されているため、docker-compose stop
で各コンテナを停止しても消えることはない。しかし、docker-compose down
でイメージ、コンテナ、ボリューム、ネットワークを削除すると消えてしまうため注意。
これで、一通りの準備が整った状態となる。
現状のディレクトリ構成:
$ tree
.
├── .env.development
├── docker-compose.development.yml
├── app
│ ├── Dockerfile.development
│ ├── entrypoint.development.sh
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── manage.py
│ └── app
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── nginx
├── Dockerfile.development
└── nginx.development.conf
docker-composeを実行してシステムを起動する
最初に、プロジェクトのルートにてシステムの初期化に必要なコマンドを実行する。
以下のコマンドは、イメージのビルド時のみに実行する。
イメージをビルドし、各コンテナを起動
$ docker-compose -f docker-compose.development.yml up -d --build
...
$ docker-compose -f docker-compose.development.yml ps -a
Name Command State Ports
----------------------------------------------------------------------------------------
django-on-docker_app_1 /usr/src/app/entrypoint.de ... Up 8000/tcp
django-on-docker_db_1 docker-entrypoint.sh postgres Up 5432/tcp
django-on-docker_nginx_1 /docker-entrypoint.sh ngin ... Up 0.0.0.0:1337->80/tcp
コンテナが3つ起動していることが確認できる。
データの初期化 → マイグレーションファイル生成 → DBテーブルの生成
$ docker-compose -f docker-compose.development.yml exec app python manage.py flush --no-input
$ docker-compose -f docker-compose.development.yml exec app python manage.py makemigrations
$ docker-compose -f docker-compose.development.yml exec app python manage.py migrate
静的ファイルをapplicationサーバコンテナの/usr/src/app/staticfiles/
以下に配置
$ docker-compose -f docker-compose.development.yml exec app python manage.py collectstatic --no-input --clear
Django管理者ユーザの登録
$ docker-compose -f docker-compose.development.yml exec app python manage.py createsuperuser
Username (leave blank to use 'app'): [admin user name]
Email address: [admin user email]
Password: [admin user login password]
Password (again):
Superuser created successfully.
http://localhost:1337/admin/login/ にアクセスしてみると、Django管理サイトログイン画面が表示される。
上記で登録したDjango管理者ユーザのアカウントでログインしてみよう。
その他コマンド
各コンテナを一時停止させる。開発を一時的に停止させるときに使用。
$ docker-compose -f docker-compose.development.yml stop
停止させた各コンテナを起動させる。開発を再開させるときに使用。
$ docker-compose -f docker-compose.development.yml start
各コンテナをリスタートさせる。stop → start と同じ。
$ docker-compose -f docker-compose.development.yml restart
イメージ、コンテナ、ボリューム、ネットワークを削除する。イメージの再ビルドが必要となった時や、プロジェクトの削除時に使用。
$ docker-compose -f docker-compose.development.yml down
指定のコンテナのみ再起動する。開発でコードの更新が発生した場合に使用。
$ docker container restart django-on-docker_app_1
データの永続化を確認する
ファイルをアップロードできるアプリケーションをDjangoに追加し、コンテナのリスタートでデータの永続化が実現出来ているかを確認する。
上記で作成してきたイメージ・コンテナ・ボリューム・ネットワークを削除しておく。
$ docker-compose -f docker-compose.development.yml down --rmi all --volumes --remove-orphans
uploads
アプリを追加する
Djangoに/app
ディレクトリで以下を実行。
$ cd app
$ pipenv shell
(app)$ python manage.py startapp uploads
$ tree
.
├── .env.development
├── docker-compose.development.yml
├── app
│ ├── Dockerfile.development
│ ├── entrypoint.development.sh
│ ├── Pipfile
│ ├── Pipfile.lock
│ ├── manage.py
│ ├── app
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── uploads
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── nginx
├── Dockerfile.development
└── nginx.development.conf
/app/app/settings.py
の修正。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'uploads.apps.UploadsConfig', <- append
]
/app/app/urls.py
の修正。
from uploads.views import image_upload <- append
urlpatterns = [
path("uploads/", image_upload, name="uploads"), <- append
path('admin/', admin.site.urls),
]
/app/uploads/views.py
の修正。
from django.shortcuts import render
from django.core.files.storage import FileSystemStorage
def image_upload(request):
if request.method == "POST" and request.FILES["image_file"]:
image_file = request.FILES["image_file"]
fs = FileSystemStorage()
filename = fs.save(image_file.name, image_file)
image_url = fs.url(filename)
print(image_url)
return render(request, "upload.html", {
"image_url": image_url
})
return render(request, "upload.html")
/app/uploads/templates/upload.html
の追加。
{% block content %}
<form action="{% url "uploads" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="image_file">
<input type="submit" value="submit" />
</form>
{% if image_url %}
<p>File uploaded at: <a href="{{ image_url }}">{{ image_url }}</a></p>
{% endif %}
{% endblock %}
docker-composeを実行してシステムを起動、アップロードファイルの永続化を確認
上記と同様に以下を順次実行する。
$ docker-compose -f docker-compose.development.yml up -d --build
$ docker-compose -f docker-compose.development.yml exec app python manage.py flush --no-input
$ docker-compose -f docker-compose.development.yml exec app python manage.py makemigrations
$ docker-compose -f docker-compose.development.yml exec app python manage.py migrate
$ docker-compose -f docker-compose.development.yml exec app python manage.py collectstatic --no-input --clear
$ docker-compose -f docker-compose.development.yml exec app python manage.py createsuperuser
http://localhost:1337/uploads/ にアクセスしてみると、ファイルアップロード画面が表示される。
ファイルを選択肢、アップロードしてみよう。
表示されたリンクには、アップロードした画像ファイルが存在する。リンクをクリックし、ブラウザで画像を表示させてみる。
コンテナにアップロードファイルが存在することを確認する。
$ docker exec -it django-on-docker_app_1 ls ./mediafiles
Screen Shot 2020-10-24 at 14.53.45.png
コンテナを再起動させ、アップロードファイルの永続化を確認してみる。
$ docker-compose -f docker-compose.development.yml restart
Restarting django-on-docker_nginx_1 ... done
Restarting django-on-docker_app_1 ... done
Restarting django-on-docker_db_1 ... done
$ docker exec -it django-on-docker_app_1 ls ./mediafiles
Screen Shot 2020-10-24 at 14.53.45.png
まとめ
本記事では、単一ホストにwebサーバ・applicationサーバ・DBサーバそれぞれ別のコンテナを起動して連携させ、開発環境を構築してみた。プロダクション環境のデプロイは、ここで実施した作業の他に考えねばならないことが山ほどある。
- SSLの設定
- オートスケールの設定
- DBマイグレーション実行環境の構築
- CI/CDの設定
- 静的ファイルをS3などの外部ストレージに配置
- インフラコードの自動化
それについてはまた別途記事を書く予定。
本記事のsourceはこちら(https://github.com/dsonoda/django-on-docker)にあるので、気軽にこの内容を試してみたい方はReadmeのUsage
をご確認ください。おそらく5分もかからずに再現できると思います。
もし気に入っていただけたらGitHubスターを押して頂けると嬉しいです。
Discussion
djangoは情報が少ないので、大変助かりました!
ありがとうございます。お役に立てて幸いです!