初めてのDjangoアプリ2【Herokuへのデプロイ】

初めてのDjangoアプリ1【Python】の続きです。
作成したDjangoアプリをHerokuへデプロイします。
ちなみに筆者はインフラに関しての知識はほぼゼロで、当初はAWSを使っていたのですが学習コストが高過ぎて無事挫折しました……。
そこで救世主Herokuです!

Heroku CLI

まずは、Heroku CLI(旧Heroku Toolbelt)というコマンドラインツールをインストールします。
ツール内にGitも含まれているようで、元々OSにインストールしてあったものがダウングレードされてしまったのでその辺りは注意が必要かもしれません。
OSの環境変数にパスを通せば使えるようになります。

1
2
> heroku version
heroku-cli/6.99.0-ec9edad (win32-x64) node-v9.10.1

デプロイに必要なパッケージをインストールする

ここからは先ほどのDjangoプロジェクトの続きです。
pipでデプロイに必要なパッケージを追加インストールします。

1
(djp) E:\foo\djp\sampleproject> pip install django-heroku gunicorn

gunicornはWSGIサーバと呼ばれ、WebサーバとWebアプリケーションをつなぐためのサーバです。
django-herokuをインストールすると、dj-database-url、whitenoise、psycopg2が合わせて自動でインストールされます。
django-heroku → その名の通りDjango+Heroku環境のデプロイ設定を楽にしてくれるためのパッケージで、Heroku公式が出している。
dj-database-url → Herokuの環境変数DATABASE_URLのURLスキーマをパースしてくれるパッケージ
whitenoise → AWSのS3等の外部ストレージを使わずに静的ファイルをHerokuサーバーから配信するためのパッケージ
psycopg2 → PostgreSQLを使用するためのアダプタパッケージ
ただし、これらはdjango-herokuが内部的に使用するもので、直接触ることはないみたいですね。

settings.pyの末尾を以下のように編集して、ローカルとHerokuの設定を分岐させます。
Heroku環境のみdjango-herokuを使用します。

settings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...(中略)...
from socket import gethostname
HOSTNAME = gethostname()
#ローカル環境
if "my-PC" in HOSTNAME:
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'hogehogedb',
'USER': 'hogehogename',
'PASSWORD': 'hogehogepass',
}
}
#Heroku環境
else:
DEBUG = False
import django_heroku
django_heroku.settings(locals())

django-herokuに関しては、これだけでOKで、あとはこのモジュールがよしなに設定してくれます。
なお、初めてのDjangoアプリ1【Python】ではデータベースはmysqlを使っていたのですが、Herokuのデフォルトがpostgresqlなため変更しました。

広告

requirements.txt

requirements.txtを生成してパッケージの依存関係を記録しておきます。

1
(djp) E:\foo\djp\sampleproject> pip freeze > requirements.txt

中身はこんな感じです。

requirements.txt
1
2
3
4
5
6
7
dj-database-url==0.5.0
Django==2.0.6
django-heroku==0.3.1
gunicorn==19.8.1
psycopg2==2.7.4
pytz==2018.4
whitenoise==3.3.1

デプロイ時にHerokuはrequirements.txtを読み取ってパッケージをインストールしてくれます。
ちなみにrequirements.txtは、より高性能なPipfileという形式にこの先置き換わっていくようです。
Herokuのpythonサンプルではrequirements.txtではなくPipfileが実装されていますね)
ただし、現在筆者が使用しているCmderというコンソールと相性が悪いようで、うまく動かず今回は保留としました。

Herokuは、アプリケーションのルートにPipfileかrequirements.txtのどちらかが存在することによって、そのアプリケーションがPythonアプリであることを認識します。

Procfile

Procfileは、アプリケーションの起動に関する設定を記述しておくファイルです。
Procfileは、Heroku上のDynoと呼ばれるLinuxコンテナ(システムから独立したプロセス、みたいなイメージ?)の起動コマンドを記述したファイルです。
アプリケーションはDyno上で実行されます。
中身は以下の一行です。

web: gunicorn sampleapp.wsgi(20180907修正)

1
web: gunicorn sampleproject.wsgi

sampleproject

runtime.txt

Procfileとrequirements.txt以外にもう一つ、runtime.txtファイルが必要です。
中身は、動かすPythonのバージョンだけ書いてます。

1
python-3.6.5

この辺りの呪文は、よく分からんがそんなもんか~と割り切ってます。

makemigrations

ここがハマった箇所です。
この後Gitのリポジトリを作成してデプロイするのですが、一部adminページにアクセスした時にDjango error: relation “users_user” does not existのようなエラーが発生してしまいました(Debug=FalseではServer 500 Errorになる)

結論から言いますと、ローカルでmakemigrationsを実行させてGitにコミットしておかないと、Herokuサーバーは自動でマイグレーションファイル(0001_initial.pyみたいなやつ)を作らない、ということでした。

How heroku run python manage.py migrate?
django makemigrations and migrate on heroku server don’t create tables

なので、予めローカルでマイグレーションファイルを作成しておきます。

1
(djp) E:\foo\djp\sampleproject> python manage.py makemigrations

Gitリポジトリを作成

プロジェクトフォルダ(manage.pyがあるフォルダ)にGitのリポジトリを作成します。
.gitignoreは、Python.gitignoreからお借りしました。

1
2
3
(djp) E:\foo\djp\sampleproject> git init
(djp) E:\foo\djp\sampleproject> git add .
(djp) E:\foo\djp\sampleproject> git commit -m "hello Heroku !"

ファイルの構成

ファイル構成は、最終的にはこんな風になりました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sampleproject
├ .git
├ sampleapp
│ ├ migrations
│ │ ├ __init__.py
│ │ └ 0001_initial.py
│ ├ admin.py
│ ├ apps.py
│ ├ models.py
│ ├ tests.py
│ └ views.py
├ sampleproject
│ ├ settings.py
│ ├ urls.py
│ └ wsgi.py
├ .gitignore
├ manage.py
├ Procfile
├ requirements.txt
└ runtime.txt
広告

Herokuにアプリケーションを作成

1
heroku create [アプリ名]

アプリ名はオプションです。
ただし、アプリ名はHerokuユーザー全体で一意でないといけません。
書かなかった場合は、Herokuが自動で付けてくれます。
.gitのあるフォルダでcreateを実行します。

1
2
3
(djp) E:\foo\djp\sampleproject> heroku create
Creating app... done, ⬢ xxxxxxx-xxxxxxx-9999
https://xxxxxxx-xxxxxxx-9999.herokuapp.com/ | https://git.heroku.com/xxxxxxx-xxxxxxx-9999.git

アプリ作成後gitを確認すると、Herokuのリモートリポジトリが登録されています。
今後ここにpushしていく形なります。

1
2
3
(djp) E:\foo\djp\sampleproject> git config -l
(...中略...)
remote.heroku.url=https://git.heroku.com/xxxxxxx-xxxxxxx-9999.git

ちなみに、作成したHerokuアプリの一覧は、

1
heroku apps

Herokuアプリの削除は、

1
heroku apps:destroy -a [アプリ名] 

Herokuのリモートリポジトリにpush

それではいよいよデプロイします。
Herokuのリモートリポジトリにpushします。

1
2
3
4
(djp) E:\foo\djp\sampleproject> git push heroku master
Counting objects: 12, done.
Delta compression using up to 8 threads.
...中略...

staticファイルの扱いについて

上記コマンドでデプロイすると、Heroku側でcollectstatic(静的ファイルをディレクトリにまとめる機能)が自動実行されます。
django-herokuの実行コードを見ると、staticfiles関連の箇所でSTATIC_ROOTの設定とかMIDDLEWAREでwhitenoiseが適切に使用されているのが分かります。

DBのマイグレート

前述のマイグレーションファイルから、Heroku環境でマイグレートを実行してDBを初期化します。
heroku runでHeroku環境でコマンドを実行できます。

1
(djp) E:\foo\djp\sampleproject> heroku run python manage.py migrate

最後にcreatesuperuserします。

1
(djp) E:\foo\djp\sampleproject> heroku run python manage.py createsuperuser

これで、Server 500 Errorを吐くこともなくなり、Djangoのadminにもログインすることができました!

Heroku Django admin

駆け足でデプロイしていきましたが、以上です!
実際静的アセットに関しては、S3(AWS)を使うのが一般的のようです。


Django Girls
Django ドキュメント | Django documentation | Django
初心者がハマった、初めてのDjangoアプリのHerokuデプロイ


開発環境
WinOS : Windows7(64bit)
Python : 3.6.5(64bit)
Django : 2.0.6

広告