./launcher rebuild app 명령으로 앱을 재빌드한 후 웹사이트를 방문하면 아무것도 로드되지 않고 로더(로딩 화면)만 표시됩니다. 네트워크 탭을 검사해 보니 미리 컴파일된 정적 자산(주로 .js 파일)을 가져올 수 없었으며, 이는 아마도 해당 파일들이 제 S3 버킷에 없기 때문이라고 추정됩니다. 여기에서 확인해 보실 수 있습니다: forum.hobiguru.com.
마이그레이레이션 rake 태스크도 실행해 보았지만 해결되지 않았습니다:
root@ubuntu-s-1vcpu-1gb-fra1-01-app:/var/www/discourse# rake uploads:migrate_to_s3 --trace
** Invoke uploads:migrate_to_s3 (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute uploads:migrate_to_s3
Please note that migrating to S3 is currently not reversible!
[CTRL+c] to cancel, [ENTER] to continue
Migrating uploads to S3 for 'default'...
Some uploads were not migrated to the new scheme. Running the migration, this may take a while...
rake aborted!
FileStore::ToS3MigrationError: Some uploads could not be migrated to the new scheme. You need to fix this manually. (FileStore::ToS3MigrationError)
/var/www/discourse/lib/file_store/to_s3_migration.rb:156:in `migrate_to_s3'
/var/www/discourse/lib/file_store/to_s3_migration.rb:59:in `migrate'
/var/www/discourse/lib/tasks/uploads.rake:126:in `migrate_to_s3'
/var/www/discourse/lib/tasks/uploads.rake:106:in `block in migrate_to_s3_all_sites'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management/null_instance.rb:36:in `each_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management.rb:21:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:104:in `migrate_to_s3_all_sites'
/var/www/discourse/lib/tasks/uploads.rake:100:in `block in <main>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:281:in `block in execute'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:281:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:281:in `execute'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:199:in `synchronize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:199:in `invoke_with_call_chain'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/task.rb:188:in `invoke'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:188:in `invoke_task'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:138:in `block (2 levels) in top_level'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:138:in `each'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:138:in `block in top_level'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:147:in `run_with_threads'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:132:in `top_level'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:83:in `block in run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:214:in `standard_exception_handling'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rake-13.2.1/lib/rake/application.rb:80:in `run'
bin/rake:13:in `<top (required)>'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/cli/exec.rb:58:in `load'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/cli/exec.rb:58:in `kernel_load'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/cli/exec.rb:23:in `run'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/cli.rb:455:in `exec'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/vendor/thor/lib/thor/command.rb:28:in `run'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/vendor/thor/lib/thor.rb:527:in `dispatch'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/cli.rb:35:in `dispatch'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/vendor/thor/lib/thor/base.rb:584:in `start'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/cli.rb:29:in `start'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/exe/bundle:28:in `block in <top (required)>'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/lib/bundler/friendly_errors.rb:117:in `with_friendly_errors'
/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.5.18/exe/bundle:20:in `<top (required)>'
/usr/local/bin/bundle:25:in `load'
제 원래 의도는 사용자 업로드 파일만 S3에 저장하고, 이를 처리하기 위해 CDN을 추가하는 것이었습니다. 하지만 이제 새로운 문제에 직면했는데, 의도하지 않았음에도 앱의 정적 자산이 CDN을 통해 서빙되고 있습니다.
앱이 시작될 때 모든 정적 자산을 S3 버킷에 업로드하고 이를 CDN을 통해 서빙하는 방법이 있거나, 사용자 업로드 파일만 CDN을 통해 서빙하는 방법이 있을까요? 아니면 이 문제를 해결하는 더 나은 방법이 있을까요?
그럼에도 불구하고 사용자 업로드가 여전히 올바르게 작동하지 않습니다. S3 버킷으로의 업로드는 (정상적으로) 이루어지지만, 이미지는 다음과 같이 서빙됩니다:
//my-bucket-eu.my-bucket-eu/original/1X/7f242572bdb45b65ded727c13366fe490541358f.jpeg
이는 물론 유효한 S3 또는 CDN 경로가 아닙니다.
참조하신 가이드에는 다음과 같이 관련이 있어 보이는 섹션이 있습니다:
DISCOURSE_CDN_URL은 Discourse 호스트 이름으로 지시하며 요청을 캐시하는 CDN입니다. 주로 CSS 및 기타 테마 자산과 같이 가져올 수 있는(pullable) 자산에 사용됩니다.
DISCOURSE_S3_CDN_URL은 오브젝트 스토리지 버킷으로 지시하며 요청을 캐시하는 CDN입니다. 주로 JS, 이미지 및 사용자 업로드와 같이 올릴 수 있는(pushable) 자산에 사용됩니다.
두 값이 서로 달라야 하며 관리자가 둘 다 설정하는 것을 권장합니다.
그러나 DISCOURSE_CDN_URL에 무엇을 설정해야 하는지 확실하지 않습니다. DISCOURSE_S3_CDN_URL: https://dsxxxxx2qn.cloudfront.net과 동일한 값을 설정해야 하나요, 아니면 별도의 CDN 인스턴스를 생성해야 하나요?
Jake님, 말씀하신 내용을 정확히 이해하지 못한 것 같습니다. 조금 더 자세히 설명해 주시겠어요?
혹시 CDN의 오리진으로 S3 버킷을 설정하는 대신, 제 포럼 도메인인 forum.hobiguru.com을 오리진으로 설정하라는 말씀이신가요? 만약 그렇다면, 아무런 변화가 없을 것 같습니다. 왜냐하면 URL이 존재하지 않는 곳으로 연결되는 문제를 일으키는 URL을 생성하는 쪽이 바로 discourse 포럼이거든요. 예를 들어 //my-bucket-eu.my-bucket-eu/original/1X/7f242572bdb45b65ded727c13366fe490541358f.jpeg 같은 URL 말입니다.
그 후로 DISCOURSE_CDN_URL: https://dsuxxxhrz2qn.cloudfront.net도 추가해 보았고, 지금은 앱이 재빌드되기를 기다리고 있습니다
app.yml 파일에만 서브도메인을 지정할 수 없습니다. 적절한 DNS 정보가 없다면 아무도 이를 사용할 수 없습니다. 또한 파일을 AWS에서 서빙하기 때문에, CDN을 사용하려면 해당 쪽 설정도 함께 해야 합니다.
하지만 다시 한번 말씀드리자면, 사용자가 디스코스의 URL을 보고 싶어 할 때만 그 URL을 볼 수 있습니다. 이는 관리자가 무언가를 수행하려는 경우가 아닌 이상 매우 드문 일입니다. 그리고 사용자는 미디어나 기타 정적 파일의 URL을 사실상 거의 보지 않습니다.
따라서 제 관점에서, CDN을 사용하지 않으려는 경우라면 시간을 허비하는 셈입니다. 전 세계적으로 사용자층이 넓고, 그중 일부가 상태가 좋지 않은 국가 수준의 네트워크 연결에 있는 경우라면 이것이 현명한 선택일 수 있습니다.
저는 핀란드에 살고 있습니다. 호주에 있는 사이트가 저로부터 1000km 떨어진 서버를 통해 CDN을 사용한다고 해서 저에게 실질적인 이점이 되지 않습니다. 진정한 병목 현상은 사이트가 어떻게 구축되었는지에 따라 발생하며, 예를 들어 자원이 충분하지 않은 상태에서 불필요한 PHP 호출을 많이 사용하는 경우와 같습니다.
하지만 기본적으로 DNS에 cdn을 적어도 포함시키지 않고는 cdn.example.tld와 같은 URL을 사용할 수 없습니다.
문제는 이 경로들이 제 S3 버킷에 존재하지 않는다는 것입니다. 사전 컴파일된 자산은 S3의 /assets/* 폴더 아래에 업로드됩니다(예: /assets/locales, /assets/plugins, /assets/scripts), 하지만 /stylesheets/ 폴더는 없으며, 당연히 해당 URL을 로드하면 403 Forbidden 오류가 발생합니다.
반면, DISCOURSE_CDN_URL: https://forum.hobiguru.com으로 변경하면 포럼은 정상적으로 작동하지만, 자산은 이제 CDN(예: https://forum.hobiguru.com/stylesheets/admin_308d905aa5c03567866fec50e9a28d8721ab0463.css?__ws=forum.hobiguru.com)이 아니라 서버(예: https://forum.hobiguru.com/)에서 제공됩니다.
제 현재 설정(참고용)은 다음과 같습니다:
app.yaml
DISCOURSE_USE_S3: true
DISCOURSE_S3_REGION: eu-central-1
DISCOURSE_S3_ACCESS_KEY_ID: AKIA......LQMB
DISCOURSE_S3_SECRET_ACCESS_KEY: PaXQu7pKN.....fJNY
DISCOURSE_S3_CDN_URL: https://dsuqioxhrz2qn.cloudfront.net # CDN URL이 CloudFront를 가리키도록 확인
DISCOURSE_CDN_URL: https://forum.hobigur.com # 이 부분을 주의하세요!
DISCOURSE_S3_BUCKET: hobiguru-s3-bucket-eu