Rake uploads:migrate_from_s3 が失敗しました

こちらの手順に従い、サイト全体をバックアップして AWS S3 バケットをクローンし、Discourse の設定でバケット名を元のバケットからバックアップ用に変更し、設定内の「S3 へのアップロード」チェックボックスをオフにしました。

これでようやく S3 からの移行を開始できるはずだったのですが、失敗してしまいました :frowning:

エラーメッセージ

root@ubuntu:/var/www/discourse# rake uploads:migrate_from_s3
Migrating uploads from S3 to local storage for 'default'...
rake aborted!
NoMethodError: undefined method `downcase' for nil:NilClass
/var/www/discourse/app/models/global_setting.rb:107:in `s3_bucket_name'
/var/www/discourse/app/models/site_setting.rb:157:in `absolute_base_url'
/var/www/discourse/lib/tasks/uploads.rake:138:in `migrate_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:118:in `block in migrate_all_from_s3'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.2.2/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.2.2/lib/rails_multisite/connection_management.rb:78:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:118:in `migrate_all_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:93:in `block in <top (required)>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => uploads:migrate_from_s3
(See full trace by running task with --trace)

(失敗している箇所の GitHub 上のコードはこちら です。おそらく s3_bucket の値を取得できていないのでしょう?)

試した他のこと

  • コマンドラインに認証情報を追加してみましたが、状況は変わりませんでした。つまり:
    DISCOURSE_S3_BUCKET="dn-forum-storage-backup" DISCOURSE_S3_REGION="us-east-1" DISCOURSE_S3_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxxxx" DISCOURSE_S3_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxx" DISCOURSE_S3_CDN_URL="https://dn-forum-storage-backup.s3.us-east-1.amazonaws.com" rake uploads:migrate_from_s3

  • 設定内の S3 バケット名を元のバケット名に戻してみましたが、やはり同じ結果でした。

  • アプリを再ビルドしてみましたが、同じ結果でした。

@vinothkannans さん、何が起きているかご存知ですか?

Discourse の皆様、どうかご助力ください!

追伸:小さな余談ですが、rake --tasks を実行してもこのタスクや uploads で始まるタスクがリストされません。これが何か意味を持つのかはわかりません。

関連する可能性のある問題はありませんか?cc @mcdanlj

@pnoeric はい、それはまさに同じもののようです。そのissueで、S3 における SiteSettingsGlobalSettings の意図について明確な回答は得られていません。そのため、現時点では私の投稿の要点1(設定を通じて SiteSettings に追加する)以上のサポートはできません。

こんにちは、返信ありがとうございます… SiteSettingsGlobalSettings の違いがよくわかりません。RoR のコードはあまり得意ではなく、全体のセットアップも十分に理解していないんです。基本的な手順に従っているだけです。:wink:

でも、ぜひ @vinothkannans さんも参加してくれるといいですね。彼がマイグレーションタスクのコードを書いたと思うので。あるいは、知っている @team のメンバーなら誰でも構いません…

このトピックに注目し続けましょう…

s3_bucketGlobalSettings にあり、通常は app.yml ファイル内の環境変数から作成される config/discourse.conf ファイルで設定されます。SiteSettings は、アプリの管理設定から変更する項目です。

当初は、S3 の設定を変更するにはアプリの再構築が必要でしたが、最近では管理設定でデータを入力できるようになりました。SiteSettings で S3 を設定できるようになった際に、一括移行を行わなかった意図が何だったのかはわかりません。

[編集:最初の投稿で二つを逆転させてしまいました]

「いいね!」 1

うーん、@mcdanlj さん、確認ですが、migrate_from_s3 が実際に動作するようにする方法がまだお分かりになっていない、ということでよろしいでしょうか?

必要な設定や低レベルのファイルなど、どのような編集でも構いません。S3 は非常に高額なため、できるだけ早く移行する必要があります。

@pnoeric さん、@mcdanlj さん、こんにちは。

デバッグのアプローチとして、Rails コンソールに入り、s2 サイト設定を確認するのはどうでしょうか?

例えば、シンプルな OOTB の Discourse Docker 単一コンテナスタンドアロンアプリでは以下のようになります:

root@localhost:~# docker exec -it app rails c
[1] pry(main)> SiteSetting.s3_upload_bucket
=> ""
[2] pry(main)> SiteSetting.enable_s3_uploads
=> false
[3] pry(main)> 

Rails コンソール経由で SiteSettings を、以下のデフォルト値と比較してみてください:

この方法でデバッグできれば役立つかもしれませんが(実際には AWS や S3 を使用していないので確信はありません)、Rails コンソールが少し助けになるかもしれません。

「いいね!」 1

rake --tasksは、説明が設定されているタスクのみを表示します。利用可能なすべてのタスクは rake -AT で確認できます。

役に立たないと思います。なぜなら、私はごく最近テストサイトでこれらのタスクを実行したからです。どちらも env に S3 変数が定義されていることに依存しているようですが、数ヶ月前の話であり、migrate_from_s3 は私にはうまく機能しませんでした。

「いいね!」 1

難しい質問ですね。私は、あなたがリンクした投稿で言及されていた通り、config/discourse.confs3_bucket を設定しました。その結果、その特定のエラーは解消されました(私もそこでそのように記述しました)。

このファイルはコンテナ内部にあります(./launcher enter app)。また、./launcher rebuild app を実行してもこの設定が保持されるようにするには、containers/app.yml ファイルの env セクションに DISCOURSE_S3_BUCKET を追加する必要があります。

私がこれを修正できたからこそ、これはサポートリクエストではなく開発者向けの投稿だったのです。私は引き続きこのコードをいじりながら、開発者たちが「正しい」解決策は何だと考えているかを尋ねたかったのです。

S3 には約 100GB のファイルがあるため、非常に慎重に進めています。現在、確認対象の投稿数に制限を設け、次に修正対象の投稿数に制限を設ける必要があります。私は一度に一つずつ試しています。このコードはあまり使われていないようですが、私はこのエラーを繰り返し目にしており、コードの劣化を懸念しています。バグによって突然サイト全体が壊れることを避けたいと考えており、これはそのような過ちを犯しやすい状況のように思えます。

  • upload://(私の場合、動画以外のアップロード)については、今のところ問題なく動作しているようです。一つずつ処理し、影響を受けた投稿を確認してすべてが正常に動作しているか確認しています。

  • upload:// 構文を使用しないアップロード(私の場合、確認できる限りでは動画アップロード)の場合、S3 の URL がリテラルで参照されている箇所では、URL が壊れてしまっています。何を何に書き換えるべきかを確実に理解すれば、修正はそれほど難しい問題ではありませんが、まだその点を確定できていません。そのため、これは私が近日中に投稿する PR の一つになる可能性が高いです。

これは私の余暇のプロジェクトですので、時期については約束できません。

「いいね!」 1

なるほど、ありがとうございます!試してみます。

うーん、やっぱりダメでした。@neounix @mcdanlj @vinothkannans さん。まだ失敗しています。でも、少なくとも新しい(あるいは異なる)エラーメッセージが出ているので、少し前進かもしれません…

今日は以下を試しました:

  1. 念のため、Discourse を最新バージョンにアップグレード。

  2. config/discourse.confs3_bucket を追加。

  3. ./launcher enter app

  4. containers/app.yml を編集し、DISCOURSE_S3_BUCKET 変数を追加。

  5. rake uploads:migrate_from_s3 を実行したが、今度は新しいエラーメッセージで失敗(以前は downcase が問題でしたが、今回は start_with? のようです):

/var/www/discourse# rake uploads:migrate_from_s3
Migrating uploads from S3 to local storage for 'default'...
rake aborted!
NoMethodError: undefined method `start_with?' for nil:NilClass
/var/www/discourse/app/models/site_setting.rb:161:in `absolute_base_url'
/var/www/discourse/lib/tasks/uploads.rake:138:in `migrate_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:118:in `block in migrate_all_from_s3'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:78:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:118:in `migrate_all_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:93:in `block in <main>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => uploads:migrate_from_s3
(See full trace by running task with --trace)
  1. そこで ./launcher rebuild app を実行。

  2. さらに ./launcher enter app、そして rake uploads:migrate_from_s3 を再度実行。

全く同じ問題が発生しました:

/var/www/discourse# rake uploads:migrate_from_s3
Migrating uploads from S3 to local storage for 'default'...
rake aborted!
NoMethodError: undefined method `start_with?' for nil:NilClass
/var/www/discourse/app/models/site_setting.rb:161:in `absolute_base_url'
/var/www/discourse/lib/tasks/uploads.rake:138:in `migrate_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:118:in `block in migrate_all_from_s3'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:78:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:118:in `migrate_all_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:93:in `block in <main>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => uploads:migrate_from_s3
(See full trace by running task with --trace)

他に何か良いアイデアはありますか?

ちなみに、この手順は本当に手間がかかります。事前に数日前からフォラムの停止を予定し、発表する必要があります。当日はメインサイトを編集してユーザーがフォラムにアクセスできないようにし、その後 DigitalOcean のフォラムサーバーをシャットダウンしてスナップショットを取得してから進めなければなりません。それだけで約 30 分です。その後、サーバーを再起動して上記の手順を試します。Amazon S3 をメディア保存用に設定したことを本当に後悔しています。この選択を元に戻そうと何時間も費やしましたが、まだ成功していません(しかも毎月 Amazon に高額な請求が来ています)。根本原因を突き止めたいです。どのようにお手伝いできますか?

その行は以下の通りです:

        if SiteSetting.Upload.s3_region.start_with?("cn-")

s3_region も必要のようですが、なぜ私がそれに直面しなかったのかはわかりません。

あなたの論理が理解できません。私が約100GBのコンテンツを移行する際は、通常のサイトバックアップ後にライブ移行を行う予定です。しかし、最初は小さく始めようとしているため、一度に移行する量を制限する作業を進めています。一点警告:コードは、動画アップロードで確認した通り、リテラルな URL の変換に対して「間違っている」ように見えます。そのため、動画アップロードを許可している場合、現在のコードの状態では問題が発生する可能性があります。

「いいね!」 2

では、私が上記で行った手順をすべて繰り返す必要があるかもしれません。ただし、s3_bucket、s3_region、s3_cnd_url、s3_secret_access_keyなど(つまり、私が持っているすべての変数)をconfファイルとymlファイルに追加しようと思います。実際には動くようにするために、(多分)必要以上に多くのものを与えたいです。

Discourseチームの誰かが、この移行を開始する前にローカルサイト全体をバックアップすることを提案しているのを見ました。これには、Digital Oceanのサーバーをオフラインにする必要があります。:frowning:

なるほど。私も小さく始めています…毎回試みると、移行されるファイルは0個です。:grin:

幸いなことに、私のフォーラムではメンバーがアップロードできるのはJPG、GIF、PNGのみなので、問題ないはずです。

祈りを込めて。

バックアップとスナップショットは同じではありません。スナップショットは最も粗い形式のバックアップです。管理コンソールにはバックアップ機能があります。まず設定でサムネイルのバックアップを有効にするように設定してください。

サイトを停止する必要がないことがわかった今、少し安心できるはずです。batch_migrate_from_s3 を使って、最大で一定数のアップロードを移行できます。現在は移行された数ではなく、対象となる投稿数を制限していますが、これは将来の PR で私が修正すべきバグです。ただし、動画アップロードのバグも修正する必要があり、また、制限の目的の一つが「移行が成功したかどうかを影響を受けた投稿で確認できること」なので、フィードバックの出力についても検討したいと考えています。

今後 1〜2 ヶ月かけてこれらを進める予定なので、もしそれを待ってみたいなら、S3 の利用料をさらに数ヶ月余計に払う価値があるかもしれません。これはあなた次第です。私は約束をしているのではなく、単に意向を述べているだけですが。

「いいね!」 2

@pnoeric サイトの稼働時間について懸念されているとのことですので、私がこれまで学んだことを共有させていただきます。

私が述べた通り、移行はライブで行いました。移行をレート制限しないと、ユーザー間のアクティビティを通知するなどの処理を行うキューが混雑し、サイトのユーザーエクスペリエンスが損なわれてしまいます。

動画を含む約500件の投稿と、画像を含む約3万件の投稿を移行しましたが、完了までに約2週間かかりました。

私が使用したコードを試してみたい場合は、現在以下の場所にあります。

これをダウンロードして、アプリ内の lib/tasks/uploads.rake の現在の内容を置き換えるようにコピーしてください。

このコードを使用すると、以下のような操作が可能です。

bin/rake uploads:batch_migrate_from_s3[100,1000]

これにより、アップロードを含む1000件の投稿のみを対象とし、最大100件のファイルから移行を行い、その後停止します。アップロードの移行後に投稿を実際に変更するたびに、次の処理を開始する前にキューが空になるまで待機します。

このファイルをコピーすると、変更を元に戻すまで今後のサイト更新が破損します。変更を元に戻す最も簡単な方法は、満足した後に ./launcher rebuild app を実行することです(ただし、開発者としては git checkout HEAD lib/tasks/uploads.rake を使用して変更を元に戻しています)。

少なくとも Digital Ocean Spaces の場合、移行が成功するまでに何度か再試行が必要なことがありました。現在のスクリプトでは、その場合の警告が表示されず、実行し続けて結果を待つしかありません。その際にエラーを出力するPRをレビュー待ちしていますので、何か問題が発生したことが少なくともわかるようになります。

簡単な短い再試行ループとエラーメッセージを追加しましたが、再試行ループによって問題が解決したようです。また、過去の投稿の生コンテンツに対して現在のルールに基づく検証が行われており、移行が破損したり、再焼成が必要な投稿が静かに残されたりする問題がありましたが、これも修正しました。少なくとも検証の修正が含まれていなければ、移行を行いたくないはずです。これは現在レビュー待ちの私のPRに含まれるコミットの1つです。

私の移行は、私の知る限り完了しました。私のPRには、移行を完了するために使用したコードがすべて含まれています。レビューは行われていません。もし関心がある場合は、Migrate_from_s3 problems をフォローすることをお勧めします。

「いいね!」 2

ありがとうございます!今後数日中に試してみます。

その投稿に、本日発見された最後のバグについて追記しました。一部のユーザーのプロフィール写真が消失しているという問題で、原因は不明です。肩をすくめながら、影響を受けたユーザーに復元を依頼し、問題に対して謝罪しています。

このプロセス中は頻繁にバックアップを取得しました!:smiling_face:

幸運を祈ります!

「いいね!」 1

本当に、これで私がさらに狂ってしまうのでしょうか?:crazy_face:

やったことは以下の通りです:

  1. 新しい lib/tasks/upload.rake のコードを私の Discourse にコピー
  2. すべての Amazon s3_ 変数を config/discourse.conf に追加
  3. それらを app.yml にも追加(これで何か変わるかは不明ですが、やらない手はありません)
  4. このコマンドを実行して…
root@:/var/www/discourse/config# rake uploads:batch_migrate_from_s3[100,1000]
You must disable S3 uploads before running that task.

というエラーが出てしまいました。

そして確認したのはこちら:

なるほど。uploads.rake ファイルを編集して、そのチェックを削除しました。

次に実行すると…

root@:/var/www/discourse/lib/tasks# rake uploads:batch_migrate_from_s3[100,1000]
Migrating uploads from S3 to local storage for 'default'...
Migrating up to 100 of 1000 posts...
... (lots of output here) ...
Modified 91/100: 28795: 28486/1 - https://example.com/t/topic-title-here/28486/1
... (lots of output here) ...

これで動いているように見えました!やったー!

最初の 100 件が完了した後、sidekiq を確認するとテスト投稿がキューに入っているのが見えたので、完了するまで待ちました…

…その後、再度確認しましたが、その投稿はまだ画像を Amazon S3 から読み込んでいます。:frowning: 投稿に対して「HTML の再構築」を試みましたが、変化はありませんでした。

そこで、rake コマンドからすべて再度実行してみましたが、同じ結果になりました。同じ 100 件の投稿が処理され、sidekiq にも同じものがキューに入れられ、実行後にテスト投稿の画像は依然として S3 から読み込まれていました。

ふむ、次に何を試せばいいか分かりません。:man_shrugging:t2:

@mcdanlj さん、もし何かアドバイスや提案があれば嬉しいです :wink:

「いいね!」 1

そのチェックを削除すれば、まさにそうなるはずです。なぜ削除したのかはわかりません。意図的なものです。マイグレーションを開始する前に、S3へのアップロードを無効にしてください。

「いいね!」 1

完全に外れていました。完全に外れています。(私の投稿にあるチェックボックスの画像は正しい設定で合っていますか?)オンにしてから再度オフにしましたが、全く動きません。