事前にprecompileしたassetsをS3に配置してdeployを高速化する方法

Railsで作られているサービスとおもわれるbasecampやgithubはassetsファイルをアプリケーションサーバとは別のCDN(akamaiやCloudFront)サービスから配信している。

CDNに配置する事のメリットとして世界中のEdgeサーバからassetsを配信できるのでどこからアクセスしてもページロードが速い事が一番大きいが、
副次的な効果として事前にassets:precompileすることが可能なためcapistranoでのdeploy時にprecompileしない戦略を取ることによって大幅にdeploy時間を短縮できるメリットがある。
(assetsをgit等ににコミットしてしまってprecompileせずにdeployする戦略もあるがコミットログが汚くなるため避けたい)

今回はasset_syncというgemを使ってS3にassetsを配置しつつcapistranoでdeployする方法を記述します。
このgemはassets:precompileした時に自動的にassetsファイルを指定のストレージに配置してくれる優れものです。

AWSに登録

詳細は省くがaccess_key_idとsecret_access_keyを取得しておく

assetsを配置するためのS3のbucketを作成します

今回は下記のbucketを作成します.

Bucket Name: hakutoitoi-assets

GemFileにassset_syncを追加

gem 'asset_sync'
$ bundle install

config/environments/production.rbを編集

# デフォルト以外のパスにassetsを配置したい場合は指定します(デフォルトは/assets)
# config.assets.prefix = "/path/to/assets"

# 事前に作成しておいたS3のbucketのURLを指定します
config.action_controller.asset_host = "//hakutoitoi-assets.s3.amazonaws.com"

assets_syncの設定ファイルを作成する

if defined?(AssetSync)
  AssetSync.configure do |config|
    config.fog_provider = 'AWS'
    config.aws_access_key_id = "XXXXXXXXXXXXX"
    config.aws_secret_access_key = "XXXXXXXXXXXXX"
    config.fog_directory = "hakutoitoi-assets"

    # アップロードするS3エンドポイントを明示的に指定
    config.fog_region = 'ap-northeast-1'
  end
end

開発マシンでprecompileする

# precompileすると自動的にS3にassetsがsyncされます
$ bundle exec rake assets:precompile RAILS_ENV=production

できあがったassetsからmanifest.ymlだけをコミットする

このファイルがdeployされたソースコードにないとrailsはassetsのパスを解決することができずassetsを利用しているページは500エラーになってしまいます

$ git commit public/assets/manifest.yml
$ git push origin master

追記:
Rails3の場合はmanifest.ymlだがRails4からはmanifest-md5hash.jsonが生成されるようになるのでこちらをコミットする必要があるので注意。詳しくはEdge RailsGuideを参照。
http://edgeguides.rubyonrails.org/asset_pipeline.html#precompiling-assets

$ git commit public/assets/manifest-51c7803d58f0eceb1bfb69a398259469.json

Capfileでdeploy/assetsの読み込みをしている場合はコメントアウトする

# こうしないとdeploy時にprecompileしてしまいます
# load 'deploy/assets'

deployにかかる時間の比較(AWS smallインスタンスの場合)

# precompileをするdeploy
$ time cap deploy --quiet
real    1m48.629s
user    0m1.269s
sys     0m0.697s

# precompileをしないdeploy
$ time cap deploy --quiet
real    0m17.132s
user    0m0.942s
sys     0m0.541s