【Django】django-on-herokuを使用して開発アプリをデプロイして公開


投稿日 2022年6月27日 >> 更新日 2023年6月23日

注意

この記事は、Herokuの無料プランが廃止される日(2022年11月28日)以前に投稿しているので、有料化された後とでは実装過程に問題が生じる恐れがあります。気になるという方は閲覧をご遠慮ください。

概要

DjangoアプリケーションをHerokuというクラウドサービスでデプロイし公開します。Herokuはアプリの開発環境やOSなどのプラットフォーム一式を提供するサービスなので「PaaS(Platform as a Service)」というサービスになります。

そんなデプロイ(Herokuサーバーに配置)を簡単にしてくれるのが「django-heroku」というパッケージです。データベースやStaticファイルの設定を自動で行ってくれるので非常に便利です。しかし現在は名前が変更され「django-heroku」から「django-on-heroku」となってリリースされています。
変更内容は以下の公式ドキュメントにあります。

ここでは「git」と「Heroku」のソフトウェアが必要となるので導入に関しては以下の記事をご覧ください。なお開発環境はWindows Subsystem for Linuxです。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.9
pip 9.0.1
git version 2.17.1
heroku 7.60.2
使用ライブラリ ライセンス
Django==3.2.10 BSD
django-on-heroku==1.1.2 MIT
gunicorn==20.1.0 MIT
$ pip install django-on-heroku gunicorn

DjangoアプリをHerokuサーバーにデプロイして公開

Herokuのリモートサーバーにデプロイするには、gitのローカルレポジトリを作成する必要があります。

ローカルレポジトリを最終的にHerokuサーバーへとプッシュするので、デプロイの準備と共にgitプロジェクトを初期化します。もちろんデプロイするレポジトリをクローンしてきても良いでしょう。

gitのレポジトリをコミットする前に、デプロイに必要なファイルをそれぞれ作成していきます。

Djangoプロジェクト直下(ローカル)のディレクトリ完成前と完成後は以下となります。

完成前

|--app
   |--...
   |--static
|--db.sqlite3
|--manage.py
|--project
   |--...
   |--settings.py
|--venv

完成後

|--.git
|--.gitignore
|--.env
|--app
   |--...
   |--static
|--Procfile
|--db.sqlite3
|--requirements.txt
|--runtime.txt
|--manage.py
|--project
   |--...
   |--settings.py
|--venv

herokuアプリの作成とリモートレポジトリの設定

Herokuがインストールされているのが確認できたらDjangoプロジェクトのアプリをHerokuで公開するためのHerokuアプリを作成します。

Herokuアプリの作成はデプロイを開始(Herokuリモートにプッシュ)する前に終わっていれば良いので、このタイミングでも後でもどちらでも良いです。

Herokuアプリの一覧を確認するには「heroku apps」もしくは「heroku list」と実行します。

$ heroku apps

アプリの作成は「heroku create アプリ名」を実行します。引数のアプリ名を除くとランダムにアプリ名が決めれらます。

※以下はランダムのアプリ名

$ heroku create

もう一度「heroku apps」で一覧を確認すると作成したアプリがリストされていると思います。

アプリ名を変更したい場合は「heroku apps:rename 変更アプリ名」と実行します。

$ heroku apps:rename new-app-name

この時点でカレントディレクトリにgitプロジェクトが作成されていれば自動でHerokuのリモートレポジトリが作成されています。

$ ls
...  .git  app  project  ...  ...

gitに設定されたリモートレポジトリの確認は「git remote -v」と実行します。

$ git remote -v
heroku  https://git.heroku.com/....git (fetch)
heroku  https://git.heroku.com/....git (push)

gitプロジェクトがまだだった場合は、gitプロジェクトの初期化をしてHerokuコマンドでリモートレポジトリを設定します。

$ git init

Herokuのリモートレポジトリを作成。アプリ名忘れてしまったら「heroku apps」コマンドで確認できます。リモートレポジトリの作成は以下のように実行します。

$ heroku git:remote -a アプリ名

これでHerokuアプリを作成してリモートレポジトリの設定が完了しました。

$ git remote -v
heroku  https://git.heroku.com/....git (fetch)
heroku  https://git.heroku.com/....git (push)

【runtime.txt】の作成

「runtime.txt」をDjangoのプロジェクト直下に配置することで、Herokuサーバーはその内容に記載されているPythonのバージョンを実行します。
なので「runtime.txt」には、Herokuサーバーに実行していもらいたいPythonのバージョンを指定することができます。

ただしHerokuのサポート対象外だとエラーとなってしまうので注意が必要です。

Herokuの公式サイトからサポートされているPythonのバージョンを確認することができます。

例えば以下のように設定するだけでHerokuは「runtime.txt」通りにPythonを環境を構築します。

$ echo python-3.7.13 > runtime.txt

ちなみに、「runtime.txt」を配置しないとデフォルトで設定されているPython環境が構築されるので、互換性が合えばすんなりデプロイできてしまいます。

【requirements.txt】の作成

「requirements.txt」にはpipでインストールしたPython外部ライブラリを設定します。

Herokuにデプロイされると自動でpipのインストールが始まり、「requirements.txt」に記載されたライブラリが一括インストールされます。

インストール用のテキスト名(requirements.txt)が一文字でも違うとデプロイされないので注意が必要です。

$ echo -e 'django\ndjango-on-heroku\ngunicorn' > requirements.txt

$ cat requirements.txt
django
django-on-heroku
gunicorn

【Procfile】の作成

「Procfile」にはPythonのHTTPサーバーであるgunicornコマンドを記載します。

Herokuサーバーは最終的にProcfileに記載されたコマンド通りにDjangoアプリを起動します。

記載例は、gunicornコマンドの引数に「wsgi.py」ファイルが配置されているプロジェクト名を指定します。「gunicorn プロジェクト名.wsgi」の先頭はプロセスタイプの「web:」と付けることでHerokuサーバーが自動的に起動してくれます。

gunicornコマンドの引数に与えるプロジェクト名が今一よく分からない場合は、「settings.py」ファイルに設定されている「WSGI_APPLICATION」に代入されている「プロジェクト名.wsgi」を確認すると良いです。

$ echo 'web: gunicorn project.wsgi' > Procfile

$ cat Procfile
web: gunicorn project.wsgi

【settings.py】の設定

「settings.py」で編集する部分は「SECRET_KEY」を空にすることと「django-on-heroku」のインポートとなります。

以下の「settings.py」は変更前の状態です。

# project/settings.py

...
# SECURITY WARNING: keep the secret key used in production secret!

SECRET_KEY = 'シークレットキー'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

...

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3',
         'NAME': BASE_DIR / 'db.sqlite3',
     }
}

...

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

...

※上下コードの説明は後ほど

以下の「settings.py」は「django-on-heroku」設定後の状態です。

# project/settings.py

...

# SECURITY WARNING: keep the secret key used in production secret!

SECRET_KEY = ''

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

...

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3',
         'NAME': BASE_DIR / 'db.sqlite3',
     }
}

...

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

...

# heroku settings

import django_on_heroku

django_on_heroku.settings(locals())

# Set DEBUG to False in remote

import os

path = 'venv'

if not os.path.isdir(path):
    DEBUG = False

上記変更点や「django-on-heroku」がどのように機能しているか。

  • 「SECRET_KEY」は空の文字列型に変更しています。Djangoのシークレットキーについての扱いは公式ドキュメント(SECRET_KEYを守る)にある通り、厳重に保管する必用があるという事で別途読み込ませるという方法です。その方法は後ほどHerokuサーバーの環境変数に設定するのでコピーしておきます。

  • 「DEBUG」に関しては「local_settings.py」などのローカル環境用で切り替えるというやり方があったりしますがここではローカル環境下でもHerokuデプロイのテストを行うことができるような設定をしています。

  • 「ALLOWED_HOSTS」に関しては「django-on-heroku」の機能で「*」が自動で代入されます。

  • 「DATABASES」に関してはsqlite3がデフォルトで設定されていますがHerokuデプロイのテストやHerokuサーバーにデプロイされる場合は「django-on-heroku」の機能によりHerokuサーバー側のPostgreSQLデータベースが設定されます。

  • Staticファイルに関しては本番環境のみ別ルートで配信するため「collectstatic」により各アプリ内の「static」ファイルを「STATIC_ROOT」に指定されたディレクトリに集約したりしますが、「django-on-heroku」の機能により自動で「staticfiles」ディレクトリとして構成されます。

  • 上記の構成(SECRET_KEY・ALLOWED_HOSTS・DATABASES・STATIC_ROOT)は全て「django-on-heroku」が自動で設定してくれます。SECRET_KEYはHerokuサーバーの環境変数に設定することで自動で読み込まれます。

  • 最下層にあるコードは、「venv」ディレクトリが存在するかしないかでローカルとリモートどちらかを判断する条件分岐です。ローカルでは「venv」として仮想環境を構築しています。後に「.gitignore」によってこのディレクトリを除外するので、Herokuサーバー内に「venv」が存在していなければDEBUGは「False」という事になります。

Mediaファイルを扱う場合は「django-storages」というライブラリが便利です。「django-storages」では様々なストレージサービスの設定がサポートされており、サポート対象の各ストレージサービスのアカウントを持っていれば簡単に設定してHerokuのような環境でもMediaファイルを扱う事ができます。
以下の記事は「Dropbox」というストレージサービスを利用した「django-storages」の実装です。ぜひ参考にしてみてください。

【環境変数】の設定と作成

「settings.py」の設定でDjangoプロジェクト下の「SECRET_KEY」をセキュリティ上の理由で空にしました。よってDjangoアプリは起動できない状態となっていますが、「django-on-heroku」の機能により自動でHerokuサーバーの環境変数から「SECRET_KEY」を読み込んでくれます。
まだHerokuサーバーの環境変数に何も設定していないのでDjangoプロジェクトの「SECRET_KEY」を「heroku config:set ...」コマンドで設定します。

$ heroku config:set SECRET_KEY='djangoのシークレットキー'

Herokuサーバーの環境変数の確認は「heroku config」コマンドです。

$ heroku config
=== heroku.apps Config Vars
SECRET_KEY:  djangoのシークレットキー

「django-on-heroku」が自動で読み込んでくれる環境変数はSECRET_KEY以外にも「DATABASE_URL」に設定されたHerokuaサーバーが提供してくれるフリーのデータベースです。この「DATABASE_URL」がDjangoプロジェクトの「DATABASES」に代入されます。
「DATABASE_URL」が設定されるタイミングはgitコマンドでherokuのリモートレポジトリにプッシュしデプロイが完了された後にデータベースを初期化した時です。

【.gitignore】の作成

「.gitignore」ファイルを作成してherokuのリモートレポジトリに送信したくないファイルを設定します。

ディレクトリ構造は以下のような例とします。

|--.git
|--.gitignore
|--app
   |--...
   |--static
|--Procfile
|--db.sqlite3
|--requirements.txt
|--runtime.txt
|--manage.py
|--project
   |--...
   |--settings.py
|--venv

除外させるファイルは以下のように設定しました。

venv
*.pyc
db.sqlite3
staticfiles
  • venvはローカルで使う仮想環境用のディレクトリ

  • *.pycはPythonのキャッシュファイル

  • db.sqlite3は本番もしくはHerokuサーバーで使わないので除外

  • staticfilesはローカルでテストする際に「django-on-heroku」の機能によって自動作成されるので除外

上記ファイル群を「.gitignore」に書き込みます。

$ echo -e 'venv\n*.pyc\ndb.sqlite3\nstaticfiles' > .gitignore

$ cat .gitignore
venv
*.pyc
db.sqlite3
staticfiles

他にも後々の開発過程で除外する必要になってくるファイルは「.gitignore」に設定します。

デプロイしてデータベースの初期化

herokuのリモートレポジトリにプッシュしサーバーでデプロイしている最中エラーが発生しなければ完了です。

gitのローカルレポジトリをリモートレポジトリに送信するには必ず「追加」「コミット」の流れが必要です。
追加する必要のあるファイルを確認するには「git status」コマンドを実行します。

$ git status

赤く表示されていれば追加対象となるので「git add ...」コマンドで追加します。

※「git add .」でカレントディレクトリにあるファイルを全て追加

$ git add .

追加されたら「git commit -m 'コメント'」コマンドでコミットします。

$ git commit -m 'コメント'

最後に「git push」コマンドの引数に「リモートレポジトリ名」と「ブランチ名」を指定して送信します。
ブランチ名の確認は「git branch -v」コマンドです。

$ git branch -v
* master e5e451e コメント

「master」ブランチであれば「git push heroku master」と実行してリモートレポジトリに送信します。

$ git push heroku master

最後にHerokuサーバーにデプロイされたDjangoプロジェクトのデータベースを初期化し必要あればスーパーユーザーを作成します。
Herokuサーバーをリモートで操作するには「heroku run」コマンドの引数に実行させたいコマンドを実行します。

例えばHerokuサーバー内にあるDjangoプロジェクトのディレクトリを確認するには「heroku run ls」と実行します。

$ heroku run ls
Running ls on ⬢ app... up, run.1178 (Free)
manage.py  Procfile  project  requirements.txt  runtime.txt  staticfiles  app

データベースの初期化を行います。

$ heroku run python3 manage.py migrate

この時点でHerokuサーバーの環境変数には「DATABASE_URL」としてPostgreSQLデータベースが設定されています。

$ heroku config
=== app Config Vars
DATABASE_URL:  データベースURL
SECRET_KEY:  Djangoのシークレットキー

Djangoプロジェクトの初期設定が終わったらHerokuサーバーにデプロイされたDjangoプロジェクトをブラウザから開きます。「heroku open」コマンドを実行することでHerokuアプリのURLを取得できます。

$ heroku open
Manually visit https://app.herokuapp.com/ in your browser.

上記URLにアクセス。

【.env】ファイルを作成しローカルでherokuアプリのテスト

「.env」ファイルを作成することによってローカルからherokuアプリのテストを行うことができます。

開発環境下(ローカル)でHerokuアプリさながらの実行を行いたい場合は環境変数を「.env」ファイルに設定することです。
以下のようにするとHerokuサーバーに設定されている環境変数を取得し書き込まれます。

$ heroku config:get 環境変数1 環境変数2 -s > .env

$ cat .env
DATABASE_URL='データベースURL'
SECRET_KEY='シークレットキー'

「.env」ファイルにはシークレットキーなどが格納されているためgitのレポジトリからは除外しておきます。

$ echo '.env' >> .gitignore

Herokuアプリのテストをローカルで実行する場合は「heroku local」コマンドを実行します。

※このコマンドは「Procfile」に設定されたプロセスタイプのコマンドを実行します。

$ heroku local
[OKAY] Loaded ENV .env File as KEY=VALUE Format
...
...

データベースはHerokuで提供されているフリーのデータベースを使用することができます。

補足

なぜ「git push heroku master」でローカルレポジトリをHerokuのリモートレポジトリに送信する前に「.env」を作成し「heroku local」によるHerokuテストを行わなかったというと、Herokuテストで起動されたアプリはHerokuの環境資源を使用するので「Heroku-PostgreSQL」データベースが未設定のままだからです。
Herokuデータベースの作成は「heroku addons:create heroku-postgresql:プラン」コマンドでDATABASE_URLを生成できるのですが、Herokuのリモート環境にレポジトリが存在しないのでマイグレートコマンドによるデータベースを初期化することができません。
よってリモートレポジトリに送信したあと、Herokuのリモート環境でDjangoのマイグレートコマンドを実行し一度データベースの初期化を行う必要があります。もちろん管理画面を使うのであればスーパーユーザーの設定も必要です。

それでは以上となります。

最後までご覧いただきありがとうございました。