kivantium活動日記

プログラムを使っていろいろやります

DjangoアプリをHerokuにデプロイする

インターンを始めたら労働のつらさを思い出しました。

というわけで、Webサービスの作り方を勉強していきたいと思います。

WebサービスといえばRuby on Railsというイメージがあったのですが、最近は人気が落ちているという話もよく聞きます。Rubyの経験が全くない自分が今から勉強する必要はないのではないかと思って調べてみたら、Rails・Django・Laravelを3大Webフレームワークと呼んでいる記事を発見しました。このうちのDjangoPythonで書かれているため今までの経験を活かすことができ、機械学習ライブラリなどと組み合わせるのもラクそうだったのでこれを使ってみようと思いました。 f:id:kivantium:20200412000515p:plain:w600

(グラフはStack Overflow Trendsより)

Ruby on Railsチュートリアルの第1章ではHello, world!をHerokuにデプロイしていたため、Djangoでも同じことをやってみようとしたら意外と情報が錯綜していて時間がかかったので自分がやった方法を記録しておきます。DjangoもHerokuも今週始めたばかりです。あまり信用しないでください。

実行環境

環境構築

Deploying Python and Django Apps on Heroku によれば、ルートディレクトリにrequirements.txt, setup.py, Pipfile のいずれかがある場合にPythonによるアプリだと認識されるとのことです。一番モダンそうなpipenによるパッケージ管理を使うことにしました。

適当なディレクトリで以下のコマンドを実行します。playgroundは好きな名前にしてください。

$ django-admin startproject playground
$ cd playground
$ pipenv --python 3.6
$ pipenv shell
$ pipenv install django-heroku gunicorn

ホームディレクトリで上のコマンドを実行したとすると、この時点での~/playground/ディレクトリの構成は以下のようになっているはずです。

~/playground
├── Pipfile
├── Pipfile.lock
├── manage.py
└── playground
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

以下、manage.pyがあるディレクトリを「ルートディレクトリ」と呼び、ファイルパスは全てそこからの相対パスで表します。playgroundではない名前をつけた場合は適当に読み替えてください。

ファイルを書き換える

まず、settings.pyと同じフォルダに以下の内容のlocal_settings.pyを作成します。こうすることで開発環境と本番環境でデバッグの有無などを切り替えることができるようになります。SECRET_KEYの行は環境ごとに違うので、playground/settings.pyからコピペしてください。(参考: pipenvでDjangoアプリをherokuにでデプロイしたので手順をメモ

DEBUG = True

SECRET_KEY = '_or(2t#$a9fm9i0_-620e(kv__qz6%%wzwx$j0*+ayo1xd&2d+'

次に、playground/settings.py を書き換えます。編集前後のdiffを示します。赤が削除した行で、青が追加した行です。

 (略)

 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # Quick-start development settings - unsuitable for production
 # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
 
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '_or(2t#$a9fm9i0_-620e(kv__qz6%%wzwx$j0*+ayo1xd&2d+'
 
 # SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
+DEBUG = False
 
 ALLOWED_HOSTS = []
 
 (略)
 
 INSTALLED_APPS = []
 
 MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
+     'whitenoise.middleware.WhiteNoiseMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',

 (略)
 
USE_TZ = True
 # https://docs.djangoproject.com/en/3.0/howto/static-files/
 
 STATIC_URL = '/static/'
+# 2020/04/26 追記
+# http://whitenoise.evans.io/en/stable/django.html#make-sure-staticfiles-is-configured-correctly
+STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
+
+# Simplified static file serving.
+# https://warehouse.python.org/project/whitenoise/
+
+STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
+
+# Use different settings in local environment and Heroku
+# https://qiita.com/miler0528/items/1926e93ed97979f8e9fa
+try:
+    from .local_settings import *
+except ImportError:
+    pass
+
+if not DEBUG:
+    import django_heroku
+    django_heroku.settings(locals())
  • SECRET_KEYは公開してはいけない情報なのでコードからは削除します。実行するたびに値が違うはずなので適宜読み替えてください。
  • DEBUGは本番環境ではFalseにします。
  • MIDDLEWARESTATICFILES_STORAGE関係の設定はWhiteNoiseを使って静的ファイルを効率的に扱えるようにするためのものです。(参考: Django and Static Assets
  • MIDDLEWAREへの追記はSecurityMiddlewareの直後にする必要があるので注意してください。(参考: enable-whitenoise
  • コードの末尾に追加したのは開発環境と本番環境の設定を切り替えるための処理です。

最後にデプロイに必要なProcfileをルートディレクトリに用意します。playground.wsgiの部分はプロジェクト名に合わせて適宜変更してください。(参考: Configuring Django Apps for Heroku

web: gunicorn playground.wsgi

Pythonのバージョンを指定する場合はルートディレクトリにruntime.txtを用意します。用意しない場合はデフォルトのバージョンが使われます。執筆時点でのデフォルトは3.6.10です(参考: specifying-a-python-version

python-3.6.10

この時点でのディレクトリ構成は以下のようになっているはずです。

playground
├── Pipfile
├── Pipfile.lock
├── Procfile
├── manage.py
├── playground
│   ├── __init__.py
│   ├── asgi.py
│   ├── local_settings.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── runtime.txt

一度ローカル環境で動作を確認してみます。ルートディレクトリで以下を実行します。

python manage.py runserver

ブラウザでhttp://127.0.0.1:8000/にアクセスすると、いい感じのロケットの画像が表示されるはずです。 f:id:kivantium:20200411235809p:plain:w600

Hello, world!

Hello, worldを実行するためのアプリを作成します。

python manage.py startapp hello

hello/views.pyを以下のように編集します。

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world!")

以下の内容のhello/urls.pyを作成します。

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

さらに、playground/urls.pyを以下のように編集します。

from django.urls import include, path

urlpatterns = [
    path('', include('hello.urls')),
]

python manage.py runserverを実行してブラウザでhttp://127.0.0.1:8000/にアクセスすると、Hello, world!が表示されるはずです。

デプロイの準備

Herokuのインストール

The Heroku CLIに従ってインストールします。Ubuntuではsnapでインストールできます。

sudo snap install --classic heroku

初めての場合はユーザー登録をする必要があります。公開鍵を登録しておくと便利です。

heroku login --interactive
heroku keys:add

公開鍵を登録したら、以下を実行します。appnameには好きな名前を設定してください。

heroku login
heroku create <appname>

settings.pyから削除したSECRET_KEY環境変数として設定しておきます。 以下のコマンドを実行してください。(SECRET_KEYの中身は環境に応じて変えてください)

heroku config:set SECRET_KEY='_or(2t#$a9fm9i0_-620e(kv__qz6%%wzwx$j0*+ayo1xd&2d+'

デプロイ

HerokuにはGit経由でデプロイします。ルートディレクトリに以下のような.gitignoreを用意します。

__pycache__
*.pyc
db.sqlite3
staticfiles
local_settings.py

ルートディレクトリで以下を実行します。

git init
git add .
git commit -m 'first commit'
git push heroku master
heroku ps:scale web=1

https://<appname>.herokuapp.com/にアクセスするとHello, world!が表示されたページが確認できると思います。

(pushできなかった場合: Herokuにpush時にdoes not appear to be a git repository出た時の対処 - Qiita

ここまでの作業をまとめたリポジトリはこのコミットなので参考にしてください。 github.com

次回Twitterによるログイン機能を実装します。

参考文献

広告コーナー