HerokuにDjangoアプリケーションを置いて、静的ファイルもHerokuから配信した場合、USまでのレイテンシがあるので1ファイルごとに結構な時間がかかります。
試しに同じアプリケーションをさくらVPS(大阪)と、Heroku(US)に置いた場合、さくらでは 30ms〜150ms程度でロードできるのに対し、Herokuでは200ms以上かかりました。
HTML1ファイルだけならともかく、CSSやJS、画像のロードにも時間がかかるため、体感時間は結構変わります。
そもそも静的ファイルをアプリケーション・サーバーから配信するのはCPU、ディスク、ネットワークなどのリソースの面でも無駄が多く、推奨されていません。
そこで今回は、静的ファイルをAmazon S3のTokyoリージョンから配信してみました。いわゆる Web Storageパターン です。
長くなったので前後編に分けています。前編はHerokuに限らない一般的なDjangoアプリケーションの話をし、後編 ではHeroku特有の話をします。
ライブラリのインストール
$ pip install django-storages boto
django-storages はDjango用のカスタム・ストレージ・バックエンドを集めたライブラリです。
boto はAWSのPythonインターフェイスを提供するライブラリです。django-storagesのs3-botoバックエンドがbotoを使用する形になります。
requirements.txtにも忘れずに書いておきます。(バージョンはご利用のものに合わせてください)
boto==2.29.1 django-storages==1.1.8
S3バケットの作成
S3に適当な名前でバケットを作成して、Static Website Hostingを有効にしておきます。
今回は static.bus.capybala.com という名前にしました。
日本に置きたかったのでTokyoリージョンを選択しています。
Access Keyの作成
また、このバケットにファイルを追加できる権限を持つAccess KeyとSecret Access Keyを作っておきます。
以下の様なポリシーで良いでしょう。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::static.bus.capybala.com",
"arn:aws:s3:::static.bus.capybala.com/*"
]
}
]
}
Djangoの設定
settings.pyに以下の設定を追加します。
INSTALLED_APPS = ( ... 'storages', # 行を追加 ... ) # S3バケットのURL(サブドメインのURLを使ってもどちらでもいいです。) STATIC_URL = 'https://s3-ap-northeast-1.amazonaws.com/static.bus.capybala.com/' # collectstaic時にS3を使う STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage' # 作成したS3バケットの名前 AWS_STORAGE_BUCKET_NAME = 'static.bus.capybala.com' # これをTrueにしたほうがファイル変更のチェックが速くなる AWS_PRELOAD_METADATA = True
以下のようにDEBUGが有効かどうかによってSTATIC_URLをローカル配信と切り替えても良いでしょう。
if DEBUG: STATIC_URL = '/static/' else: STATIC_URL = 'https://s3-ap-northeast-1.amazonaws.com/static.bus.capybala.com/'
AWSのクレデンシャル
AWSのAccess KeyとSecret Access Keyは秘密の値なので、settings.pyには書きません。Twelve Factor に従って、環境変数AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYで渡します。
.envファイルに書いておきます。
AWS_ACCESS_KEY_ID=*** AWS_SECRET_ACCESS_KEY=***
アップロードのテスト
ここまでの設定をした上で、以下のようにcollectstaticを実行すると、静的ファイルがS3バケットにアップロードされるはずです。
$ foreman run python manage.py collectstatic --noinput
細かい話(アップロード時のエラー)
S3への大きなファイルのアップロード時にBroken pipeというエラーが発生する問題に遭遇したので、コメントにあるように~/.botoというファイルを作って対処しました。
$ cat ~/.boto [s3] host = s3-ap-northeast-1.amazonaws.com
なお、後編のHerokuでは特に必要ありませんでした。
前編はここまでです。後編ではHerokuを使います。