DjangoBrothers BLOG

2018/11/11

このエントリーをはてなブックマークに追加
デプロイ Django Heroku

DjangoアプリをHerokuでデプロイする手順

開発したDjangoアプリを、Herokuというサービスを使ってWebサーバーにデプロイする方法を説明します。デプロイができれば、自分のサービスが世界中に公開され、多くの人にアクセスしてもらうことができるようになります!

django-herokuという、便利なライブラリが公開されたおかげで、Djangoアプリは以前と比べてだいぶ簡単にデプロイできるようになりました。とはいえ、デプロイにはsettings.pyファイルの編集、git、Webサーバー等、初心者にとっては馴染みの薄い知識が少なからず必要となってくるため、つまづいてしまう人も多いのではないかと思います。

そこで今回は、初心者を対象として、Djangoアプリをデプロイする方法について説明していきたいと思います。単に手順だけではなく、それぞれのファイルがどんな役割を果たしているのかも合わせて説明していきます。

手順

デプロイするまでの手順は大きく分けると以下の通りです。

  1. デプロイ前に必要な設定をアプリに加える
  2. Herokuを操作してデプロイする

前提

  • この記事では、mysiteという名前のプロジェクトを作成したと仮定します。mysiteと書いてある部分は、自分のアプリのプロジェクト名に置き換えて実行しないとエラーの元となりますので、注意してください。
  • 今回のデプロイ方法ではdjango-herokuというパッケージを使いますが、このパッケージがサポートしているPythonのバージョンはPython3系のみですので注意してください。Djangoのバージョンは2.0が対象となっていますが、それより古いバージョンでも互換性はあります。
  • gitに関する基礎的な知識が必要となります。

1. デプロイ前に必要な設定

では、さっそく準備をしていきましょう。Herokuでデプロイするためには、アプリに対していくつか変更を加えてHeroku用の設定をしてあげる必要があります。

アプリをgit管理下に置く

Herokuでデプロイするために、まずはアプリをgitで管理するようにします。git initコマンドでgitでの管理ができるようになりますが、その前に.gitignoreファイルを作りましょう。(既にgitで管理している場合は、git initは不要です。)

ルートディレクトリに、.gitignoreというファイルを新規作成してください。最初のドットも忘れずにファイル名に入れてください。

.gitignoreに記述されたファイルは、git管理下から除外されることになります。.gitignoreに何を定義するかは、アプリごとによって変わってきますが、以下の例は.gitignoreに記述されることが比較的多いファイルになります。特別な理由がなければこの例にならって追加しておきましょう。

~/mysite/.gitignore

*.pyc
*.swp
db.sqlite3
__pycache__
.DS_Store
staticfiles

追加したら、これまで加えた変更をコミットしておきます。

~/mysite

$ git init
$ git add -A
$ git commit -m "Initial commit"

これで、.gitignoreに記述されたファイル以外はgit管理下に置かれることになりました。

requirements.txtで必要なパッケージを定義

次に、requirements.txtというファイルをルートディレクトリに作ります。このファイルはrequirements(必需品)という名の通り、アプリを動かすために必要なパッケージを一覧としてまとめておくファイルです。自分が開発したアプリに応じて、必要となるパッケージを記述します。

自分の開発環境にインストールされているパッケージを一覧として取得するには、pip freezeコマンドが使えます。また、pip freeze > requirements.txtと打つことでパッケージの一覧を自動的にrequirements.txtに記述することができますので、試してみましょう。

~/mysite

$ touch requirements.txt
$ pip freeze > requirements.txt

requirements.txtはこんな感じになるはずです。

~/mysite/requirements.txt

Django==2.1.3
pytz==2018.7

Herokuでは、ルートディレクトリにrequirements.txtが存在していることで、そのアプリがPythonアプリであると認識してくれます。また、アプリがデプロイされる時、Herokuが自動的にrequirements.txtを読み込んで、定義されているパッケージを本番環境のWebサーバーにインストールしてくれます。

Procfileを作る

Procfileとは、アプリのプロセスのタイプやエントリーポイントを宣言するファイルです。詳細についてはちょっと難しい話になりますのでここでは説明を割愛します。興味があれば調べてみてください。

このファイルもルートディレクトリに作成します。

~/mysite/Procfile

web: gunicorn mysite.wsgi

「web」の部分はプロセスタイプを指定しています。

「mysite」の部分は、自分が作ったアプリのプロジェクトディレクトリ名を指定します。mysiteの下にデフォルトで作られている、wsgiというファイルの設定を使ってサーバーを動かすということを意味しています。

「gunicorn」とは、HerokuでDjangoをデプロイするときに推奨されている、Webサーバーのことです。

gunicornを使うには、パッケージをインストールする必要があります。インストールしたら、requirements.txtに忘れずに追記しておきましょう。

~/mysite

$ pip install gunicorn==19.9.0

~/mysite/requirements.txt

###
省略
###
gunicorn==19.9.0  # 追記

runtime.txtでPythonのバージョンを定義

次にルートディレクトリに、runtime.txtというファイルを作成します。runtime.txtは「プログラム実行時に必要なもの」を定義します。つまり、ここでは使用するPythonのバージョンを指定します。このファイルでは、スペースなど余計なものが含まれているとエラーとなりますので注意してください。

~/mysite/runtime.txt

python-3.6.6

django-herokuを使った設定

django-herokuは、無料で公開されているとても便利なパッケージです。DjangoアプリをHeroku上で動かす際に必要な諸々の設定を自動的に行ってくれます。pipコマンドでインストールできます。

~/mysite

$ pip install django-heroku==0.3.1

django-herokuをインストールすると、whitenoiseやdj-database-urlという他のパッケージも複数同時にインストールされます。インストールが完了したら、pip freeze > requirements.txtコマンドを打ちましょう。requirements.txtはこのようになります。

~/mysite/requirements.txt

dj-database-url==0.5.0
Django==2.1.3
django-heroku==0.3.1
gunicorn==19.9.0
psycopg2==2.7.5
pytz==2018.7
whitenoise==4.1

django-herokuの機能はsettings.pyファイルで使います。まずは、ファイル冒頭でインポートをします。

~/mysite/mysite/settings.py

# 注意! ハイフンではなく、アンダースコア 
import django_heroku

###
省略
###

インポートしたら、以下の記述を一番下に追加してください。

~/mysite/mysite/settings.py

###
省略
###

django_heroku.settings(locals())

これにより、django-herokuで定義されている、settings関数が実行されることになります。このsettings関数が、デプロイに必要な諸々の設定をしてくれるというわけです。コードを見るとなんとなく推測できるかと思いますが、データベース、静的ファイル、シークレットキー等についての設定を行ってくれます。

もし、シークレットキーや静的ファイルの設定をdjango-herokuに任せるのではなく、独自に設定したい場合は、django_heroku.settings(locals(), secret_key=False, staticfiles=False)のように引数でFalseを指定すれば、それに関する設定は自動で行われません。

locals()というのはpythonの書き方で、その環境内で定義されている変数を辞書として取得しています。

これでアプリに加える変更はおわりです!

Herokuを操作してデプロイする

デプロイする準備ができましたので、実際にHerokuを使っていきましょう。

まずは、このページからHerokuのアカウントを作ってください。

次に、このページから、自分のOSに合わせてHeroku CLI(Command Line Interface)をインストールします。これにより、コマンドライン上からHerokuの操作が行えるようになります。

Heroku CLIが無事にインストールできたら、herokuコマンドが使えるようになりますので、まずは以下のようにログインします。Herokuアカウントを作成したときの情報を打ち込んでログインしてください。

~/mysite

$ heroku login

heroku createコマンド

次に、heroku createコマンドを使います。このコマンドの後ろには、heroku create djangobros-mysiteのように自分の好きなプロジェクト名をつけることができます。プロジェクト名を指定しない場合は、自動的にランダムな名前がプロジェクト名として付与されます。

このコマンドは、プロジェクトルートディレクトリ(今回の場合は"~/mysite")にいる状態で打ってください。

~/mysite

$ heroku create djangobros-mysite

コマンドを打った時に、urlが二つ表示されれば成功です。既に他の人に使われているプロジェクト名を指定した場合は、Name djangobros-mysite is already takenのようなエラーが出ますので、他のプロジェクト名を考えてコマンドを打ち直してください。

heroku createコマンドによって、プロジェクトのドメインが自動で取得されるのと同時に、Heroku上にリモートリポジトリが作成されます。

先ほど2つURLが表示されましたが、1つめがプロジェクトのドメインです。2つ目はリモートリポジトリのURLです。プロジェクトのURLにアクセスするとこんな画面が表示されるでしょう。

リモートリポジトリを一覧として取得するgit remoteコマンドを打てば、"heroku"と表示されることが確認できるはずです。git remote -vコマンドでは、リポートリポジトリのURLも表示できます。

また、Herokuのページでも、プロジェクトが追加されていることが確認できます。

Herokuにpushする

Heroku上にリモートリポジトリができたので、このリポジトリに対してローカルリポジトリの内容をpushします。

まずはpushする前に、これまでファイルに加えた変更内容をコミットします。

~/mysite

$ git add -A
$ git commit -m "Herokuデプロイのための設定"

次に、herokuのmasterブランチにpushします。

~/mysite

$ git push heroku master

画像のようにメッセージがでればデプロイ完了です。

ここで、git branch -aコマンドを打つと、herokuのブランチ追加されているのが確認できるでしょう。

~/mysite

$ git branch -a
master
remotes/heroku/master

プロセスを起動する

ここまでで、一旦デプロイは完了しましたが、現状だとgunicornサーバーのプロセスが起動されていないため、URLにアクセスしてもエラーとなります。プロセスを以下コマンドで起動しましょう。

~/mysite

$ heroku ps:scale web=1

このコマンドによって、Procfileで定義したコマンドが実行され、プロセスが起動します。

再びURLにアクセスすると、これまでとは違った画面になると思います。ほとんどの場合、データベースのテーブルが作成されていないという旨のエラー画面が表示されるかと思います。これは、本番環境で使用しているデータベースにマイグレーションファイルの情報が反映されていないことが原因ですので、ローカル環境と同じようにマイグレート処理を行えば解消されます。

Heroku側で何かしらのコマンドを実行したい時は、heroku run をコマンドの先頭につけて実行します。以下コマンドで、本番環境でのマイグレート処理が実行されます。

~/mysite

$ heroku run python manage.py migrate

スーパーユーザーの作成や、shellの起動も同様です。

~/mysite

$ heroku run python manage.py createsuperuser
$ heroku run python manage.py shell

マイグレーションエラーが解消されて、無事サイトが表示されたらデプロイは完了です!

セキュリティ上の設定

デプロイが完了しましたが、実はここまでのやり方では問題が2つあります。

1. 本番環境でDebug=Trueとなっている点

1つ目は、settings.pyのDebugの値がTrueのままデプロイされているという点です。

Debugの値がTrueになっていると、エラーが起きた時に詳細な情報を画面に表示してくれます。これは開発環境下においては便利なのですが、その情報の中には一般ユーザーに知られない方が良い情報が含まれているため、本番環境ではDebug=Falseにして、詳細なエラーページを表示させないようにします。

つまり、本番環境のコードではDebug=False、ローカルの開発環境上ではDebug=Trueとなるように切り分け設定をする必要があります。

環境によってDebugの値を切り替える方法はいくつかありますが、1例を紹介します。

まずは、.gitignoreファイルに、以下のようにlocal_settings.pyと追記してファイルを保存してください。

~/mysite/.gitignore

### 省略 ###

# 追加
local_settings.py 

次に、settings.pyファイルと同じ階層に、local_settings.pyファイルを作成し、中身を以下のように編集します。

~/mysite/mysite/local_settings.py

import os

# settings.pyからそのままコピー
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

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

DEBUG = True

このファイルは、.gitignoreファイルで指定されているため、git管理下からは外れます。つまり、git push heroku masterコマンドを打っても、Herokuにはpushされないため本番環境には何ら影響を与えません。ローカルの開発環境のみで動作するファイルとなります。

次に、settings.pyのDebugの値をFalseに変更し、最後の方でlocal_settings.pyファイルを読み込みます。

~/mysite/mysite/settings.py

# Falseに変更
DEBUG = False

### 省略 ###

# 追加
try:
    from .local_settings import *
except ImportError:
    pass

# Debug=Falseの時だけ実行する設定
if not DEBUG:
    import django_heroku
    django_heroku.settings(locals())

追加した記述によって、settings.pyはlocal_settings.pyファイルをインポートしようとします。

ローカル環境では、local_settings.pyは問題なく読み込まれるので、このタイミングでDebug=Trueに切り替わります。一方、本番環境ではlocal_settings.pyは存在しないので、うまく読み込まれずImportErrorが起こります。その結果、Debugの値はFalseのままとなります。

また、django_herokuは本番環境だけで実行されれば良いため、Debug=Falseの時だけ実行されるように修正を加えています。

これで、設定は完了です。heroku masterに変更を反映しましょう。念の為、pushする前にgit statusコマンドを打ち、local_settings.pyがきちんとgit管理下から外れていることを確認しましょう。

~/mysite

# local_settings.pyがgit管理下にないことを確認
$ git status
# local_settings.pyが表示されなければOK

$ git add -A
$ git commit -m "Debugモードの切り替え設定"
$ git push heroku master

無事デプロイができたら本番環境で、存在しない架空のURLを打ってみてこのような404ページになるか確認しましょう。詳細なエラー情報は出ていませんね。

一方、ローカル環境で同じことを試すと、詳細なエラーページが出るでしょう。これで、Debugモードが正しく切り替えられてることが確認できました。

2. SECRET_KEYがハードコーディングされている点

2つ目の問題は、SECRET_KEYがファイル上にハードコーディングされている点です。セキュリティ上の観点から、機密性の高い情報は、Djangoのコード上に直接書くのは避けるべきです。機密性の高い情報とは、settings.pyにデフォルトで書いてあるSECRET_KEYや、何かしらのAPIを使うときに必要なAPIキーなど、他人に知られてはまずいもののことです。

ローカル環境では、ハードコーディングされていても問題ないので、先ほど作ったlocal_settings.pyファイルにSECRET_KEYをコピーしましょう。

~/mysite/mysite/local_settings.py

import os

# settings.pyからそのままコピー
SECRET_KEY = 'wn$m$y04ubtwg&l+atr_4ny8t18x2&=y*)z$3jbg*9n5c7$r$a'

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

DEBUG = True

次に、settings.pyでは、デフォルトで記述されているSECRET_KEYに関する記述を削除して、新たにSECRET_KEY = os.environ['SECRET_KEY']という記述を追加します。

~/mysite/mysite/local_settings.py

# 削除
SECRET_KEY = 'wn$m$y04ubtwg&l+atr_4ny8t18x2&=y*)z$3jbg*9n5c7$r$a'

###
省略
###

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

これによって本番環境においては、ハードコーディングされた値ではなく、「SECRET_KEY」という環境変数を参照するようになります。

あとは、本番環境でSECRET_KEYという名前の環境変数を定義してあげればOKです。

本番環境の環境変数を設定するには、heroku config:setコマンドを使います。

~/mysite

$ heroku config:set SECRET_KEY="wn$m$y04ubtwg&l+atr_4ny8t18x2&=y*)z$3jbg*9n5c7$r$a"

heroku configコマンドで、設定されている環境変数の一覧が取得できるので、コマンドを打ってSECRET_KEYが表示されれば無事設定できています。

~/mysite

$ heroku config
SECRET_KEY:   wn&l+atr_4ny8t18x2&=y*)zjbg*9n5c7 # 表示されれば正しく設定されている

あとは、変更をコミットして、heroku masterにpushすれば完了です。

また、環境変数はHerokuのサイトからも確認、編集ができます。自分のアプリを選択して、「Settings」→「Reveal Config Vars」ボタンを押すと編集画面が表示されます。

以上、DjangoアプリをHerokuでデプロイする方法でした。気づきや質問があればメッセージを頂けると嬉しいです。