S3 마이그레이션 실패로 복원 실패

자체 호스팅된 Discourse 데이터베이스를 사용 중이며, 새 서버로 마이그레이션을 시도하고 있습니다.

양쪽(구 서버 및 신 서버) 모두 최신 Discourse로 업데이트되어 있으며(양쪽 모두 몇 분 간격으로 앱 재빌드가 수행됨), 며칠 전 구 서버에서 새 Postgresql로의 마이그레이션이 성공적으로 완료되었습니다. 따라서 이 부분들은 문제의 원인으로 배제할 수 있다고 봅니다.

모든 업로드 파일은 S3 호환 객체 스토리지(Digitalocean Spaces)에 저장되어 있으므로, 이론적으로는 마이그레이션이 쉬워야 합니다. “게시물 및 카테고리” 부분의 복원이 잘 작동한다면, S3 업로드 링크도 당연히 정상적으로 작동할 거라고 생각했습니다.

하지만 그렇지 않았습니다. 새 서버에서 백업 복원을 수행할 때마다 게시물 부분은 100% 정상적으로 진행되지만, S3 부분에서 실패합니다.

확인되는 에러는 다음과 같습니다.

Reconnecting to the database...
Reloading site settings...
Disabling outgoing emails for non-staff users...
Running seed fu...
Disabling readonly mode...
Clearing category cache...
Reloading translations...
Remapping uploads...
Restoring uploads, this may take a while...
Migrating uploads to S3 for 'default'...
Uploading files to S3...
 - Listing local files
 => 3 files
 - Listing S3 files
..................................................................................................................................................... => 148892 files
 - Syncing files to S3
...
Updating the URLs in the database...
Removing old optimized images...
Flagging all posts containing lightboxes for rebake...
29368 posts were flagged for a rebake
EXCEPTION: rake posts:missing_uploads identified 2274 issues. S3 migration failed for db 'default'.
/var/www/discourse/lib/file_store/to_s3_migration.rb:132:in 'FileStore::ToS3Migration#raise_or_log'
/var/www/discourse/lib/file_store/to_s3_migration.rb:104:in 'FileStore::ToS3Migration#migration_successful?'
/var/www/discourse/lib/file_store/to_s3_migration.rb:384:in 'FileStore::ToS3Migration#migrate_to_s3'
/var/www/discourse/lib/file_store/to_s3_migration.rb:59:in 'FileStore::ToS3Migration#migrate'
/var/www/discourse/lib/file_store/s3_store.rb:372:in 'FileStore::S3Store#copy_from'
/var/www/discourse/lib/backup_restore/uploads_restorer.rb:69:in 'BackupRestore::UploadsRestorer#restore_uploads'
/var/www/discourse/lib/backup_restore/uploads_restorer.rb:49:in 'BackupRestore::UploadsRestorer#restore'
/var/www/discourse/lib/backup_restore/restorer.rb:179:in 'BackupRestore::Restorer#restore_uploads'
/var/www/discourse/lib/backup_restore/restorer.rb:72:in 'BackupRestore::Restorer#run'
script/discourse:242:in 'DiscourseCLI#restore'
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/thor-1.5.0/lib/thor/command.rb:28:in 'Thor::Command#run'
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/thor-1.5.0/lib/thor/invocation.rb:127:in 'Thor::Invocation#invoke_command'
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/thor-1.5.0/lib/thor.rb:538:in 'Thor.dispatch'
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/thor-1.5.0/lib/thor/base.rb:585:in 'Thor::Base::ClassMethods#start'
script/discourse:396:in '<top (required)>'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/cli/exec.rb:61:in 'Kernel.load'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/cli/exec.rb:61:in 'Bundler::CLI::Exec#kernel_load'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/cli/exec.rb:24:in 'Bundler::CLI::Exec#run'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/cli.rb:504:in 'Bundler::CLI#exec'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/vendor/thor/lib/thor.rb:538:in 'Bundler::Thor.dispatch'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/cli.rb:29:in 'Bundler::CLI.start'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/exe/bundle:28:in 'block in <top (required)>'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/lib/bundler/friendly_errors.rb:118:in 'Bundler.with_friendly_errors'
/usr/local/lib/ruby/gems/3.4.0/gems/bundler-4.0.11/exe/bundle:20:in '<top (required)>'
/usr/local/bin/bundle:25:in 'Kernel#load'
/usr/local/bin/bundle:25:in '<main>'
Trying to rollback...
Rolling back...
Cleaning stuff up...
Dropping functions from the discourse_functions schema...
Removing tmp '/var/www/discourse/tmp/restores/default/2026-08-11-075958' directory...
Unpausing sidekiq...
Marking restore as finished...
Notifying 'system' of the end of the restore...
Finished!
[FAILED]
Restore done.

몇 가지 질문이 있습니다:

  • 데이터베이스에 저장된 S3 환경 설정은 변경되지 않았는데, 왜 실패하는 것일까요?
  • 업로드에서 발견된 2274개의 문제를 관리할 수 있도록 목록을 생성하는 대신, 왜 크래시(에러 발생)를 일으키는 것일까요?
  • 왜 자동으로 빈 데이터베이스로 롤백하여, 아무것도 없는 상태로 남겨두는 것일까요?
  • 복원이 완료되도록 하는(비록 문자 그대로의 성공은 아니더라도) 우회 방법이 있거나, 최소한 롤백을 방지하여 업로드에서 발견된 문제를 직접 처리할 수 있게 하는 방법이 있을까요?

도움 주시면 감사하겠습니다!

백업과 함께 포함된 플래그를 단순히 카운트하고 있기 때문입니다. 이는 실제로 문제를 찾는 프로세스가 아닙니다.

이상적으로는 먼저 기존 서버에서 누락된 업로드 플래그를 정리한 후, 새로 백업을 받아야 합니다.

기존 서버에서 누락된 업로드를 나열하고 수리를 시도하는 작업을 실행할 수 있습니다(시간이 걸릴 수 있음).

cd /var/discourse
./launcher enter app
VERBOSE=1 rake posts:missing_uploads

계속 진행하려면 남은 항목을 무시(ignored)로 표시할 수도 있습니다.

GIVE_UP=1 VERBOSE=1 rake posts:missing_uploads

이 시점에 새로운 백업을 받아 복원할 수 있습니다.

이 답변에 진심으로 감사드립니다! 실제로 큰 도움이 되네요. 적어도 업로드 문제가 있는 모든 주제의 전체 목록을 확보할 수 있었으니까요.

가능하면 최대한 많이 수정해 보겠지만, 적어도 이제 앞으로 나아갈 방향이 생겼습니다!