Heroku に SPA on Django の Docker イメージをデプロイする
11/29/2020 1:48:15 PM
TL;DR
- Heroku に Docker イメージをデプロイします
- SPA を同一オリジンで Django から配信するアーキテクチャです
- Docker 関連以外の話はしません (静的ファイル配信、DB 等)
Heroku には Docker イメージもデプロイできる
Heroku といえば、Git からデプロイのイメージが強かったんですが、Docker イメージでのデプロイにも対応しているらしいです
Deploying with Docker | Heroku Dev Center
によると、
- 手元で Docker イメージをビルドして、Heroku に投げる
heroku.yml
を使って、リポジトリからイメージをビルドする
の 2 つがあります
この記事では、前者を使いますが、CI/CD を絡ませるなら、手元でビルドするのではなくheroku.yml
にしたがって Heroku 上でビルドできるので後者のが良さそうな印象でした
どちらも単一のイメージから建てるものですが、Heroku にはもともと Heroku Postgres があり、Docker イメージならマルチステージビルドも行えるので
- フロント: SPA / SSG
- バック: API サーバー
- RDB: Heroku Postgres
みたいなアーキテクチャなら対応できます
逆に、API サーバーも建てるけどフロントでは SSR したいとかは無理なはずなので、別の形を考える必要があると思います
SPA on Django のイメージを作る
Heroku では、
Using Multiple Buildpacks for an App | Heroku Dev Center
辺りを使うことで、 ビルドに Node.js と Python を使う、みたいなことはできるんですが、
プロジェクトルートからデーモン用のビルドパックを自動認識するので
- Django Project (
Python Runtime
)- ...
- frontend (
Node Runtime
)- ...
みたいなディレクトリ構成じゃないと(たぶん)ダメです
Docker のビルドならこの辺の縛りがないのでとても良きです
とりえあず
- backend (
Django Application
) - frontend (
Node Application
) - ...
みたいなディレクトリ構成ということにします
dockerfile# Build Frontend Staticfiles FROM node:14.15.1-stretch as front-build WORKDIR /frontend COPY ./frontend /frontend RUN yarn install RUN yarn build # frontend/dist にビルドされる # Build Backend Django Packages FROM python:3.8.6-buster WORKDIR /backend COPY ./backend /backend COPY /frontend/dist /frontend/dist RUN pip install --no-cache-dir -q -r requirements.txt CMD gunicorn config.wsgi:application --bind 0.0.0.0:$PORT # $PORTは実行時の環境変数から取得される
こんな感じでマルチステージビルドを使って、イメージが構築されます
CMD と ENTRYPOINT について
$ heroku run
のときの環境で Dockerfile の各値が使われます
WORKDIR
: カレントディレクトリENV
: 環境変数ENTRYPOINT
:$ heroku run <cmd>
→$ <ENTRYPOINT> <cmd>
てな感じ。
Docker-Compose のくせで、
こんな感じでdockerfile# ... ENTRYPOINT [ "./entrypoint.sh" ] CMD [ "gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:$PORT" ]
entrypoint.sh
を ENTRYPOINT
に指定して、CMD
を渡したんですけど、毎回 entrypoint.sh
が呼ばれてしまって $ heroku run
がまともに使えませんでした
なので、ENTRYPOINT
は指定せずに
dockerfile# ... CMD [ "./entrypoint.sh" ]
とする必要があります
起動前に走らせたいものがないなら、直接サーバーを起動しても良いですけど、配列型で指定すると変数が展開されないので注意が必要です
dockerfileCMD [ "gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:$PORT" ] # NG CMD gunicorn config.wsgi:application --bind 0.0.0.0:$PORT # OK
デプロイする
Heroku CLI
のセットアップとログイン、アプリ作成(heroku create
)がされていることは前提として、
で、ローカルのbash$ heroku container:push web
Dockerfile
を参照した Docker イメージのビルドが行われます
今回のやり方では、デプロイがリポジトリと連動しません
bash$ heroku container:release web
によって、ビルドされたイメージがリモートに送信され、デプロイされます
一連の流れとして、
deploy.shheroku container:push web if [ $? = "0" ]; then heroku container:release web heroku logs -t fi
辺りを定義しておくと良さそうでした
終わりに
以上です
実際にデプロイするには、
- Nginx 等を挟まないので、collectstatic して、whitenoise で配信する
- SPA のルーティング設定
- Heroku Postgres に環境変数から接続情報を取得して繋ぐ
とかとか、もろもろすることはありますが、その辺は主題じゃないので割愛しました
ステージング環境用に作ったんですが、結構良さそうだったので良ければぜひ。