🦊

Rails8 + Cloudflare R2を試してみた

に公開

こんにちは。今回はRails8 + Cloudflare R2を試す機会があったのでそれをまとめていこうと思います。
Cloudflare R2はS3互換のストレージでS3と同様に利用出来ます。また毎月無料枠があるので少量利用目的だととても助かります。
https://www.cloudflare.com/ja-jp/developer-platform/products/r2/

それでは早速始めましょう!
以下が今回Cloudflare R2を試した状態の環境です

環境

  • Rails 8.1.2
  • Ruby 4.0.1

画像アップロードにはActiveStorageを利用します

必要なもののインストール

画像処理系のライブラリをローカルに入れておくことが必要なのでもしImageMagickかvipsが入っていなければ以下から入れる
依存先ライブラリとかはREADME読むとわかる
https://github.com/janko/image_processing

brew install imagemagick # or vips

Gemfileに以下を追記して bundle install

Gemfile
gem 'image_processing', '~> 1.0'
gem 'aws-sdk-s3', require: false

ActiveStorageのインストールも終わっていなければ入れます。ActiveStorageのmigrationも作成されるので必要であればここらへんでコミットしとくといいかも。

bin/rails active_storage:install
bin/rails db:migrate

環境別設定など

ストレージ設定

今回は以下のようにcloudflareという環境を作成します

config/storage.yml
test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

cloudflare:
  service: S3
  access_key_id: <%= ENV["CLOUDFLARE_R2_ACCESS_KEY_ID"] %>
  secret_access_key: <%= ENV["CLOUDFLARE_R2_SECRET_ACCESS_KEY"] %>
  endpoint: <%= ENV["CLOUDFLARE_R2_ENDPOINT"] %>
  bucket: <%= ENV["CLOUDFLARE_R2_BUCKET"] %>
  region: <%= ENV.fetch("CLOUDFLARE_R2_REGION", "auto") %>
  force_path_style: true
  request_checksum_calculation: when_required
  response_checksum_validation: when_required

force_path_style: true, request_checksum_calculation: when_required, response_checksum_validation: when_required がないとうまくアップロードが出来ませんでした。
force_path_style はS3互換ストレージを使うときに必要そう。 request_checksum_calculation, response_checksum_validation についてはリクエストやレスポンス送信/受信時にデータ整合性の検証のために設定しておく必要がありそう。
以下の記事を参考にさせていただきました、ありがとうございます!
https://qiita.com/otohusan/items/e88af190e330fa3cb9ae

環境変数

今回の環境では .env を使って環境変数を設定しているのでそこに書く想定ですが設定値は各々の設定箇所で。
Rails8.2からは Rails.app.creds で 1)ENV, 2)credentialsの順で値を取得できるぽいのでそこらへんを使うのも良さげ🎉
https://github.com/rails/rails/pull/56404

以下に設定する値は

  1. https://dash.cloudflare.com/ にアクセス
  2. ストレージとデータベース->R2オブジェクトストレージ->概要 にアクセス
  3. 「バケットを作成」から「バケット名」を入力し「バケットを作成」をクリック
    1. のページの右下のAccount Detailsの「API Tokens」項目にある "Manage"をクリック
  4. 「Account APIトークンを作成する」をクリック
  5. 後から見てもわかりやすい「トークン名」を入力し、「管理者読み取りと書き込み」のラジオボタンを有効にしてトークン作成
  6. 必要な情報が揃ったので環境変数に入れていく
.env
CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key_id # 6で取得したもの
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_access_key # 6で取得したもの
CLOUDFLARE_R2_ENDPOINT=https://<ACCOUNT_ID>.r2.cloudflarestorage.com # 2のS3 APIっていうURL
CLOUDFLARE_R2_BUCKET=your-bucket-name # 3で設定したバケット名
CLOUDFLARE_R2_REGION=auto

Rails環境別設定

今回は本番環境のみCloudflare R2を使い、その他の環境ではローカルを利用するように設定

  # config/environments/development.rb
  config.active_storage.service = :local

  # config/environments/production.rb
  config.active_storage.service = :cloudflare

  # config/environments/test.rb
  config.active_storage.service = :test

Railsのモデルへ設定

今回はUserモデルに1つのavatarという画像を設定したかったので以下のように記述

app/models/user.rb
class User < ApplicationRecord
  has_one_attached :avatar
end

ここで最低限の設定は終わったので簡単にテストしてみましょう。
コンソールから適当なものをアップロードしてみます

bin/rails c
user = User.first
# R2にtest.txtをアップロード
user.avatar.attach(
  io: StringIO.new('Hello R2!'),
  filename: 'text.txt',
  content_type: 'text/plain',
  service_name: :cloudflare,
) # => これでtext.txtというファイルがアップロードされるはず

# 確認
user.avatar.attached?
user.avatar.key

# DLしてみて中身を確認
user.avatar.download # => "Hello R2!"

# テスト終わったので削除
user.avatar.purge

これで最低限の設定が終わりました!後は画面からアップロードできるようにしたりアップロードできるもののvalidationを追加したりは適宜行えば良さそうです。

本番環境(Fly.io)へデプロイ

最後に今回はflyioを使っているのでそこにデプロイしておきます。
環境変数などをflyioに設定したあとにデプロイする必要があるので各自必要に応じて変更して環境変数を設定しましょう。

fly secrets set CLOUDFLARE_R2_ACCESS_KEY_ID=xxx
fly secrets set CLOUDFLARE_R2_SECRET_ACCESS_KEY=xxx
fly secrets set CLOUDFLARE_R2_ENDPOINT=https://xxx.r2.cloudflarestorage.com
fly secrets set CLOUDFLARE_R2_BUCKET=your-bucket-name

flyioのconsoleから先程のテストを行えばファイルがアップロードできるかどうかも試せるので試してみると環境変数が設定されているかわかるかと思います🎉

Discussion