orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

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

pylibmcdjango-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)で静的ファイル配信 f:id:mi_kattun:20140604002053p:plain Heroku+S3(Tokyo)で静的ファイル配信 f:id:mi_kattun:20140604002038p:plain

(参考)さくらVPS(大阪) f:id:mi_kattun:20140604002105p:plain

今回は日本のみをターゲットとしたのでS3のみを使いましたが、全世界をターゲットとする場合はCloudFrontなどのCDNを使うのが効果的でしょう。