S3 버킷 설정이 되지 않습니다

안녕하세요,

S3 업로드를 설정하고 있는데, 업로드 자체는 정상적으로 작동하는 것 같지만 GET 요청이 이상한 이유로 실패하고 있습니다. Discourse가 업로드된 객체의 URI에 ".cn"을 추가하는 것 같습니다.

S3 버킷에 이미지를 업로드한 후 공개 URL은 다음과 같습니다:

https://hobig...bucket-eu.s3.eu-central-1.amazonaws.com/original/1X/5e894113...48918.jpeg

하지만 브라우저에서 검사해보니 제 Discourse 포럼이 로드하려고 하는 URL에는 .cn이 붙어 있습니다:

https://hobig...bucket-eu.s3.eu-central-1.amazonaws.com.cn/original/1X/5e894113...48918.jpeg

이것이 왜 일어나는 건지 아시는 분 계신가요?

관리자 설정은 다음과 같습니다:

S3 호환 객체 저장소 제공자 설정을 통한 업로드 구성 및/또는 https://meta.discourse.org/t/set-up-file-and-image-uploads-to-s3/7229의 지침을 따르고, 데이터베이스/UX가 아닌 yml 파일에 해당 설정을 입력하세요.

AWS를 위한 엔드포인트 구성은 필요하지 않습니다. 대신 CDN을 구성하는 것이 좋습니다.

안녕하세요 @pfaffman,

같은 가이드를 두 번 붙여넣으신 것 같습니다.

“yml 파일의 설정”이라고 언급하셨는데, 어떤 YAML 파일을 말씀하시는 건지 구체적으로 알려 주실 수 있을까요? /var/discourse/containers 폴더에 있는 app.yml 파일을 말씀하시는 건가요?

또한, S3/CloudFront 통합을 YAML 파일로 직접 구성할 경우, 그 설정이 Discourse 관리자 섹션에서 설정된 내용을 덮어쓰게 될까요?

감사합니다!

죄송합니다. 그중 하나는 Set up file and image uploads to S3 이어야 했습니다.

S3를 설정하는 방법을 설명하는 링크된 주제들을 확인해 보시되, 네, 수정해야 하는 파일은 app.yml입니다. (web_only.yml에 대해서는 언급이 없었으므로)

네. YML 파일에 입력하면 UX(사용자 인터페이스)에서 해당 설정이 숨겨집니다.

Hi @pfaffman,

저는 S3 버킷, CloudFront 설정, 그리고 CloudFront의 오리진(Orgin)을 제 S3 버킷으로 설정해 두었습니다. 현재 제 app.yml 설정은 다음과 같습니다:

ENV:
  DISCOURSE_USE_S3: true
  DISCOURSE_S3_REGION: eu-central-1
  DISCOURSE_S3_ACCESS_KEY_ID: AKIAWPLPUxxxxxx
  DISCOURSE_S3_SECRET_ACCESS_KEY: PaXQu7pKxxxx
  DISCOURSE_S3_CDN_URL: https://dsuxxxhrz2qn.cloudfront.net
  DISCOURSE_S3_BUCKET: hobigxxxxbucket-eu

./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을 통해 서빙되고 있습니다. :confused:

앱이 시작될 때 모든 정적 자산을 S3 버킷에 업로드하고 이를 CDN을 통해 서빙하는 방법이 있거나, 사용자 업로드 파일만 CDN을 통해 서빙하는 방법이 있을까요? 아니면 이 문제를 해결하는 더 나은 방법이 있을까요?

아직 뻔한 것을 놓치고 있는 건지 모르겠습니다. 잘 모르겠네요.
도와주셔서 감사합니다!

설명을 제대로 따르지 않아서 그런 것 같습니다.

네, 이해합니다. 가능은 하지만 그렇게 동작하게 만들 수는 없었습니다. 이것이 권장되는 방식이며, 잘 문서화되어 있고 수백 명의 사람이 이 방식으로 하고 있습니다.

죄송합니다, 말씀하신 부분이 정확히 빠져 있었네요. app.yml에 아래 내용을 추가했고, 이제 포럼이 올바르게 로드됩니다:

after_assets_precompile:
    - exec:
        cd: $home
        cmd:
          - sudo -E -u discourse bundle exec rake s3:upload_assets
          - sudo -E -u discourse bundle exec rake s3:expire_missing_assets

그럼에도 불구하고 사용자 업로드가 여전히 올바르게 작동하지 않습니다. 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 인스턴스를 생성해야 하나요?

아니면 완전히 다른 문제일 수도 있겠네요 :confused:

도움 주셔서 정말 감사합니다!

AWS 측에서 CDN을 구성하고 원하는 도메인을 사용해야 합니다. 또한 DNS에서도 설정해야 합니다.

그런데 왜 그런 걸까요? 실제로 그 URL을 보는 사람은 거의 없으니까요.

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도 추가해 보았고, 지금은 앱이 재빌드되기를 기다리고 있습니다 :crossed_fingers:

업데이트: 아니요, 이것도 작동하지 않았습니다 :confused:

도움을 정말 감사히 생각합니다.
감사합니다

app.yml 파일에만 서브도메인을 지정할 수 없습니다. 적절한 DNS 정보가 없다면 아무도 이를 사용할 수 없습니다. 또한 파일을 AWS에서 서빙하기 때문에, CDN을 사용하려면 해당 쪽 설정도 함께 해야 합니다.

하지만 다시 한번 말씀드리자면, 사용자가 디스코스의 URL을 보고 싶어 할 때만 그 URL을 볼 수 있습니다. 이는 관리자가 무언가를 수행하려는 경우가 아닌 이상 매우 드문 일입니다. 그리고 사용자는 미디어나 기타 정적 파일의 URL을 사실상 거의 보지 않습니다.

따라서 제 관점에서, CDN을 사용하지 않으려는 경우라면 시간을 허비하는 셈입니다. 전 세계적으로 사용자층이 넓고, 그중 일부가 상태가 좋지 않은 국가 수준의 네트워크 연결에 있는 경우라면 이것이 현명한 선택일 수 있습니다.

저는 핀란드에 살고 있습니다. 호주에 있는 사이트가 저로부터 1000km 떨어진 서버를 통해 CDN을 사용한다고 해서 저에게 실질적인 이점이 되지 않습니다. 진정한 병목 현상은 사이트가 어떻게 구축되었는지에 따라 발생하며, 예를 들어 자원이 충분하지 않은 상태에서 불필요한 PHP 호출을 많이 사용하는 경우와 같습니다.

하지만 기본적으로 DNS에 cdn을 적어도 포함시키지 않고는 cdn.example.tld와 같은 URL을 사용할 수 없습니다.

안녕하세요 @Jagster

사실 CDN을 전혀 사용하고 싶지 않습니다. 사용자 업로드된 모든 파일을 S3에 저장하고, 거기서 직접 서빙하는 것만 원합니다. 물론 CloudFront가 S3를 직접 사용하는 것보다 훨씬 나은 솔루션이라는 점은 알고 있습니다.

제 문제는 S3를 설정하려 했을 때(구체적으로는 업로드가 아닌 에셋 서빙을 위해) 발생했는데, @pfaffman님이 업로드용 S3 호환 객체 저장소 제공자 설정 가이드를 따르고 CDN을 설정하라고 조언해 주셨습니다.

여러분의 코멘트에 대해 말씀드리자면:

“app.yml에서만 서브도메인 이름을 지정할 수 없습니다. 적절한 DNS 정보 없이는 아무도 사용할 수 없습니다. 그리고 파일을 AWS에서 서빙하기 때문에, CDN을 사용하려면 그쪽(AWS)도 설정해야 합니다.”

죄송하지만 이 단락을 정확히 이해하지 못했습니다. 조금 더 자세히 설명해 주시거나 명확히 해 주실 수 있을까요? S3 버킷에 대해 DNS 레코드를 설정해야 한다는 뜻인지, 아니면 AWS 쪽에서 특정 사항을 조정해야 한다는 뜻인가요?

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

다시 인사드립니다, 여러분,

이전 게시물에 대해 후속으로 말씀드리고자 하며, 일부 진전이 있었다는 점을 공유하고 싶습니다.

현재 정상 작동하는 부분

  • 사용자 업로드는 S3 버킷을 통해 CDN(CloudFront)으로 올바르게 제공되고 있으니, 이 부분은 좋습니다!

그러나 여전히 사전 컴파일된 자산(precompiled assets)과 관련하여 문제가 있습니다

사전 컴파일된 자산은 여전히 CDN에서 올바르게 제공되지 않고 있습니다.

DISCOURSE_CDN_URL을 CloudFront URL(예: https://dsuqioxhrz2qn.cloudfront.net)로 설정하면, 사전 컴파일된 자산의 URL은 다음과 같이 됩니다:

•	https://dsuqioxhrz2qn.cloudfront.net/stylesheets/color_definitions_shades-of-blue_7_1_e6f11758f9c015d1e5ed9b08c437e9c5c267c932.css?__ws=forum.hobiguru.com
•	https://dsuqioxhrz2qn.cloudfront.net/stylesheets/discourse-presence_308d905aa5c03567866fec50e9a28d8721ab0463.css

문제는 이 경로들이 제 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

그리고 사전 컴파일 훅(precompile hooks)은 다음과 같습니다:

hooks:
  after_assets_precompile:
    - exec:
        cd: $home
        cmd:
          - sudo -E -u discourse bundle exec rake s3:upload_assets
          - sudo -E -u discourse bundle exec rake s3:expire_missing_assets

자산이 사전 컴파일된 후 특정 구조로 S3에 업로드되는 것으로 보이지만, CDN을 통해 로드될 때 객체에 대한 경로가 약간 어긋나는 것 같습니다.