復旧支援 - システムが真夜中にハングアップしました

まあ、これは起きるべくして起きたことですね。これまでずっと調子が良すぎました。長年、クルーズコントロール状態で動いていて、システムは自動的に更新され、Discourse も数週間ごとに私が更新していました。昨夜の真夜中、Amazon からシステムが応答しないと通知があり、Discourse がダウンし、CPU が 100% に張り付いて AWS の CPU リソースが尽きるまでそのままの状態でした。システムにログインできず、数回の再起動後に一時的にログインできた際、htop で次のプロセスが CPU を大量に消費しているのを確認しました。

snap lxd activate

もしこれをご覧になった方や、なぜこれが自然に発生したのか理由がわかる方がいれば、今後の参考のためにご教示いただければ幸いです。

さて、現在の切迫した問題ですが、Ubuntu 20LTS を使用して AWS 上に新しいサーバーを再構築しました。Discourse のセットアップは非常に簡単でした。app.yml ファイルのコピーを持っていたので、それを使って Discourse フォーラムを再構築しました。古いサーバーでは、バックアップとコンテンツ(画像など)の両方に S3 を使用していました。

サーバーを作成した後、S3 から最新の Discourse バックアップファイルをダウンロードし、手動で Discourse サーバーにアップロードし、「Restore」ボタンを押しました。数分後、以下のエラーが表示されました。

[2022-06-09 09:01:56] ALTER TABLE
[2022-06-09 09:01:56] ALTER TABLE
[2022-06-09 09:01:56] Migrating the database...
[2022-06-09 09:02:11] == 20220308201942 CreateUploadReferences: migrating ===========================
-- create_table(:upload_references, {})
   -> 0.0486s
-- add_index(:upload_references, [:upload_id, :target_type, :target_id], {:unique=>true, :name=>"index_upload_references_on_upload_and_target"})
   -> 0.0030s
== 20220308201942 CreateUploadReferences: migrated (0.0580s) ==================

== 20220309132719 CopyPostUploadsToUploadReferences: migrating ================
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT post_uploads.upload_id, 'Post', post_uploads.post_id, uploads.created_at, uploads.updated_at\nFROM post_uploads\nJOIN uploads ON uploads.id = post_uploads.upload_id\nON CONFLICT DO NOTHING\n")
   -> 0.0595s
== 20220309132719 CopyPostUploadsToUploadReferences: migrated (0.0602s) =======

== 20220309132720 CopyPostUploadsToUploadReferencesForSync: migrating =========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT upload_id, 'Post', post_id, NOW(), NOW()\nFROM post_uploads\nON CONFLICT DO NOTHING\n")
   -> 0.0076s
== 20220309132720 CopyPostUploadsToUploadReferencesForSync: migrated (0.0080s) 

== 20220330160747 CopySiteSettingsUploadsToUploadReferences: migrating ========
-- execute("WITH site_settings_uploads AS (\n  SELECT id, unnest(string_to_array(value, '|'))::integer upload_id\n  FROM site_settings\n  WHERE data_type = 17\n  UNION\n  SELECT id, value::integer\n  FROM site_settings\n  WHERE data_type = 18 AND value != ''\n)\nINSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT site_settings_uploads.upload_id, 'SiteSetting', site_settings_uploads.id, uploads.created_at, uploads.updated_at\nFROM site_settings_uploads\nJOIN uploads ON uploads.id = site_settings_uploads.upload_id\nON CONFLICT DO NOTHING\n")
   -> 0.0034s
== 20220330160747 CopySiteSettingsUploadsToUploadReferences: migrated (0.0038s) 

== 20220330160751 CopyBadgesUploadsToUploadReferences: migrating ==============
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT badges.image_upload_id, 'Badge', badges.id, uploads.created_at, uploads.updated_at\nFROM badges\nJOIN uploads ON uploads.id = badges.image_upload_id\nWHERE badges.image_upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0006s
== 20220330160751 CopyBadgesUploadsToUploadReferences: migrated (0.0010s) =====

== 20220330160754 CopyGroupsUploadsToUploadReferences: migrating ==============
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT groups.flair_upload_id, 'Group', groups.id, uploads.created_at, uploads.updated_at\nFROM groups\nJOIN uploads ON uploads.id = groups.flair_upload_id\nWHERE groups.flair_upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0050s
== 20220330160754 CopyGroupsUploadsToUploadReferences: migrated (0.0055s) =====

== 20220330160757 CopyUserExportsUploadsToUploadReferences: migrating =========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT user_exports.upload_id, 'UserExport', user_exports.id, uploads.created_at, uploads.updated_at\nFROM user_exports\nJOIN uploads ON uploads.id = user_exports.upload_id\nON CONFLICT DO NOTHING\n")
   -> 0.0013s
== 20220330160757 CopyUserExportsUploadsToUploadReferences: migrated (0.0041s) 

== 20220330164740 CopyThemeFieldsUploadsToUploadReferences: migrating =========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT theme_fields.upload_id, 'ThemeField', theme_fields.id, uploads.created_at, uploads.updated_at\nFROM theme_fields\nJOIN uploads ON uploads.id = theme_fields.upload_id\nWHERE type_id = 2\nON CONFLICT DO NOTHING\n")
   -> 0.0006s
== 20220330164740 CopyThemeFieldsUploadsToUploadReferences: migrated (0.0010s) 

== 20220404195635 CopyCategoriesUploadsToUploadReferences: migrating ==========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT categories.uploaded_logo_id, 'Category', categories.id, uploads.created_at, uploads.updated_at\nFROM categories\nJOIN uploads ON uploads.id = categories.uploaded_logo_id\nWHERE categories.uploaded_logo_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0095s
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT categories.uploaded_background_id, 'Category', categories.id, uploads.created_at, uploads.updated_at\nFROM categories\nJOIN uploads ON uploads.id = categories.uploaded_background_id\nWHERE categories.uploaded_background_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0004s
== 20220404195635 CopyCategoriesUploadsToUploadReferences: migrated (0.0103s) =

== 20220404201949 CopyCustomEmojisUploadsToUploadReferences: migrating ========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT custom_emojis.upload_id, 'CustomEmoji', custom_emojis.id, uploads.created_at, uploads.updated_at\nFROM custom_emojis\nJOIN uploads ON uploads.id = custom_emojis.upload_id\nWHERE custom_emojis.upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0032s
== 20220404201949 CopyCustomEmojisUploadsToUploadReferences: migrated (0.0036s) 

== 20220404203356 CopyUserProfilesUploadsToUploadReferences: migrating ========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT user_profiles.profile_background_upload_id, 'UserProfile', user_profiles.user_id, uploads.created_at, uploads.updated_at\nFROM user_profiles\nJOIN uploads ON uploads.id = user_profiles.profile_background_upload_id\nWHERE user_profiles.profile_background_upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0017s
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT user_profiles.card_background_upload_id, 'UserProfile', user_profiles.user_id, uploads.created_at, uploads.updated_at\nFROM user_profiles\nJOIN uploads ON uploads.id = user_profiles.card_background_upload_id\nWHERE user_profiles.card_background_upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0011s
== 20220404203356 CopyUserProfilesUploadsToUploadReferences: migrated (0.0033s) 

== 20220404204439 CopyUserAvatarsUploadsToUploadReferences: migrating =========
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT user_avatars.custom_upload_id, 'UserAvatar', user_avatars.id, uploads.created_at, uploads.updated_at\nFROM user_avatars\nJOIN uploads ON uploads.id = user_avatars.custom_upload_id\nWHERE user_avatars.custom_upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0200s
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT user_avatars.gravatar_upload_id, 'UserAvatar', user_avatars.id, uploads.created_at, uploads.updated_at\nFROM user_avatars\nJOIN uploads ON uploads.id = user_avatars.gravatar_upload_id\nWHERE user_avatars.gravatar_upload_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0069s
== 20220404204439 CopyUserAvatarsUploadsToUploadReferences: migrated (0.0276s) 

== 20220404212716 CopyThemeSettingsUploadsToUploadReferences: migrating =======
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT theme_settings.value::int, 'ThemeSetting', theme_settings.id, uploads.created_at, uploads.updated_at\nFROM theme_settings\nJOIN uploads ON uploads.id = theme_settings.value::int\nWHERE data_type = 6 AND theme_settings.value IS NOT NULL AND theme_settings.value != ''\nON CONFLICT DO NOTHING\n")
   -> 0.0025s
== 20220404212716 CopyThemeSettingsUploadsToUploadReferences: migrated (0.0030s) 

== 20220526203356 CopyUserUploadsToUploadReferences: migrating ================
-- execute("INSERT INTO upload_references(upload_id, target_type, target_id, created_at, updated_at)\nSELECT users.uploaded_avatar_id, 'User', users.id, uploads.created_at, uploads.updated_at\nFROM users\nJOIN uploads ON uploads.id = users.uploaded_avatar_id\nWHERE users.uploaded_avatar_id IS NOT NULL\nON CONFLICT DO NOTHING\n")
   -> 0.0227s
== 20220526203356 CopyUserUploadsToUploadReferences: migrated (0.0234s) =======


[2022-06-09 09:02:11] Reconnecting to the database...
[2022-06-09 09:02:12] Reloading site settings...
[2022-06-09 09:02:12] Disabling outgoing emails for non-staff users...
[2022-06-09 09:02:14] Disabling readonly mode...
[2022-06-09 09:02:14] Clearing category cache...
[2022-06-09 09:02:14] Reloading translations...
[2022-06-09 09:02:14] Remapping uploads...
[2022-06-09 09:02:14] Restoring uploads, this may take a while...
[2022-06-09 09:03:05] EXCEPTION: 509 of 1823 uploads are not migrated to S3. S3 migration failed for db 'default'.
[2022-06-09 09:03:05] /var/www/discourse/lib/file_store/to_s3_migration.rb:132:in `raise_or_log'
/var/www/discourse/lib/file_store/to_s3_migration.rb:79:in `migration_successful?'
/var/www/discourse/lib/file_store/to_s3_migration.rb:373:in `migrate_to_s3'
/var/www/discourse/lib/file_store/to_s3_migration.rb:66:in `migrate'
/var/www/discourse/lib/file_store/s3_store.rb:328:in `copy_from'
/var/www/discourse/lib/backup_restore/uploads_restorer.rb:62:in `restore_uploads'
/var/www/discourse/lib/backup_restore/uploads_restorer.rb:44:in `restore'
/var/www/discourse/lib/backup_restore/restorer.rb:61:in `run'
/var/www/discourse/script/spawn_backup_restore.rb:23:in `restore'
/var/www/discourse/script/spawn_backup_restore.rb:36:in `block in <main>'
/var/www/discourse/script/spawn_backup_restore.rb:4:in `fork'
/var/www/discourse/script/spawn_backup_restore.rb:4:in `<main>'

問題の原因と、Amazon S3 のサーバーバックアップからサーバーを復元する方法について、ご助言いただけないでしょうか。

こんにちは。
app.yml から新しいサーバーを再作成した後、https://your.domain/admin/backups セクションでバックアップにアクセスできましたか?

いいえ、app.yml から再作成した後、何も入っていないクリーンな新しいセットアップが提供されただけでした。S3 から最後のバックアップをダウンロードし、ローカルの Discourse に手動でアップロードして、復元を実行しました。

復元を開始すると、すべての設定(S3、設定ページからのすべての認証情報を含む)が戻り、すべてのカテゴリが表示され、投稿が表示され、すべてが表示されます。その後、数分後にログアウトメッセージが表示され、すべてのカテゴリとトピックが消え、ログにそのエラーが表示されます(ロールバックするようです)。

:thinking: S3の設定はapp.yml(こちらで説明されているように Configure an S3 compatible object storage provider for uploads )にはなく、Set up file and image uploads to S3 のように設定されているということですか?

いいえ、アプリのymlには表示されません。

すべてのS3設定は、管理者 → 設定ページで定義されており、サーバーが昨夜ダウンして復元する必要があるまで、1年間正常に動作していました。

はい、S3のバックアップとアップロードを設定するために使用したのはこれです。

アプリの app.yml を設定で編集してから、管理セクションにバックアップが表示され、そこから復元できるはずです。手動でのインポートやアップロードなしで、それらはバックアップに含まれているはずですが、なぜ失敗しているのかはわかりません。

バックアップにS3とローカルアップロードが混在していることが原因である可能性があります。これは私の専門分野ではないのですが、このトピックで、失敗を回避するための議論と回避策があります。ただし、これははるかに少ないエラー数に対するものでしたので、それを考慮に入れる必要があるかもしれません。

残念ながら、私のサイトはクラッシュしてしまいました。そのため、S3へのアップロードとバックアップしか残っていません。残りのローカルファイルをS3に移行する方法はないと仮定しています。

そこで、現在の選択肢についてお伺いします。S3のバックアップから復元し、ローカルファイルを無視する方法はありますか?S3へのアップロードを無視する方法を見つけましたが、その場合、ほとんどすべての投稿でリンク/画像が壊れています(S3へのアップロードをセットアップしてから何年も経っているため、90%以上がS3にあると思われます)。

同じ問題で苦労している方のためにアップデート情報をお伝えします(基本的に、バックアップからの復元ができず、システムアップグレードの不具合でサーバーがクラッシュしました)。

問題の根本原因は、ローカルアップロードとS3アップロードの両方があるため、復元ツールがローカルとS3の復元を同時に処理する方法がわからずバグが発生しているようです(Discourseはバックアップ/復元を見直す時期かもしれません)。

@RGJさんからのヒントに感謝します。彼は、復元中にDiscourseにS3アップロードを無視させることを提案しました。

  1. app.ymlDISCOURSE_ENABLE_S3_UPLOADS=false という行を追加します。
  2. Discourseを再構築します ./launcher rebuild app
  3. 復元を試みます(GUIのバックアップページから、またはCLIを使用)。
  4. 復元後、app.ymlからその行を削除し、もう一度再構築します。

これは機能しましたが、注意点として、フォーラムはひどく壊れていました。カテゴリ、設定、投稿は復元されましたが、すべての画像、リンク、埋め込みドキュメントなどは壊れてエラーになりました。

最後の手段:
古いサーバーを救出し、/var/discourseディレクトリを(tar/gzで)抽出し、新しいサーバーにコピーして./launcher rebuild appを実行しました。これによりフォーラムの運用は完全に復元されましたが、根本的な問題は残っています。ローカルとS3のアップロードが混在しているため、バックアップは機能しません。

この問題を根本的に解決するための最善の方法について、ぜひアドバイスをいただきたいです。すべてのアップロードをローカルからS3へ、またはS3からローカルへ移動する方が良い/簡単でしょうか?また、その方法は?バックアップの目的はこのような状況を助けることですが、今回は役に立たなかったので、きちんと修正する必要があります。

Object Storage for Uploads (S3 & Clones) の使用 に記載されているように設定すると、

 rake uploads:migrate_to_s3

を実行できるようになります。s3 の使用を停止したい場合は、rails コンソールに入り、

  SiteSetting.include_s3_uploads_in_backups=true

を設定します。その後、バックアップを取得し、app.yml で s3 が設定されていないことを確認して、バックアップを復元します。これにより、バックアップがローカルに復元されるはずです。

ただし、どちらの場合も、app.yml ファイルの ENV 変数にキーとバックアップバケットを設定し、新しいサイトに復元できることを確認することを推奨します。

少し混乱しているようです。

理想的なのは、すべてのアップロードをローカルで行い、バックアップ(zip)をS3に保存することだと思います。これにより、サーバーに何かあった場合にバックアップがS3で利用可能になりますが、バックアップ自体は依存関係のない自己完結型なので、新しいサーバーに簡単に復元できるはずです。

もし私の理解が正しければ、これらの指示に従うべきです。

S3の使用を停止したい場合は、railsコンソールに入り、以下のように設定します。

  SiteSetting.include_s3_uploads_in_backups=true
その後バックアップを取り、app.ymlにS3が設定されていないことを確認し、バックアップを復元します。これにより、バックアップがローカルに復元されると思います。

そして

  1. 管理 -\u003e 設定 -\u003e ファイル で S3へのアップロードを有効にする オプションを無効にする
  2. 管理 -\u003e 設定 -\u003e バックアップ ページでS3へのバックアップを有効にする

これで正しいですか?

ここが混乱した点です。なぜapp.ymlファイルにS3の設定を入れたいのですか?

これにより、バックアップを復元する前にコマンドラインからバックアップにアクセスできます。そうしないと、管理者アカウントを設定してから S3 を設定し、その後復元する必要があります。同様に、データベースに設定した設定は、データベースを復元すると上書きされます。

ベストプラクティスは、app.yml ファイルの ENV 変数のみを使用して S3 を設定することだと思います。数百人が表示されなくなったことに驚くことを除けば、それらを非表示設定にすることが理にかなっているでしょう。

そうしないと、復元が困難になるためです。

コマンドラインからS3のバックアップを復元するにはどうすればよいですか? こちらの指示によると:Restore a backup from the command line
バックアップファイルを /var/discourse/shared/standalone/backups/default フォルダにドロップし、その後CLIから復元を開始できると記載されています。これは以前、あなたの提案で行ったこと(残念ながらリンク切れの原因となった)ですが、それは機能します。

CLIから直接S3を復元するにはどうすればよいですか?

cd /var/discourse
./launcher enter app
discourse restore

利用可能なバックアップが表示されるので、復元したいものをコピー/ペーストしてください。

ありがとうございます。S3バックアップを読み込み、オプションとして一覧表示するということですね。

Jayさん、アセットをローカルに移動するというご提案について、フォローアップです。

非表示設定 include_s3_uploads_in_backups を true に設定し、S3をオフにしてからバックアップと復元を行うことで、S3の使用を停止できると思います。

S3バックアップを app.yml で設定すると、コマンドラインで復元する際に app.yml ファイルのみで可能になります(DiscourseをクローンしてDockerをインストールした後)。

最初のステップとして、S3バケットをバックアップする必要がありますか、それともこれはバケットに安全な操作ですか?

まあ、少なくとも昨夜サーバーがクラッシュした理由(そして完全な再構築後にも再びクラッシュした理由です:frowning:、詳細は次のトピックを参照してください: https://meta.discourse.org/t/ubuntu-20-04-kernel-update-with-docker-causing-a-crash/229526)がわかりました。

バックアップから起動するには、以下の手順を実行する必要がありました。

  1. 設定 → ファイルS3アップロードを有効にする を無効にする
  2. 設定 → バックアップバックアップ場所S3
  3. 設定 → バックアップアップロード付きバックアップ を有効にする

その後、バックアップを取得し、正常に復元することができました。しかし、一つ問題が発生しました。すべての添付ファイル(ファイル)が無効なリンクになりました。画像はすべて正常ですが、添付ファイルのリンク(例:https://domain.com/uploads/short-url/phu1HOLvkE8LWpkKYfnMPSWsvHh.zip)は現在エラーが発生します。

おっと!そのページは存在しないか、プライベートです。

これらの短縮URLリンクを修正する方法はありますか?

それらのトピックのいずれかでHTML再構築(別名リベイク)を試して、それが修正されるかどうかを確認してみてください。

ありがとうございます。特定のトピックをベイクするコマンドの発行方法に関するガイドはどこかにありますか?