초기화되지 않은 TransferManager로 인한 백업 업로드 실패

v2026.8.0-latest.1 +4로 업데이트한 후 내 백업이 실패하기 시작했습니다. 로그는 다음과 같습니다: log.txt.zip (22.1 KB)

특히, 다음 부분이 관련 있어 보입니다:

[2026-08-04 03:37:52] Uploading archive...
[2026-08-04 03:37:52] EXCEPTION: uninitialized constant Aws::S3::TransferManager
[2026-08-04 03:37:52] /var/www/discourse/lib/s3_helper.rb:453:in 'S3Helper#transfer_manager'
/var/www/discourse/lib/s3_helper.rb:332:in 'S3Helper#upload_file'
/var/www/discourse/lib/backup_restore/s3_backup_store.rb:48:in 'BackupRestore::S3BackupStore#upload_file'
/var/www/discourse/lib/backup_restore/creator.rb:437:in 'BackupRestore::Creator#upload_archive'
/var/www/discourse/lib/backup_restore/creator.rb:41:in 'BackupRestore::Creator#run'
/var/www/discourse/lib/backup_restore.rb:13:in 'BackupRestore.backup!'
/var/www/discourse/app/jobs/regular/create_backup.rb:10:in 'Jobs::CreateBackup#execute'
/var/www/discourse/app/jobs/base.rb:313:in 'block (2 levels) in Jobs::Base#perform'

이 문제를 어떻게 해결해야 할지 모르겠습니다.

v2026.9.0-latest +565로 업데이트했지만, 여전히 동일한 문제가 발생합니다.

사용 중인 Discourse 버전에는 Aws::S3::TransferManager를 제공해야 하는 aws-sdk-s3 버전이 포함되어 있습니다.

공식 Discourse Docker 설치 방식을 사용 중인가요? 공식 Docker 설정을 사용 중인 경우, 업그레이드 과정에서 컨테이너가 완전히 다시 빌드되었나요?

네, 공식 Discourse Docker 설치를 사용하고 있는 것 같습니다. 확실하게 확인할 수 있는 방법이 있을까요? 설치를 한 지 꽤 시간이 지났거든요.

업데이트를 위해 ./launcher rebuild app 명령을 실행했습니다. 이 명령은 컨테이너가 완전히 다시 빌드되었음을 의미한다고 생각하지만, 제가 잘못 알고 있는 건 아닌지 궁금합니다.

관련이 있을 수 있는 몇 가지 추가 정보:

  • Ubuntu 24.04.4 LTS를 사용 중입니다.
  • app.yml에서 after_code 훅을 통해 공식 docker_manager 및 discourse-doc-categories 플러그인을 설치합니다. app.yml에는 다른 플러그인이 정의되어 있지 않습니다.
  • 백업 파일을 Backblaze에서 제공하는 S3 호환 스토리지에 저장하려고 하고 있으며, 이전에는 정상적으로 작동했습니다.

이 문제를 좁혀 보는 데 도움이 될 수 있는 한 가지 방법은 실행 중인 컨테이너 내에서 실제로 로드되고 있는 aws-sdk-s3 게임을 확인하는 것입니다.

Aws::S3::TransferManager를 도입한 Discourse의 변경 사항은 aws-sdk-s3 버전을 1.182.0에서 1.227.0으로 올렸습니다:

링크하신 Discourse 리비전에서는 Gemfile.lock이 여전히 aws-sdk-s3 1.227.0을 사용해야 하며, 이 버전은 Aws::S3::TransferManager를 제공합니다.

컨테이너에 진입해 보실 수 있을까요:

cd /var/discourse
./launcher enter app

그리고 컨테이너 내에서 다음을 실행해 주세요:

cd /var/www/discourse
bundle info aws-sdk-s3
bundle exec ruby -e '
require "aws-sdk-s3"
puts "gem: #{Gem.loaded_specs["aws-sdk-s3"]&.full_name}"
puts "path: #{Gem.loaded_specs["aws-sdk-s3"]&.full_gem_path}"
p Aws::S3.autoload?(:TransferManager)
p Aws::S3::TransferManager
'

Rails 환경에서 애플리케이션 자체를 확인하는 것도 유용할 수 있습니다:

RAILS_ENV=production bundle exec rails runner '
puts "aws-sdk-s3: #{Gem.loaded_specs["aws-sdk-s3"]&.version}"
p Aws::S3.autoload?(:TransferManager)
p Aws::S3::TransferManager
'

그리고 다음 명령어로 컨테이너를 종료하세요:

logout

만약 여기서 더 오래된 aws-sdk-s3 버전이 보고되거나 TransferManager가 없다면, 이는 백업 예외를 설명해 줄 것이며 컨테이너에 설치된 게임들이 현재 Discourse Gemfile.lock과 일치하지 않는다는 것을 시사합니다.

1.227.0이 보고되고 Aws::S3::TransferManager가 성공적으로 해결된다면, 문제는 더 드문 경우에 해당하며 Sidekiq 백업 프로세스와 해당 인터랙티브 환경 사이의 차이점을 살펴봐야 한다는 것을 알 수 있습니다.

./launcher rebuild app를 사용 중이시므로, 이는 표준 discourse_docker 워크플로우처럼 보입니다. 위의 명령어들은 재빌드된 컨테이너에 실제로 무엇이 들어갔는지를 더 정확하게 알려줄 것입니다.

aws-sdk-s3의 오래된 버전을 사용하고 있는 것 같습니다.

$ bundle info aws-sdk-s3
  * aws-sdk-s3 (1.177.0)
	Summary: AWS SDK for Ruby - Amazon S3
	Homepage: https://github.com/aws/aws-sdk-ruby
	Source Code: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sdk-s3
	Changelog: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sdk-s3/CHANGELOG.md
	Path: /var/www/discourse/vendor/bundle/ruby/3.4.0/gems/aws-sdk-s3-1.177.0

이 문제를 조사하면서, AWS SDK가 Backblaze와 함께 작동하지 않는 문제를 해결하기 위해 이 임시 해결책을 사용했음을 기억해냈습니다. 해당 주제를 다시 살펴보니, Backblaze가 API를 업데이트하여 임시 해결책이 더 이상 필요 없게 되었습니다.

임시 해결책을 제거한 후 백업이 성공적으로 완료되었습니다! 도움 주셔서 감사합니다. :heart:

이 글에서 한 가지 생각해보는 점은, 이러한 임시 호환성 템플릿은 무기한으로 Discourse의 종속성을 덮어쓰는 것보다, 자체 만료/가드 조건이 있는 것이 더 안전할 수 있다는 것입니다.

예를 들어, 임시 소스 백포트를 위해 프로덕션에서 훅을 사용할 계획인데, 먼저 업스트림 수정 사항이 이미 존재하는지 확인하고, 여전히 필요한 경우에만 패치를 적용할 예정입니다.

이 경우, 다음과 같은 방식이 Discourse 자체가 더 새로운 SDK 기능을 의존하기 시작했을 때 오래된 aws-sdk-s3 1.177.0 고정(pin)이 계속 유지되는 것을 방지할 수 있었을 것입니다:

hooks:
  after_bundle_exec:
    - exec:
        cd: $home
        cmd:
          - |
            if grep -q "Aws::S3::TransferManager" lib/s3_helper.rb; then
              echo "Discourse now requires Aws::S3::TransferManager; skipping obsolete Backblaze aws-sdk-s3 downgrade"
            else
              echo "Applying temporary Backblaze aws-sdk-s3 compatibility pin"
              bundle config set frozen false
              sed -i 's/gem "aws-sdk-s3", require: false/gem "aws-sdk-s3", "1.177.0", require: false/' Gemfile
              bundle update aws-sdk-s3
              bundle add aws-sdk-core --version 3.215
            fi

해당 체크는 단순히 예시를 위한 것입니다. 더 일반적인 버전 체크나, Discourse가 예상 SDK 버전을 넘어갈 때 명시적으로 실패하도록 하는 것이 더 나은 방법이 될 것입니다.

Discourse가 기대하는 버전과 더 이상 일치하지 않는 gems를 가진 컨테이너를 조용히 생성하는 것보다, 크게 실패(fail loudly)하는 것이 훨씬 나을 것입니다.

소음이 크고 명확하게 실패하는 것이 나았을 것이라는 데에는 동의하지만, 이전부터 Discourse가 기대하는 버전과 일치하지 않는 게름을 원했기 때문이었습니다. 새로운 aws-sdk-s3 버전은 Backblaze가 지원하지 않는 헤더를 전송했고, 저는 백업이 작동하기를 원했기 때문입니다.

우회책이 더 이상 필요 없어졌을 때를 알릴 수 있도록 사전에 설정할 수 있는 체크가 있었는지 모르겠습니다. 저는 2025년 4월, 수정이 가능해지기 전에 1년 반 전에 이 우회책을 구현했습니다.

제가 할 수 있는 최선은 만료일을 추가하여 우회책이 여전히 필요한지 확인하도록 강제하는 것이었을 것입니다. 이러한 기능은 좋을 수 있습니다. 아니면, 우회책이 여전히 필요한지 확인하도록 리마인더를 설정하는 등 더 능동적으로 행동할 책임은 저에게 있을 수도 있습니다. 그 점에서 몇 가지 메모가 여기에서 도움이 되었습니다.

오늘 제가 매우 유사한 상황에 직면한 후, 몇 가지 추가 의견을 드립니다.

저는 병합되지 않은 Discourse PR에 대해 app.yml에 임시 소스 백포트를 적용하고 있었습니다. 해당 훅은 다음과 같이 사용되었습니다:

git apply --check /tmp/patch &&
  git apply /tmp/patch

그 후 업스트림에서 해당 영역 중 하나가 리팩토링되어, 기존 패치가 더 이상 깨끗하게 적용되지 않았습니다. git apply --check는 제가 원했던 대로 정확히 작동했습니다. 패치가 부분적으로 적용되지 않았고, 재구축이 조용하게 불일치한 체크아웃을 생성하는 대신 명확하게 실패했습니다.

그러나 중요한 단점이 있었습니다. 이 문제가 ./launcher rebuild app 실행 중에 발생했기 때문에, 재구축 실패로 인해 구식 훅을 제거하고 다시 재구축할 때까지 app 컨테이너가 사용 불가능한 상태가 되었습니다.

따라서 저는 이전 제안을 약간 수정할 것 같습니다. 임시 프로덕션 우회책/백포트의 경우, 가장 안전한 접근 방식은 다음과 같은 것들의 조합인 것 같습니다:

  • 제안하신 대로 명시적인 검토/만료 날짜;
  • 신뢰할 수 있는 경우를 위해 세맨틱/버전 가드;
  • 실용적인 범위 내에서 프로덕션 재구축을 시작하기 이전에 사전 적용 가능성 확인;
  • 그리고 최종 안전망으로 git apply 직전에 여전히 git apply --check를 사용하는 것.

제 경우라면 만료/리마인더가 우회책을 재검토하도록 알려주었을 것이고, 적용 가능성 확인은 오래된 패치가 더 새로운 Discourse 체크아웃을 조용히 수정하는 것을 방지했을 것입니다.

따라서 만료 날짜에 대한 당신의 주장은 이제 저에게 더 합리적으로 느껴집니다. 특히 원래 우회책이 의도적으로 설치를 업스트림과 다르게 만드는 경우, "이 우회책이 여전히 필요한가?"에 대한 완벽한 자동 테스트가 항상 존재하지는 않기 때문입니다.