Heroku上のDjangoアプリで静的ファイルをS3から配信する(前編)
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を使います。