DiscourseコミュニティのCloudflare R2の設定方法

Cloudflare R2 バケットは、Discourse コミュニティの画像や GIF などの静的アセットの保存に使用できますが、コミュニティのバックアップの保存には使用できません!

はじめに:

Cloudflare R2 オブジェクトストレージは、Discourse フォーラムのアップロードを保存するために Amazon S3 の代替として使用できます。次の手順に、これを構成する方法の概要を示します。

構成手順:

  1. S3 アップロードを有効にする: Discourse の設定で S3 アップロードを有効にするチェック ボックスをオンにします。
  2. S3 アクセス キー ID: R2 ストレージ バケットの API キー ID を入力します。これは、バケットの API トークンを作成したときに提供された ID です。
  3. シークレット アクセス キー: ストレージ バケットへのアクセスを許可する API トークンを作成したときに提供されたシークレット キーを入力します。重要: このシークレット キーは一度しか表示されないため、安全にバックアップしてください。
  4. S3 リージョン: R2 には関係ないため、任意のリージョンを入力できます。
  5. S3 アップロード バケット: R2 ストレージ バケットの名前を入力します。
  6. S3 エンドポイント: R2 バケットの S3 API リンクを入力します。形式は https://xxxxxx.com です。このリンクは Cloudflare R2 ダッシュボードで確認してください。
  7. S3 CDN URL: バケットの公開 R2.dev ストレージ バケット URL を入力します。これも Cloudflare R2 ダッシュボードで見つけることができます。

完了:

これらの設定が構成されると、Discourse フォーラムはストレージに Cloudflare R2 を使用するようにセットアップされます。

無料ティア情報:

Cloudflare の R2 サービスは、10 GB のストレージ、月間 100 万回のアップロード、および月間 100 万回の読み取り操作を含む無料ティアを提供します。

「いいね!」 5

アップロード用のS3互換オブジェクトストレージプロバイダーの設定方法](Configure an S3 compatible object storage provider for uploads)の例に従い、設定をデータベースではなくymlファイルに入れることをお勧めします。

フィードバックありがとうございます。以前にガイドを注意深く読みましたが、Cloudflare R2に関するアドバイスは間違っていると思います。記事では、DiscourseコミュニティはCloudflare R2バケットをサポートしていないと示唆しています。しかし、実際には、Cloudflare R2はS3と非常に互換性があり、Discourseコミュニティの画像やファイルのアップロードとダウンロードを完全に処理できます。これは、私のコミュニティ(starorigin.net)での実際の適用を通じて検証されています。

「いいね!」 1

そして、それは書かれた当時、真実だったと私は推測します。

S3設定をUX経由で設定してデータベースに保存するよりも、ymlファイルに配置する方がはるかに優れています。データベースを新しいサーバーに復元したことはありますか?

推奨される方法でセットアップしたら、そのトピックを編集するか、コメントを作成して他の人に依頼することができます。

「いいね!」 1

おっしゃる通り、コミュニティの画像、GIF、その他のリソースを保存するためにCloudflare R2ストレージバケットを使用しています。これにより、コミュニティサーバーの負荷が大幅に軽減され、ページの読み込み速度が向上します。

Cloudflare R2バケットは圧縮ファイルの保存をサポートしていないため、コミュニティの自動バックアップをCloudflare R2ストレージバケットに保存するように設定していません。しかし、Cloudflare R2ストレージは、コミュニティのPDF、画像、GIF、その他の静的リソースを保存できるため、これも非常に優れています。

おかしいな。以前はバックアップにR2を使用していたと思ったのですが。しかし、記憶違いかもしれません。

それでも推奨される手順に従い、そこにバックアップを置かないように注意することができます。

リマインダーをありがとうございます。この部分を強調します。

Cloudflare R2 バケットは、Discourse コミュニティの画像や GIF のような静的アセットを保存するために使用できますが、コミュニティのバックアップを保存するために使用することはできません!

cloudflare が機能するために含める必要があったいくつかの注意点をこの投稿に追記します。


1. リージョン


これは真実ではありませんでした。私は「auto」または選択したリージョンを使用する必要がありました。「auto」の方が簡単なので、「auto」を使用してください。
使用できるオプションを知る必要がある場合は、ランダムな文字列をリージョンに入力して試してください。

sudo -E -u discourse bundle exec rake s3:upload_assets

nixos を使用する場合

sudo discourse-rake s3:upload_assets

これにより、有効なオプションのエラーが出力されます。


2. API の権限


制限的な API トークンは機能しないことも重要です。Admin Read & Write を使用する必要があります。
Object Read & Write は機能しませんでした。

「いいね!」 3

sudo -E -u discourse bundle exec rake s3:upload_assets を実行するとエラーが発生しました @Eviepayne

地域を自動に設定します。
以下も設定する必要がある場合があります。
DISCOURSE_S3_INSTALL_CORS_RULE: false

上記の両方を行い、app.yml を再構築しました。

  ## S3 設定
  DISCOURSE_USE_S3: true
  DISCOURSE_S3_REGION: auto
  DISCOURSE_S3_ACCESS_KEY_ID: XXX
  DISCOURSE_S3_SECRET_ACCESS_KEY: XXX
  DISCOURSE_S3_CDN_URL: https://pub-XXX.r2.dev
  DISCOURSE_S3_ENDPOINT: https://XXX.r2.cloudflarestorage.com/XXX
  DISCOURSE_S3_BUCKET: XXX
  DISCOURSE_S3_INSTALL_CORS_RULE: false

また、API キーがバケット固有のキーではなく、アカウント API キーであることを確認しました(投稿で言及されているように)。さらに、私の Discourse インスタンスには次のように表示されます。

そして、sudo -E -u discourse bundle exec rake s3:upload_assets を実行すると、次のように表示されます。

`/root` は書き込み可能ではありません。
Bundler は一時的に `/tmp/bundler20250410-2363-zj2g6x2363` をホームディレクトリとして使用します。
CORS ルールをインストールしています...
スキップ
rake が中止しました!
Seahorse::Client::NetworkingError: 空または不完全なレスポンスボディ (Seahorse::Client::NetworkingError)
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/seahorse/client/plugins/raise_response_errors.rb:17:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-s3-1.182.0/lib/aws-sdk-s3/plugins/sse_cpk.rb:24:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-s3-1.182.0/lib/aws-sdk-s3/plugins/dualstack.rb:21:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-s3-1.182.0/lib/aws-sdk-s3/plugins/accelerate.rb:43:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/checksum_algorithm.rb:169:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/jsonvalue_converter.rb:16:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/invocation_id.rb:16:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/idempotency_token.rb:19:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/param_converter.rb:26:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/seahorse/client/plugins/request_callback.rb:89:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/response_paging.rb:12:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/seahorse/client/plugins/response_target.rb:24:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/telemetry.rb:39:in `block in call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/telemetry/no_op.rb:29:in `in_span'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/telemetry.rb:53:in `span_wrapper'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/telemetry.rb:39:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/seahorse/client/request.rb:72:in `send_request'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-s3-1.182.0/lib/aws-sdk-s3/client.rb:12654:in `list_objects_v2'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-s3-1.182.0/lib/aws-sdk-s3/bucket.rb:1513:in `block (2 levels) in objects'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/plugins/user_agent.rb:69:in `metric'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-s3-1.182.0/lib/aws-sdk-s3/bucket.rb:1512:in `block in objects'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:101:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:101:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:101:in `block in non_empty_batches'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:52:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:52:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:52:in `block in each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:58:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:58:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/aws-sdk-core-3.219.0/lib/aws-sdk-core/resources/collection.rb:58:in `each'
/var/www/discourse/lib/tasks/s3.rake:14:in `map'
/var/www/discourse/lib/tasks/s3.rake:14:in `existing_assets'
/var/www/discourse/lib/tasks/s3.rake:36:in `upload'
/var/www/discourse/lib/tasks/s3.rake:197:in `block (2 levels) in <main>'
/var/www/discourse/lib/tasks/s3.rake:197:in `each'
/var/www/discourse/lib/tasks/s3.rake:197:in `block in <main>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/exe/rake:27:in `<top (required)>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'
Tasks: TOP => s3:upload_assets
(See full trace by running task with --trace)

エンドポイントからバケット名を削除する必要があるかもしれません。
末尾の /xxx を削除して、.com のみになるようにしてください。

再構築中で、コマンドを再実行します。ご協力ありがとうございます!

私の app.yml は以下のようになりました。

  ## S3 設定
  DISCOURSE_USE_S3: true
  DISCOURSE_S3_REGION: auto
  DISCOURSE_S3_ACCESS_KEY_ID: XXX
  DISCOURSE_S3_SECRET_ACCESS_KEY: XXX
  DISCOURSE_S3_CDN_URL: https://pub-XXX.r2.dev
  DISCOURSE_S3_ENDPOINT: https://XXX.r2.cloudflarestorage.com
  DISCOURSE_S3_BUCKET: XXX
  DISCOURSE_S3_INSTALL_CORS_RULE: false

すべて正しいと思います。
CDN_URL(https://pub-xxx.r2.dev)に
パブリック読み取りアクセス権があり、匿名ユーザーがアセットを表示できることを確認してください。
ブラウザの開発者ツールで何が起こっているかを確認できます。権限が間違っている場合、ネットワークタブに多数の403エラーと赤いリクエストが表示されます。

はい、そう思います。

これは正しい設定ですか?

それは一つの方法ですが、推奨される方法ではなく、問題が発生します。
ドメインとCloudflareがすでにDNSであると仮定します。

Cloudflareは自動的にプロキシでき、そのドメインのキャッシュを行うことができます。
その後、CDN_URLをそのカスタムドメインに変更できます。

カスタムドメインをバケットに接続する必要があるのですか?

S3 バケットの設定内に、パブリックアクセス設定があります。
ユニークなサブドメインを設定します。(Cloudflare が DNS レコードを自動的に作成し、プロキシとキャッシュも行います)

これでできたと思いますか?

バックアップもCloudflare R2で機能するようにしましたか?また、Cloudflare R2へのバックアップが可能であると仮定した場合、ローカルとCloudflare R2の両方にバックアップするように設定することは可能ですか?

また、アセットをすべてアップロードするスクリプトは、ローカルのアセットを削除することを意味しますか(ストレージを解放するため)?それとも、別途実行する必要のある手順がありますか?

お手伝いいただきありがとうございます :slight_smile:

試したことはありません。
私のフォーラムは「サポート対象外」に分類されます。データベースが外部にあり、フォーラムが使用するpg_dumpsとは異なるバックアップ戦略を採用しているためです。
聞いた話によると、Cloudflareではバックアップは機能しないようですが、試してみるのを止めるものはありません。

「いいね!」 1