🌏

DjangoアプリをHerokuにデプロイしました。

2023/04/07に公開

DjangoアプリをHerokuにデプロイしたので手順を記録します。
環境は、
macOS 12.6.4
Python 3.10.10
Django 4.0.2

基本的にはこのサイトを参考にさせてもらいました。大変助かりました。
DjangoアプリをHerokuにデプロイする方法

1.各種インストール

まずは各種インストールを実施します。

Heroku CLIインストール

Heroku Command Line Interface (CLI) を使用すると、Heroku アプリをターミナルから直接、作成および管理できます。ただし、Gitがインストールされていることが前提です。

% brew tap heroku/brew && brew install heroku

参考:The Heroku CLI
Homebrewでのインストールですが、早速エラーに遭遇しました。

Error: Your Command Line Tools are too outdated.
Update them from Software Update in System Preferences.

If that doesn't show you any updates, run:
  sudo rm -rf /Library/Developer/CommandLineTools
  sudo xcode-select --install

Alternatively, manually download them from:
  https://developer.apple.com/download/all/.
You should download the Command Line Tools for Xcode 13.4.

Command Line Tools が古いということなので、CommandLineToolsディレクトリのバックアップをとって、sudo rm -rf /Library/Developer/CommandLineToolssudo xcode-select --installを実施しました。
再度brew tap heroku/brew && brew install herokuを実行しインストール成功。
ちなみに、Appleのサイトの手動ダウンロードのリソースも探しましたが、見つけることができませんでした。

ここからは仮想環境でインストールします。

% source venv/bin/activate

gunicornインストール

Gunicornは、WSGIアプリケーション用の純粋な Python HTTP サーバーです。

% pip install gunicorn

参考:Gunicorn を使用した Python アプリケーションのデプロイ

django-herokuインストール

django-herokuは、DjangoアプリをHeroku上で動かす際に必要な諸々の設定を自動的に行ってくれます。

$ pip install django-heroku

ログを見ると、同時に以下のパッケージ等もインストールされたようです。というわけで、個別にwhitenoiseやdj-database-urlをインストール必要はなくなりました。

Successfully installed dj-database-url-1.3.0 django-heroku-0.3.1 psycopg2-2.9.5 typing-extensions-4.5.0 whitenoise-6.4.0

2.各種ファイル作成・編集

次に各種ファイルの作成と修正です。

Procfile作成

Procfileの作成場所は、プロジェクトのベースディレクトリ直下です。下記のProcfileに書き込む「設定ディレクトリ名.wsgi」とは、「(あなたのプロジェクトのwsgi.pyがあるディレクトリの名前).wsgi」のことになります。‘-‘を指定すると、標準エラー出力にログを出力するそうです。

$ echo web: gunicorn 設定ディレクトリ名.wsgi --log-file - > Procfile

参考:Getting Started on Heroku with Python
参考:Heroku ArchitectureThe / Procfile

requirements.txt作成

requirements.txtの作成場所も、プロジェクトのベースディレクトリ直下です。

$ pip freeze > requirements.txt

runtime.txt作成

Python ランタイムを指定するには、使用する正確なバージョン番号を宣言する runtime.txtファイルをプロジェクトのベースディレクトリに追加します。

$ echo python-3.10.10 > runtime.txt

参考:Specifying a Python Runtime

.gitignore作成

.gitignoreの作成場所も、プロジェクトのベースディレクトリ直下です。この他にも必要なものがあれば追記してください。

# dirs
__pycache__
venv
media

# files
db.sqlite3
.DS_Store
local_settings.py

settings.py の編集

データベース設定(その1)

dj_database_urlをimportします。

import dj_database_url #追加

Debug設定(その1)

Heroku環境用にデバッグのフラグをFalseにします。

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

ALLOWED_HOSTS設定

localhostとHerokuの両方を設定。

ALLOWED_HOSTS = ['127.0.0.1', '.herokuapp.com']

whitenoise設定

MIDDLEWAREのSecurityMiddlewareの直下にwhirenoiseを追加。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware', #追加
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

参考:Using WhiteNoise with Django|2. Enable WhiteNoise

データベース設定(その2)

SQLiteからPostgreSQLに変更。ただし値はダミー値を設定しておく。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'name',
        'USER': 'user',
        'PASSWORD': '',
        'HOST': 'host',
        'PORT': '',
    }
}

静的ファイル設定

静的ファイル収集用にPathを設定。Herokuではpython manage.py collectstatic が自動的に実行され、静的ファイルが1箇所に収集されます。

STATIC_ROOT = BASE_DIR / "staticfiles"

参考:Using WhiteNoise with Django | 1. Make sure staticfiles is configured correctly
ちなみに、STATIC_URLはSTATIC_URL = 'static/'ですが、古いバージョンの Django の場合、STATIC_URL = '/static/' と定義されている可能性があります。

Debug設定(その2)

以下は、settings.pyの最後の部分に記載します。
local_settings.pyファイルがない場合はHeroku環境の設定を実行し、local_settings.pyファイルがある場合はlocal_settingsをimportしデバッグ環境に変更する仕組み。

try:
    from .local_settings import *
except ImportError:
    pass

Heroku環境での設定

上記「Debug設定(その2)」の下に追記します。DEBUGFalseの時はHeroku環境。
Heroku環境の場合には、SECRET_KEYを環境変数から取得し、django_heroku.settings(locals())でHerokuの各種設定を実行。
また、dj_database_url.config()でデータベースの設定を取得し、上記「データベース設定(その2)」のDATABASES['default']の設定を上書きします。

if not DEBUG:
    SECRET_KEY = os.environ['SECRET_KEY']
    import django_heroku
    django_heroku.settings(locals())

    db_from_env = dj_database_url.config(conn_max_age=600, ssl_require=True)
    DATABASES['default'].update(db_from_env)

参考:Connecting to Heroku Postgres | Connecting with Django

SECRET_KEYの移動

settings.pyからSECRET_KEY = '***************************'を切り取り(削除し)、次のlocal_settings.pyに貼り付けます。(実際の'***************************'の部分はランダムな文字列です。)

local_settings.py の作成

local_settings.pyは、settings.pyと同じディレクトリに作成します。SECRET_KEYをsettings.pyから削除して、local_settings.pyに移動します。ローカルのBASE_DIRを取得します。DATABASESは、Djangoの初期設定のSQLiteをそのままコピーするか、PostgreSQLなどを使用している場合はそれぞれ必要な情報を設定します。最後にDEBUG = Trueとし、デバッグモードに設定します。

from pathlib import Path

#settings.pyからそのままコピー
SECRET_KEY = '***************************'

BASE_DIR = Path(__file__).resolve().parent.parent

#settings.pyからそのままコピー
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

DEBUG = True #ローカルでDebugできるようになります。

3.Gitにコミット

HerokuにデプロイするためにはGitで管理しておく必要があります。これまでの修正などをコミットしておいてください。まだGitで管理していなかった場合は、git initから始めてください。

% git add -A
% git commit -m "Deploy to Heroku"

4.Herokuにデプロイ

前提として、Herokuのユーザ登録と最低限 Dyno ECOの契約が必要です。残念ながら無料プランはなくなりました。

Herokuにログイン

% heroku login

以下のメッセージが出るので、q 以外のキーを押すとブラウザが開きます。

heroku: Press any key to open up the browser to login or q to exit: 

ブラウザでログインすると2要素認証の設定などが要求されたかと思います。手順に従って設定してください。私はスマホのSalesforce Authenticater アプリを使って認証しました。

Herokuのプロジェクトを作成

Herokuのプロジェクトを作成します。プロジェクト名が「MyHerokuProject」だとすると以下のようになります。

% heroku create MyHerokuProject

プロジェクト名が既に使われていると出たら、違うプロジェクト名を試します。成功したら以下のように出力されます。

Creating ⬢ MyHerokuProject... done
https://MyHerokuProject.herokuapp.com/ | https://git.heroku.com/MyHerokuProject.git

https://MyHerokuProject.herokuapp.com/はHerokuのアプリにアクセスするURLで、https://git.heroku.com/MyHerokuProject.gitはHerokuにデプロイするためのGitのリモートのURL。
この段階でGitのリモートを確認すると、

% git remote -v
heroku	https://git.heroku.com/MyHerokuProject.git (fetch)
heroku	https://git.heroku.com/MyHerokuProject.git (push)
origin	https://github.com/githubusername/MyDjangoProject.git (fetch)
origin	https://github.com/githubusername/MyDjangoProject.git (push)

このようにGithubの他に、Herokuのリモートが登録されています。
すでにHerokuにプロジェクトが登録澄みの場合は、

% heroku git:remote -a MyHerokuProject

として、gitのremoteへHerokuを追加します。
参考;Deployment / Deploying with Git / Deploying with Git | Create a Heroku Remote

SECRET_KEYをHerokuに設定

settings.pyにあったSECRET_KEYをHerokuにも登録します。

% heroku config:set SECRET_KEY='***************************'

HerokuにPush

% git push heroku main

参考:Deployment / Deploying with Git / Deploying with Git | Deploy Your Code
Pushするとビルドが始まります。
たくさんログが出ますが、このWARNINGはまだデータベースの環境変数が設定されていないので出てしまいます。

remote:        WARNING:root:No DATABASE_URL environment variable set, and so no databases setup

そして、以下のように自動的に heroku-postgresql がプロビジョニングされます。後で確認したところ Heroku Postgres Mini ($5.00/month) が適用されていました。

remote:  !     The following add-ons were automatically provisioned: heroku-postgresql. These add-ons may incur additional cost, which is prorated to the second. Run `heroku addons` for more info.

参考:Language SupportPythonHeroku Python Support | Database provisioning

というわけで無事にデプロイできたので、Herokuのプロセスを確認してみます。アプリで現在実行されている各タイプの dyno の数を確認するには、heroku ps コマンドを使用します。自動的にDynoが起動しています。

% heroku ps
Eco dyno hours quota remaining this month: 1000h 0m (100%)
Eco dyno usage for this app: 0h 0m (0%)
For more information on Eco dyno hours, see:
https://devcenter.heroku.com/articles/eco-dyno-hours

=== web (Eco): gunicorn config.wsgi --log-file - (1)
web.1: up 2023/04/02 22:38:37 +0900 (~ 19m ago)

データベース設定

次にデータベースのテーブル等を作成します。

% heroku run python manage.py migrate

そして、superuserを作成します。
ユーザ名とパスワードを設定してください。

% heroku run python manage.py createsuperuser

アプリを開く

これでアプリを開きます。

% heroku open

ブラウザが起動し、MyHerokuProject.herokuapp.com が開きます。

5.ディレクトリ階層

プロジェクトのベースディレクトリ配下の2階層までの構成。ここではプロジェクトのベースディレクトリを仮に「MyDjangoProject」、設定ディレクトリを「config」としています。

% tree MyDjangoProject -L 2   
MyDjangoProject
├── Procfile
├── README.md
├── myapp1
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── context_processors.py
│   ├── forms.py
│   ├── migrations
│   ├── models.py
│   ├── static
│   ├── templates
│   ├── templatetags
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── config
│   ├── __init__.py
│   ├── __pycache__
│   ├── asgi.py
│   ├── local_settings.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3
├── manage.py
├── media
│   └── uploads
├── requirements.txt
└── runtime.txt

このとき、画像のアップロードが失敗していたので、Cloudinary を使って修正した記事をUPしました。
https://zenn.dev/treefield/articles/012b1f469f750a

Discussion