一年ぶりにHerokuを触ってみたのですが、ほとんど忘れていました。
また、HerokuでDjangoの環境構築を行う上で、リソースが少なかったので同じ境遇の方の助けになれればと思います。
Herokuでは便利なことにDockerのイメージをそのままデプロイ出来るようになったそうです。
これが無料で出来るとはSalceforceさんも太っ腹ですね。
前置きはさておき早速説明を始めますね。
環境
# 実行環境
macOS: Monterey(12.3.1)
shell: zsh 5.8(x86_64-apple-darwin21.0)
Python: 3.9.7
Docker: 20.10.8
docker-compose: 1.29.2
# 主要ライブラリ
django-heroku==0.3.1
django-mysql==4.7.0
django==4.0.5
djangorestframework==3.13.1
guicorn==20.1.0
urllib3==1.26.9
whitenoise==6.2.0
ライブラリのインストール
$ pipenv install django-heroku gunicorn whitenoise
Error: pg_config executable not found.
django-heroku
をインストールする際にError: pg_config executable not found.
とエラーが出る場合はpostgresql
をインストール。
$ brew install postgresql
settings.pyの変更
settings.py
と同じディレクトリにsettings_local.py
を作成してください。
そして、settings.py
をsettings_local.py
にコピペし、settings.py
を変更してください。
import os
import django_heroku
from urllib.parse import urlparse
from .settings_local import *
DEBUG = False
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# static setting
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
if not DEBUG:
# Heroku settings
# HerokuのConfigを読み込み
django_heroku.settings(locals())
また、settings_local.py
のミドルウェア(一番上)にwhitenoise.middleware.WhiteNoiseMiddleware
を追加。
...
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware',
...
]
...
厳密に言うとdjango.middleware.security.SecurityMiddleware
の下に配置すれば良いのですが、あまり変化はないため一番上に追加します。
プラットフォーム指定(M1・Mac使用者)
M1チップ搭載のMacbookを使用している方は、Dockerfile
に--platform=linux/amd64
を指定してください。
FROM --platform=linux/amd64 python:3.9.7-alpine
このプラットフォームの指定を行わないとError: Exec format error
とHeroku上でエラーが起き、アプリケーションを稼働させることができません。
Dockerfile
Docker
ファイルの最後に以下を追加。
...
RUN python3 manage.py collectstatic --noinput
CMD gunicorn --bind 0.0.0.0:$PORT app.wsgi
このCMD~
がProcfileの代わりとなります。
念の為私が使っているDockerfile
を載せておきます。参考程度に使ってください。
FROM --platform=linux/amd64 python:3.9.7-alpine
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONUTF8=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
APP_HOME=/usr/src/app
RUN addgroup -S app && adduser -S app -G app
RUN mkdir -p $APP_HOME && \
mkdir $APP_HOME/staticfiles
WORKDIR $APP_HOME
COPY . $APP_HOME
RUN apk update && \
apk --no-cache add gcc python3-dev mysql-client mysql-dev postgresql-dev musl-dev libffi-dev tzdata && \
pip install --upgrade pip && \
pip install pipenv && \
pipenv install --system --ignore-pipfile --deploy
RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime
RUN chown -R app:app $APP_HOME
USER app
RUN python3 manage.py collectstatic --noinput
CMD gunicorn --bind 0.0.0.0:$PORT app.wsgi
heroku.yml
ディレクトリ直下にheroku.yml
を作成し、以下をコピペ。
build:
docker:
web: Dockerfile
Heroku設定
ログイン
$ heroku login && heroku container:login
アプリ作成
$ heroku create [heroku-app-name]
DB設定
ClearDBをアプリに追加。
$ heroku addons:add cleardb:ignite
JawsDBのすゝめ(2022年6月1日追記)
Heroku上でMySQLのバージョンが5.7以上を使用する場合はJawsDB
が推奨されているそうなので、特に理由はない限りJawsDBを使用しましょう。
念の為、ClearDB・JawsDB両方の設定方法を記述します。
JawsDBの追加方法
$ heroku addons:create jawsdb:kitefin --version=8.0 // 任意のバージョンを指定
次に、ClearDBのデータベースURLを確認しコピーします。
$ heroku config
CLEARDB_DATABASE_URL: mysql://***:***@***/***?reconnect=true // ClearDB
JAWSDB_URL: mysql://***:***@***/*** // JawsDB
環境変数
環境変数にDATABSE_URL
を追加。
DATABASE_URL = mysql://***:***@***/***
ClearDBを使用した場合?reconnect=true
は取り除いちゃってください。
settings.py
冒頭に修正したsettings.py
にClearDBの設定を追加。
import os
import django_heroku
from urllib.parse import urlparse
from .settings_local import *
DEBUG = False
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# static setting
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
if not DEBUG:
# Heroku settings
# ClearDB setting
if 'DATABASES' not in locals():
DATABASES = {}
if 'DATABASE_URL' in os.environ:
url = urlparse(os.environ['DATABASE_URL'])
# Ensure default database exists.
DATABASES['default'] = DATABASES.get('default', {})
# Update with environment configuration.
DATABASES['default'].update({
'ENGINE': 'django.db.backends.mysql',
'NAME': url.path[1:],
'USER': url.username,
'PASSWORD': url.password,
'HOST': url.hostname,
'PORT': url.port,
})
django_heroku.settings(locals())
スタック設定
$ heroku stack:set container
Dockerイメージをデプロイ
$ heroku container:push web && heroku container:release web
スケール設定
$ heroku ps:scale web=1
マイグレーション
$ heroku run python3 manage.py migrate
管理者ユーザー作成
$ heroku run python3 manage.py createsuperuser
アプリ状態確認
$ heroku open
いつもどおりの画面が開けばOK
補足
Procfileは不要
途中にも書いたのですが、Dockerfile
のCMD
がProcfileの代わりとなっています。
STATICFILES_DIRSは不要
Heroku上での環境構築を行うにあたって、静的ファイルを読み込めないエラーに苦戦しました。
結果、settings.pyに以下の2文を付け加えるだけ。
...
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
...
色々情報は出回ってますが、これが現時点での最適解かと思われます。
また、whitenoise
に関連するミドルウェアの追加も忘れずに。
ALLOWED_HOSTS
Heroku上で作業を行う上で、settings_local.py
にあるALLOWDED_HOSTS
にHerokuのホストを指定してください。
...
ALLOWED_HOSTS = ['[アプリ名].herokuapp.com']
...
追記:スリープ
Herokuの無料枠では30分アクセスがないとサーバーがスリープ状態に入ります。
それを防ぐためには様々な方法があります。
こちらの記事を見ていただければ大概のことは解決できます。
ただし、少し調べてみると、メジャーなやり方でもあるScheduler
だと時々実行されない、意図せぬ課金が発生などの問題もあるそうです。
なので、私はdjango-crontab
を使って25分毎にHerokuにアクセスするように設定しました。
最後に
Djangoのリソースが出回っておらず、かなり苦戦しましたが動いてくれてよかったです。
何か不明な点や上手く動作しないということがあれば気軽にコメントにて質問してください。
以上、「Django+MySQL+Dockerの環境をHerokuにデプロイする」でした!
Thank you for reading
参考記事
以下、この記事を書くにあたって参考にさせていただいた記事となります。
・https://qiita.com/rebi/items/efd1c36f0a9e46222d80
・https://zenn.dev/daku10/articles/m1-heroku-container-trouble-exec-format-error
・https://qiita.com/akirakudo/items/16a01271b0a39316e439
・https://devcenter.heroku.com/articles/cleardb#using-cleardb-with-python-django