🌥️
asset_syncを使ってCloud Front + S3でassetファイルを配信する
既に知見が転がっている内容だけど、個人的な備忘メモも兼ねて書いておきます。
やりたかったこと
- ECSで動いているRailsのimageサイズを小さくしたかった
- Railsアプリケーションに(画像等)assetファイルが大量にあった
- そのため、assetファイルをCloud Front + S3で配信することでimageサイズが大きく改善しそうだった
やったこと
Before
After
環境・各バージョン
- Ruby: 3.0.4
- Rails: 6.1.6
- webpacker: 5.4.3
- asset_sync: 2.16.0
内容
大まかな流れ
ざっくり、以下のような流れで進めました。Railsアプリケーションに後からasset_sync
を適応する流れとなりましたが、特に事故もなく思ったよりスッと進められてよかったです。
- Rails:
asset_sync
の設定を行い、S3バケットにassetファイルをアップロードできるようにする - Terraform: (S3にファイルがアップロードされたので)CloudFrontの設定を変更し、特定パスへのリクエスト(=assetファイルの配信)をS3にリダイレクトさせる
- Docker: (S3にあるassetファイルを見るようになったので)assetファイル群をimageに含めないようにする
実装内容
asset_sync
の設定
1. Rails: i. Railsの各環境設定
-
gemのREADME通り、
asset_sync
の設定を進めます- 具体的には、
asset
パスの指定と、配信を行うhostの指定を行います - staging環境など他の環境についても、該当ファイルに同様の設定を行います
- 開発環境(development)でのasset利用に変更がない場合は、
config.action_controller.asset_host
はlocalhostを指定すると良いです
- 具体的には、
config/environments/production.rb
Rails.application.configure do
config.action_controller.asset_host = 'https://example.com'
config.assets.prefix = '/assets'
end
asset_sync
のカスタマイズ設定
ii. - 必要に応じて、
asset_sync
の設定をカスタマイズします- 今回はfogやwebPackerを使用しているRailsアプリケーションで、以下のような設定を行いました
- 各種設定値の意味はgemのREADMEを参照ください(名前から意味がわかるものが多いです)
config/initializers/asset_sync.rb
# frozen_string_literal: true
if defined?(AssetSync)
AssetSync.configure do |config|
config.fog_provider = 'AWS'
config.fog_directory = if Rails.env.production?
'prd.hoge.assets'
elsif Rails.env.staging?
'stg.hoge.assets'
end
config.fog_region = 'ap-northeast-1'
config.existing_remote_files = 'keep'
config.gzip_compression = true
config.manifest = true
config.aws_acl = 'private'
# webPacker を assets:precompile 時に S3 に同期する設定
config.add_local_file_paths do
Dir.glob('public/packs/**/*').map { |str| str.sub('public/', '') }
end
end
end
2. Terraform: CloudFrontの設定を変更
i. S3のoriginを追加
-
aws_cloudfront_distribution
のoriginに、s3_origin_configが設定されたS3向けのオリジンを追加します-
domain_name
とorigin_id
には、(当然ですが)assetファイルを配信するS3バケットの値を指定してください
-
resource "aws_cloudfront_distribution" "app" {
# 略
origin {
domain_name = aws_s3_bucket.hoge.bucket_regional_domain_name
origin_id = "s3-${aws_s3_bucket.hoge.bucket}"
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.hoge.cloudfront_access_identity_path
}
}
}
ordered_cache_behavior
の設定を変更
ii. - 同じく
aws_cloudfront_distribution
のキャッシュ配信の設定を変更します- ordered_cache_behaviorでリダイレクトさせるパスパターンを指定し、リダイレクト先をS3に変更します
- 今回は
/assets/*
と/packs/*
以下のパスへのアクセスをS3へリダイレクトさせています(元々は図の通りALBに通していました)
resource "aws_cloudfront_distribution" "app" {
default_cache_behavior {
# 略
}
ordered_cache_behavior {
path_pattern = "/packs/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-${aws_s3_bucket.hoge.bucket}" # 変更
# 略
}
ordered_cache_behavior {
path_pattern = "/assets/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-${aws_s3_bucket.hoge.bucket}" # 変更
# 略
}
}
3. Docker: assetファイル群をコンテナに含めないようにする
-
Dockerfile
でCOPYしているassetファイルがあれば、それをCOPYしないようにする - 注意点として、Railsの動作に必要となるmanifest関連のファイルはそのまま残しておく必要がある
Dockerfile
# 修正前
COPY /public/packs ./public/packs
COPY /public/assets ./public/assets
# 修正後
COPY /public/packs/manifest.json public/packs/
COPY /public/assets/.sprockets-manifest-* public/assets/
結果
- AWSのコンソール等から、該当するECSのimageサイズが小さくなっていることが確認できます
参考記事
- 実装するにあたりお世話になった記事を貼っておきます
Discussion