Heroku上のDjangoアプリで静的ファイルをS3から配信する(後編)
前編 までで静的ファイルをS3から配信することができました。しかし、実際にHerokuにpushしてみると、毎回全ファイルがアップロードされてしまい、非常に時間がかかります。
原因はこうです。collectstatic
はファイルのタイムスタンプを比較してファイルが変更されたかどうかを判定しています。しかし、Herokuではpushの度に全ファイルのタイムスタンプが更新されるので、全ファイルに変更があったと判断されてしまうのです。
参考: Django collectstatic from Heroku pushes to S3 everytime - Stack Overflow
この対策として、前編 のようにcollectstatic
をローカルマシンで実行する方法もあります。しかしせっかくならHerokuへのpush時にやってくれたほうがエレガントです。
これを解決するのが collectfast です。collectfast
はcollectstatic時にファイルの変更チェックをタイムスタンプではなく、ハッシュ値で行うライブラリです。
collectfast
はS3上のファイルのハッシュ値をDjangoのキャッシュに保存することで、高速に変更チェックを行います。キャッシュなしでも利用できますが、毎回全ファイルのハッシュ値をS3から取得するので導入効果が薄れます。手元では1分ほどかかりました。
ライブラリのインストール
$ pip install collectfast pylibmc django-pylibmc-sasl
pylibmc
と django-pylibmc-sasl
は後述のmemcachierを使うのに必要です。
requirements.txt
にも忘れずに書いておきます。(バージョンはご利用のものに合わせてください)
Collectfast==0.2.0 django-pylibmc-sasl==0.2.4 pylibmc==1.3.0 python-dateutil==2.2 pytz==2014.3 six==1.6.1
Herokuの設定
キャッシュで使うmemcachier
アドオンを追加します。
$ heroku addons:add memcachier
また、AWSのクレデンシャルはHerokuの設定に追加しておきます。
$ heroku config:set AWS_ACCESS_KEY_ID=*** AWS_SECRET_ACCESS_KEY=***
Djangoの設定
settings.py
に以下の設定を追加します。
INSTALLED_APPS = ( ... 'collectfast', # 行を追加 ... ) os.environ['MEMCACHE_SERVERS'] = os.environ.get('MEMCACHIER_SERVERS', '').replace(',', ';') os.environ['MEMCACHE_USERNAME'] = os.environ.get('MEMCACHIER_USERNAME', '') os.environ['MEMCACHE_PASSWORD'] = os.environ.get('MEMCACHIER_PASSWORD', '') CACHES = { 'default': { 'BACKEND': 'django_pylibmc.memcached.PyLibMCCache', 'TIMEOUT': 1000, 'BINARY': True, 'OPTIONS': { 'tcp_nodelay': True, 'remove_failed': 4 } } } COLLECTFAST_CACHE = 'default'
ここでは説明を簡単にするため、default
のキャッシュを使ってますが、他の用途で使っている場合は、collectfast
などのキャッシュを作るとよいでしょう。
参考:
デプロイ
あとは普通にHerokuにデプロイするだけです。
$ git push heroku master
1回目はある程度時間がかかりますが、2回目以降は変更のあったファイルだけがアップロードされるのですぐに終わります。
結果
Heroku単体で配信していた時は200ms程度かかっていた静的ファイルの配信が、S3を使うことで30〜100ms程度で終わっていることがわかります。
Heroku(US)で静的ファイル配信 Heroku+S3(Tokyo)で静的ファイル配信
(参考)さくらVPS(大阪)
今回は日本のみをターゲットとしたのでS3のみを使いましたが、全世界をターゲットとする場合はCloudFrontなどのCDNを使うのが効果的でしょう。