🔨
S3にある大量のファイルのメタデータを書き換える
背景
レシピけんさく では画像をS3に置いてCloudflare (CDN) でキャッシュしているのだが, Cache-Control
の max-age
を1ヶ月に設定してしまった. CDN側でもっとキャッシュして転送量 (料) を節約するべく, max-age
を1年に設定したい気持ち.
S3では既存のメタデータを書き換えることが出来ないので, copy-object で上書きコピーして, その際に新たなメタデータを設定する. ごく少数ならコンソールや aws
コマンドを使っても良いが, 大量にあると切りが無いので, AWSのAPIを直接叩くことにした. (aws
コマンド呼び出しだと, パフォーマンスが出なかった) 以下は, *.webp, *.jpg, *.png の Cache-Control
を書き換える例.
64並列で実行して, 約215万個のファイルを5500秒 (秒間約400件) で処理することができた. ここまでやっても一切エラーを起こさなかったAWSはさすが. ちなみに, COPY リクエストは1000リクエストで0.0047USDが課金されるので, 10USDちょいかかっている.
にゃーん
Rubyスクリプト
aws-sdk-s3
, parallel
, dotenv
が必要なので, gem で入れるか Bundler 使うかする.
#! /usr/bin/env ruby
require "dotenv"
require "aws-sdk-s3"
require "parallel"
Dotenv.load
pp ENV["ASSETS_HOST"]
list_file = ARGV.shift
fh = File.open(list_file)
puts Time.now.iso8601(6)
Parallel.each_with_index(fh.each_line, in_threads: 64) do |line, index|
next unless line =~ /\.(?:webp|jpeg|png)/
path = line.chomp.sub(/.* /, "")
ext = path.sub(/.*\./, "")
s3 = Aws::S3::Client.new
begin
s3.copy_object(
bucket: ENV["ASSETS_HOST"],
copy_source: "/#{ENV['ASSETS_HOST']}/#{path}",
key: path,
content_type: "image/#{ext}",
cache_control: "public, max-age=31536000",
metadata_directive: "REPLACE"
)
rescue Aws::Errors::ServiceError => error
pp [path, index, error]
end
pp [path, index, Time.now.iso8601(6)] if index%10000 == 0
end
puts Time.now.iso8601(6)
使い方
- 環境変数
AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
,AWS_REGION
,ASSETS_HOST
を設定するか, .env に書き込む -
aws s3 ls
の結果をファイルに落とす. (APIで取得して順次処理しても良いのだが, 途中でコケたときに困るので.)aws s3 ls --recuesive s3://$ASSET_HOST > s3.list
- スクリプトにファイル一覧を食わせる.
ruby update-max-age.rb s3.list
Discussion