assets on S3の導入
こんにちは。エンジニアの志村です。 最近暑くて参りますね…。アイスばっかり食べてます。
さて今回ですが、Assets on S3を導入しましたのでその際のメモです。
結構この形でassetsを配信しているサービスは多いですよね。
今回は、Cloudfront+S3 / asset_sync+capistranoという定番のパターンで実装しております。
assets on S3とは
デプロイ時にassetsファイルをS3に配置し、CDN経由で配信する方法です。
通常であればnginxやApache等のWebサーバーを介して静的ファイルは配信されています。
assets on S3はassetsファイルをS3に配置し、CloudfrontやAkamai等のCDN経由で配信します。
Webサーバー負担軽減やCDNを噛ませることによる高速化を目的として用いられることが多いかと思います。
仕組み
下記のような流れになります。
- デプロイ時に
rake assets:precompile
を走らせる。その際にasset_syncを使用し、S3にassetsファイルをアップロードする。 - manifestファイルをEC2にアップロードする。
rake assets:precompile
時に作成されるassetファイルを削除する(public/assetsに格納されている)
こんな感じでしょうか。 他にもCloudfrontやS3の設定があるので、下記に記していきます。
Rails側の設定
ではまずasset_syncを導入し、bundle install
をします。
asset_syncの導入
gem 'asset_sync'
次にasset_syncのconfigファイルを生成します。
$ rails g asset_sync:install --provider=AWS
今回はAWSを使用するのでproviderオプションにはAWSを設定します。
そうするとasset_sync.rb
という設定ファイルが生成されます。
- config/initializers/asset_sync.rb
AssetSync.configure do |config| # fogを使用するサービス config.fog_provider = 'AWS' # S3の存在するリージョン config.fog_region = 'ap-northeast-1' # S3のバケット名 config.fog_directory = 'assets' # IAMのアクセスキー config.aws_access_key_id = 'xxxx' # IAMのシークレットアクセスキー config.aws_secret_access_key = 'xxxx' # 既にS3上に存在しているファイルの扱い keep, delete, ignoreから選択 config.existing_remote_files = 'keep' # gzip圧縮をするかどうか config.gzip_compression = true end if defined?(AssetSync)
上記で使用してるIAMに関しては、次の章のS3の部分で説明します。 これでasset_sync自体の設定は完了しました!
assetsのURLをCloudfrontにする
assetsファイルはCloudfrontより配信されます。
通常の設定では、http://domain/assets/
配下のファイルが配信されますが、これをhttps://Cloudfrontのエンドポイント/assets
に変更し、Cloudfrontから配信するように設定します。
- config/environments/production.rb
〜中略〜 # Enable serving of images, stylesheets, and JavaScripts from an asset server. config.action_controller.asset_host = '//xxxxx.cloudfront.net' 〜中略〜
これでassetsファイルがCloudfrontより配信されるようになりました!
次はcapistranoの設定を行います
capistranoの設定
流れとして、
cap 〜 deploy
コマンドを叩いた時に、ローカルにてrake assets:precompile
を走らせる(asset_syncが自動的にS3にアップロードしてくれる)- manifestファイルをサーバの
public/assets
ディレクトリに転送する - ローカルで生成されたpublic/assetsディレクトリを消去する
この中で特に大切なのは2.かと思います。
railsは、productionにデプロイされる際にprecompileを行います。
その際にdigestを付与してファイル名-digest.scss
のようなファイル名に変更します。
このdigestはprecompile時にランダムに設定されるのでrails側で管理が必要になります。
そのファイル名を管理するのがmanifestファイルです。これが存在しないと、railsはassetのパスを正しく指定出来ません。
ではcapistranoの設定に移っていきます。 こちらのコードを参考にさせて頂きました。
- config/deploy.rb
〜中略〜 namespace :deploy do # rake db:assets:precompileを実行する task :asset_sync do run_locally do Bundler.with_clean_env do execute :rake, 'assets:precompile' end end end # manifestファイルをアップロードする task :upload_manifest do on roles(:app) do |host| if test "[ ! -d #{release_path}/public/assets ]" execute "mkdir -p #{release_path}/public/assets" end file_path = Dir::glob('public/assets/.sprockets-manifest*').first upload!(file_path, "#{release_path}/public/assets") end end # ローカルに残されたprecompile後のassetsファイルを削除する task :assets_cleanup do run_locally do Bundler.with_clean_env do execute :rake, 'assets:clobber' end end end task :restart do invoke 'unicorn:restart' end end before 'deploy:starting', 'deploy:asset_sync' after 'deploy:publishing', 'deploy:upload_manifest' after 'deploy:publishing', 'deploy:assets_cleanup' after 'deploy:publishing', 'deploy:restart' 〜中略〜
上記のようになります。
rakeタスクに関しては新しくファイルに切り分けても良いかと思いますが、そんなにデプロイスクリプトが多いわけでも無かったので僕はそのままdeploy.rbに書いてしまっています。
release_path
の部分はそれぞれの環境に合わせたpathを指定すればOKです。
developmentではsprocketsを使用しているのでそれをそのままuploadしております。
AWS側の設定
S3の設定
S3はバケットを作成し、CORSの設定を行います。 CORSの設定をしないと、Font Awesomeが正しく表示されません。 詳しくは
を御覧ください。
AWSコンソールの「S3」を選択
「バケットの作成」を選択
バケット名、リージョンを設定
作成したバケットを選択し、「プロパティ」→「アクセス許可」→「CORS設定の編集」を選択
ここに下記のコードを貼り付け、「保存」をします 下記のコードを使用させて頂きました。
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds> <AllowedHeader>Authorization</AllowedHeader> </CORSRule> </CORSConfiguration>
Cloudfrontの設定
基本設定
assets用のディストリビューションを作成します。
AWSコンソールの「Cloudfront」を選択
「Web」を選択
Origin Settingsを入力する
項目 | 設定 | 説明 |
---|---|---|
Origin Domain Name | 先ほど作成したS3バケット名 | オブジェクトの取得先 |
Origin Path | 空のまま | S3のルートディレクトリの設定 |
Origin-ID | 分かりやすい名前 | オリジンを区別する一意のID |
Restrict Bucket Access | Yes | CloudfrontのURLからのみS3のオブジェクトにアクセス出来る |
Comment | 分かりやすい名前 | identityを区別するIDのようなもの |
Grant Read Permissions on Bucket | Yes | バケット内のオブジェクトの読み取り許可 |
Origin Custom Headers | 空のまま | リクエストをオリジンに転送する際にカスタムヘッダーを含めるかどうか |
※ Default Cache Behavior Settings, Distribution Settingsに関しては環境に応じて入力して下さい。今回は特にいじらずに行きます。
「Create Distributions」を選択するとCloudfrontのDistributionが作成されます。
Behaviorの設定
さて、Behaviorの設定を行っていきましょう。
Behaviorは振り分けのルールです。
例えば、
https://xxxx.cloudfront.net/assets
にリクエストが来た場合にはassetsバケットに転送https://xxxx.cloudfront.net/images
にリクエストが来た場合にはimagesバケットに転送
のようなことが可能になります。
「Behavior」タブの「Create Behavior」を選択
Settingsを入力
項目 | 設定 | 説明 |
---|---|---|
Path Pattern | /assets/* | 振り分けパターン。今回は/assets配下のオブジェクト全てをS3から配信したいので/assets/*とする |
Origin | 先ほど設定したOrigin-ID | どのオリジンからオブジェクトを取得するかを選択 |
Viewer Protocol Policy | 環境によって選択 | アクセスする際のプロトコルの選択 |
Allowed HTTP Methods | GET, HEAD | オリジンに転送するHTTPメソッド。今回は取得のみなのでGET, HEADを選択 |
Forward Headers | Whitelist -> Origin | どのヘッダを転送するか 今回CORSの設定を行っているので、OriginをAddする |
Grant Read Permissions on Bucket | Yes | バケット内のオブジェクトの読み取り許可 |
Object Caching | Use Origin Cache Headers | MaximumTTL等を変更しないのであればUse Origin Cache Headersのままで良い |
「Create」を選択すると、このBehaviorが有効になります。
設定の反映までは10分〜20分ぐらいかかるので気長に待ちましょう!
以上です。 asset_syncのおかげでそこまで多くの手順を踏むこと無く、assets on S3の導入が出来ました。 次回はLambdaを使用してInvalidationを走らせる処理について書きたいと思います。