S3互換のR2に移行後、アバターの読み込みに時間がかかる

こんにちは、

R2に移行しましたが、すべてが完璧に進みました。すべての画像にS3 CDNのURLリンクが設定されています。しかし、1つ問題に気づきました:アバターの読み込みに時間がかかっています。ユーザーのアバターをクリックしたときや投稿の中身を見ているときなど、平均して3〜4秒ほどかかります。これは想定内の動作でしょうか?

ふむ、3つの異なる問題のいずれかだと思うのですが、最も可能性が高いのはリアルタイムでのリサイズです。

1. リアルタイムのアバターリサイズ

アップロードをR2に移行した際、元の画像が移動しました。ただし、Discourseでは投稿用(45px)やユーザーカード用(120px)など、さまざまなサイズのアバターを使用しています。

これらの特定の最適化されたサイズが完璧に移行されなかった場合、またはまだ生成されていない場合、Discourseはユーザーがクリックした瞬間にそれらを同期的に生成する必要があります。

  1. DiscourseがR2から元の avatar をローカルサーバーにダウンロード
  2. ImageMagickを使用してリサイズ
  3. 新しいサイズをR2にアップロード
  4. ブラウザを新しいURLにリダイレクトし、このプロセスには3〜4秒かかる

確認方法: ページをハードリフレッシュしてください。最初にアバターが3〜4秒かかるが、2回目には即座に読み込まれる場合、まさにこれが起きていることです。

修正方法: ユーザーが閲覧するにつれてサイズが生成されるため、自然と修正されます。ただし、SSHでサーバーに接続して以下のコマンドを実行することで、サーバーにすべてのアバターをバックグラウンドでプリ生成させ、即座に修正することもできます。

./launcher enter app
rake avatars:refresh

2. 3秒のIPv6タイムアウト

アバターが複数回のリフレッシュ後も毎回3〜4秒かかる場合、ネットワークタイムアウトに遭遇している可能性が高いです。

Cloudflare R2 APIエンドポイントはデュアルスタック(IPv4とIPv6の両方を使用)です。サーバーのドロップレットにIPv6アドレスが割り当てられているが、ホストのIPv6ゲートウェイが正しくルーティングされていない場合、R2バケットへのRuby内部接続はまずIPv6を試み、3秒間ハングし(これはLinuxのデフォルトTCPタイムアウトです)、失敗してからIPv4を使用して即座に成功します。

確認方法: サーバーにSSHで接続し、以下を実行してください。

curl -I -6 https://cloudflare.com

数秒間ハングして失敗する場合、サーバーのIPv6が壊れており、内部のS3 APIチェックごとに3秒の遅延が発生しています。

修正方法: ホストコントロールパネルでIPv6ルーティングを修正するか、ドロップレット全体でIPv6を無効にする必要があります。

3. Gravatarの遅延

サイトがGravatarの更新を確認するように設定されている場合、アバターを描画する前にGravatarの外部サーバーにピングを送信する可能性があります。サーバーの送信接続が遅い場合(これもDNSやIPv6に関連していることが多いです)、アバターの描画がブロックされる可能性があります。

確認方法: サーバーで以下を実行してください。
curl -I -6 https://gravatar.com
3秒間ハングする場合、IPv6が壊れています(上記参照)。

Gravatar関連の修正: Discourseの設定で「Gravatarを自動的にダウンロード」を一時的にオフにし、それが修正されるか確認してください。これが問題だとは思いませんが、もし問題であれば、この設定をオフにしたままにするか、上記2のIPv6ルーティングを修正するか、DNSリゾルバーを変更してください。

お返事が早く、ありがとうございます。以前に「rake avatars:refresh」を試した記憶があるのですが、確信はありません。

以前、アバターを即座に表示させる方法として、一度クリックすると開き、二度目のクリックでは瞬時に表示されるという動作が機能していました。これはおそらくキャッシュによるものだと思います。また、2つ目のヒントを試しましたが、「HTTP/2 301」が返され、他にもいくつかの行が表示されました。3つ目のヒントも同様でした。スナップショットを復元する必要があったため、数日後に再度「avatars:refresh」を実行します。再度ありがとうございます!

Gravatar

server: nginx
date: Mon, 22 Jun 2026 19:29:00 GMT
content-type: text/html; charset=utf-8
content-length: 0
content-language: en
expires: Wed, 11 Jan 1984 05:00:00 GMT
cache-control: no-cache, must-revalidate, max-age=0
x-redirect-by: Gravatar
location: https://en.gravatar.com/
alt-svc: h3=":443"; ma=86400
strict-transport-security: max-age=31536000; includeSubdomains; preload

CF

HTTP/2 301
date: Mon, 22 Jun 2026 19:27:00 GMT
content-type: text/html
content-length: 167
location: https://www.cloudflare.com/
cache-control: max-age=3600
expires: Mon, 22 Jun 2026 20:26:59 GMT
set-cookie: __cf_bm=eBP2aJ7Eg30nHPuvMMNxxKrgNtcNwKs0WDgnYyONeus-1782156420-1.0.1.1-sXpW27iuhGDF615cOfwNFybH4IMxgvZy3uA_3X_o..402T_3KSgT7CSymipL5RjdpGe3raWEqsVxQFFLPKRoDjfoT7B.0rqyDt.osbkOF98; path=/; expires=Mon, 22-Jun-26 19:57:00 GMT; domain=.cloudflare.com; HttpOnly; Secure; SameSite=None
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=QfYqSekEDPJHC2k%2BMjHN0cGjz172tmUWe2GSR8EgwNLh3TGjFYkQ0vwPxlzY1NcBcKFOMaAi4FlgjqjhETOOtHf%2BH9KdQSvqN3OME2Uh1i4nHIw%2Fy1qkvSpf4jxDchM7CaDW80tJkjBV4OqF"}],"group":"cf-nel","max_age":604800}
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
strict-transport-security: max-age=15780000; includeSubDomains
server: cloudflare
cf-ray: a0fda5d8ecd6b26d-LAX
alt-svc: h3=":443"; ma=86400

返信をいただいたので、CloudflareとGravatarに対する curl コマンドの結果が期待どおりであることを考えると、ほぼ間違いなく問題#1が原因だと確信しています。都合の良い時に rake avatars:refresh を試してみて、うまくいくかどうか教えていただけますか?

リリーさん、こんにちは。まだ同じ問題が発生しています。rake avatars:refresh を実行した後でも、/latest で同じ問題が発生しています。ブラウザと Cloudflare のキャッシュをクリアしてテストしましたが、まだ解決していません。もしかしたら、もう少し待った方がいいのでしょうか? 4500人のユーザーがいるフォーラムでテストしています。

ブラウザやCloudflareのキャッシュをクリアする必要はありません。rake avatars:refresh を多数のユーザーに対して実行すると、処理は即時には完了しません。代わりに、数千のジョブがSidekiqにバックグラウンドで処理されるようキューに入れられ、サーバーのCPU性能に応じて数時間かかることがあります。申し訳ありませんが、Sidekiqの存在や、ユーザー数によっては完了まで時間がかかることを事前に伝えるべきでした。

your-forum.com/sidekiq/queues にアクセスしてキューの状況を確認してください。キューが完全に空になるまで待ちます。Sidekiqの処理が完了すれば、すべてのアバターのサイズがR2バケットに恒久的に保存され、アバターの読み込み速度も通常に戻ると考えられます。

なるほど、他に何か起きているのかもしれません。自分のキューには何もありません。ですが、あるユーザーのアバターをクリックすると、tail -f log/production.log で以下のような出力が確認できます:

Sent file /var/www/discourse/tmp/avatar_proxy/3689d91eb5e1013beef831c585b5e62edeeecbd6.jpeg (0.2ms)

おっと、なるほど。これは決定的な手がかりで、別の問題を示唆していますね。

ログに avatar_proxy が表示されるのは、通常、Discourse が Cloudflare R2 CDN から直接アバターを提供することを拒否していることを意味します。代わりに、Discourse はリクエストを積極的に傍受し、R2 から画像をローカルサーバーの /tmp フォルダにダウンロードしてから、Ruby を使ってブラウザに画像を提供しています。つまり、CDN が完全にバイパスされており、これが3秒の遅延の原因だと考えられます。サーバーがリクエストのたびに手動でファイルをフェッチして読み込んでいるのでしょう:grimacing:

Discourse はごく限られた特定の状況でのみ avatar_proxy を使用し、通常は外部 URL を隠すためのプライバシーまたはセキュリティ設定が原因です。

管理画面の「サイト設定」で以下の設定を確認してください。

「外部システムアバターの URL」を探してください。もしそのボックスに何か入力されている場合(例:/letter_avatar_proxy/v4/... など)、空になるように削除してください。これで、デフォルトの文字アバターがプロキシされるのを防げます。また、「アップロードされたアバターを許可するグループ」も確認し、TL_0 と表示されているか確認してください。

DISCOURSE_S3_CDN_URL も再確認し、末尾にスラッシュがないか、タイプミスがないか確認してください。

カスタムアバターの再マッピング:
データベースには、新しい CDN URL の代わりに生きた R2 バケット URL が残っている可能性が高いです。これらが一致しないため、セキュリティ上の理由からフォラムがそれらをプロキシしているのでしょう。

Rails コンソールで、Discourse が何と戦っているかを確認してください。

./launcher enter app
rails c

アバターの読み込みが遅いユーザー名を選んでください。

u = User.find_by_username("the_selected_username")
u.user_avatar.custom_upload.url

出力に生きたバケット URL が返ってきた場合、以前の再マッピングがすべてを捕捉できていません(サブドメインやスキーマを見落としている可能性があります)。

修正するには、サーバーに SSH で接続し、コンテナに戻ってください(Rails ではなく)(./launcher enter app)、そして再マッピングツールを再度実行して、生 URL を CDN URL に置き換えます(またかよ、という感じですが):

discourse remap "https://<your-raw-cloudflare-url>.r2.cloudflarestorage.com" "https://cdn.your-domain.com"

念のため、2 回目は https:// の代わりに // を使用して実行してください。

余談ですが、好奇心から伺いたいのですが、どのようなホスティングサービスをお使いですか?私もあなたとほぼ同じ構成ですが、この問題はまだ経験していません。そのため、あなたの構成にも興味があり、何かしらの方法で再現を試みたいと考えています。

取得したURLはS3のCDN URLを表示しており、ブラウザで画像を開くことができます。

今回はS3は使用しないことにします。現時点では特に必要ないためです。

私は長年Advinserversを使用しています。

ご協力いただきありがとうございます。大変助かりました。

さらにテストを実施し、この問題がR2に関連していないことを確認しました。適切に設定されたAWS S3を使用した場合でも、同じ現象(投稿時のアバター読み込みの遅さや、ユーザー名をクリックした際の問題)が継続しています。また、"ufw status"でファイアウォールが現在無効であることを確認しており、ファイアウォールが原因ではありません。今週末にはステージング環境でより多くのテストを行う予定です。ここでは、フォーラムを長時間稼働させたり、必要に応じてオフラインにしたりすることが可能です。

サイトとS3の両方のCDNが設定されていますか?

はい、両方とも使用しています。バケットはCloudFrontとS3 CDN URLで接続されており、Discourse CDN URLについても同様です。

R2のテストでは、Discourse CDN URLを使用していませんでした。

動いていない方のサーバーでは使ってないんですよね?

Discourse CDNを使わないR2の設定で問題が発生している、ということですね?

あなたの問題が何なのか、またアバター画像がどのように動作しているのか、正確に理解しているとは言い切れませんが、さらにテストを行う前にCDNの設定を整えておくことをお勧めします。もしかすると、新しいバケットに移行したことが原因で、画像を再生成する必要があるのかもしれません。

このサイトのアバターは、Discourse CDNのように見えるURLから読み込まれています:https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/48/555832_2.png

ユーザープロフィールは、一度読み込まれた後、2回目以降の読み込みが速くなりますか?

繰り返しになりますが、コードを確認したわけではなく、理解しているとも保証しませんが、これらはDiscourse CDNによって配信されており、Discourseはそれ以降のリクエストがCDNから取得することを前提にしている、と推測します。これが、Discourse CDNがないR2バージョンで動作しない(または遅い)理由を説明していると思います。

ここで言っている意味を完全に理解していないかもしれませんが、私はR2オブジェクトストレージを実行している2つのサイトを持っており、この問題には遭遇していません。:woman_shrugging:t2:

Discourse CDN を持っていないのでしょうか?もし持っていないなら、私の理解は間違っています。持っているなら、Discourse CDN がないこと(S3 バケットがある場合)がこの問題の原因かもしれません。

でも、S3 を使った場合と使わない場合でアバターの挙動が変わるのは奇妙に思えます。

実際、私は4つの異なる設定でこれをテストしました:

  1. Discourse CDNなしのR2

  2. Discourse CDNありのR2

  3. Discourse CDNありのAWS S3

  4. Discourse CDNなしのAWS S3

すべてのケースで、S3 CDN URL(files.mydomain.com)を使用しました。

Discourse CDNを使用するケースでは、cdn.mydomain.comを使用しました。

問題は、どのシナリオでもアバターの読み込みが非常に遅いことです。

トピックを開くと、アバターが1つずつ読み込まれるのが見えます。ただし、これは1回だけ発生します。例えば、admin/usersに移動すると、最初はニックネームしか表示されず、その後アバターが1つずつ読み込まれ始めます。

ニックネームをクリックすると、ユーザーカードが開き、3〜4秒後にアバターが表示されます。これも1回だけ発生し、再度クリックするとアバターは即座に表示されます。これはおそらくキャッシュによるものです。

PS: 各テストを実行する際、S3/R2を使用していなかった時点のスナップショットを復元し、バケットを削除して最初からやり直します。

オブジェクトストレージの設定とは無関係だろうと推測します。4つの設定すべてでアバターが遅いのは、ストレージプロバイダーがボトルネックではなく、サイトドロップレットとバケット間のネットワークレイテンシ、またはサーバーのCPUが画像のリアルタイムリサイズに追いついていないからではないでしょうか。サーバーやCDNの設定、またここでの距離については詳しくありませんが、エッジキャッシュが構築されれば、どのストレージを使うかは問題にならなくなるはずです。ただし、1つのストレージを使い続け、キャッシュが自然に構築されるのを待つ必要があります。ただし、現時点では推測に過ぎません。他のアイデアがないためです。:woman_shrugging:t2: :grinning_cat_with_smiling_eyes:

もう、どう考えればいいのか分かりません。R2では西ヨーロッパ(WEUR)リージョンを使用し、S3では eu-north-1 を使用していました。私のVPSの仕様は以下の通りです:

AMD Turinプロセッサ(4 vコア) 8GB DDR5 ECCメモリ 256GB NVMe SSDストレージ 5TBの帯域幅(10 Gbit) カリフォルニア州ロサンゼルスに位置

次はアメリカのリージョンでテストすべきでしょうか?R2ではそれができないと思います。

その通りです。すべてのアバターを生成するには時間がかかります。rakeタスクを実行すれば生成は行われますが、特にユーザーが多い場合は非常に時間がかかります。初めてユーザーにアクセスした際、アバターが生成され、画像データをさまざまなサイズに処理する外部プログラムが数秒間実行されます。その後、問題なく動作します。rakeタスクを実行して、すべてのアバターがさまざまなサイズで生成されるのを待つ必要があるだけではないでしょうか?

うん、そのあたりを言いたかったんだけど、うまく説明できてなかったかも:

[quote=“Lilly, post:2, topic:405827”]
確認方法: ページをハードリフレッシュしてください。初回にアバターの読み込みに3〜4秒かかるが、2回目以降は瞬時に読み込まれる場合、まさにこの現象が起きています。

解決方法: ユーザーが閲覧するにつれてサイズが生成されるため、自然に解消されます。ただし、SSHでサーバーに接続し、以下のコマンドを実行して、バックグラウンドですべてのアバターを事前に生成させることで、即座に修正できます:

[/quote]\nでも、毎回読み込みが遅いって言ってたような気がしますね。:thinking:

S3の設定を変更したり、キャッシュをクリアするたびに、最初からやり直しているような気がします。ユーザーのアバターが多い場合、それには非常に時間がかかります。