无法设置 S3 存储桶

您好,

我正在尝试设置 S3 上传,似乎上传工作正常,但 GET 请求因奇怪的原因失败。看起来 discourse 在上传对象的 URI 中添加了“.cn”

在我的 S3 存储桶中上传图片后,公共 URL 是:
https://hobig...bucket-eu.s3.eu-central-1.amazonaws.com/original/1X/5e894113...48918.jpeg

但当我检查浏览器时,我的 discourse 论坛正在尝试加载(请注意 .cn):
https://hobig...bucket-eu.s3.eu-central-1.amazonaws.com.cn/original/1X/5e894113...48918.jpeg

有人知道为什么会这样吗?

这是我的管理员设置:

您应该遵循 为上传配置 S3 兼容对象存储提供商 和/或 Set up file and image uploads to S3 中的说明,并将这些设置放在您的 yml 文件中,而不是放在数据库/用户体验中。

您不需要为 AWS 配置端点。您确实需要配置一个 CDN。

1 个赞

您好 @pfaffman

您似乎无意中重复粘贴了同一个指南。

当您提到“yml 文件中的设置”时,您能否澄清您指的是哪个 YAML 文件?您是否在谈论位于 /var/discourse/containers 文件夹中的 app.yml 文件?

此外,如果我直接通过 YAML 文件配置我的 S3/CloudFront 集成,这些设置会覆盖 Discourse 管理部分中的配置吗?

谢谢!

抱歉。其中一个应该是 https://meta.discourse.org/t/set-up-file-and-image-uploads-to-s3/7229。

请参阅有关如何配置 S3 的链接主题,但是的,app.yml 是您想要更改的文件(因为您没有提到 web_only.yml

是的。在 YML 文件中输入它们会将它们从用户界面中隐藏。

您好 @pfaffman

我已经设置好了 S3 存储桶、CloudFront,并将 CloudFront 的源设置为我的 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
请注意,迁移到 S3 目前是不可逆的!
[CTRL+c] 取消,[ENTER] 继续
正在为 'default' 迁移上传到 S3...
一些上传未迁移到新方案。运行迁移,这可能需要一段时间...
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 提供用户上传的文件,或者是否有更好的解决方案?

也许我还是忽略了显而易见的东西?不知道
感谢您的帮助!

我怀疑那是因为你没有遵循说明。

是的。我明白。这是可能的,但你无法实现。这是推荐的方式;它有详细的文档,而且有数百人都是这样做的。

1 个赞

抱歉,你说得对,我漏掉了那部分。我已将以下内容添加到我的 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 和其他主题资源。

DISCOURSE_S3_CDN_URL 是一个指向你的对象存储桶并缓存请求的 CDN。它主要用于可推送的资源:JS、图像和用户上传。

我们建议将它们设置为不同的值,并由管理员进行设置。

但是,我不确定 DISCOURSE_CDN_URL 应该设置为多少?我应该将其设置为与 DISCOURSE_S3_CDN_URL: https://dsxxxxx2qn.cloudfront.net 相同的值,还是创建一个单独的 CDN 实例?

又或者,这可能是完全不同的问题 :confused:

非常感谢你的所有帮助!

您需要在 AWS 端配置 CDN 并使用您想要的域名。另外,还需要配置 DNS。

但为什么呢?实际上没有人会看到那个 URL。

1 个赞

嗨 Jake,我不确定我是否正确理解了你的意思。你能详细说明一下吗?

你的意思是不是,我应该将我的 forum.hobiguru.com 域名设置为 CDN 的源站,而不是将 S3 存储桶设置为源站?如果是这样,我认为这不会有任何改变,因为是 discourse 论坛生成了那些指向无效位置的 URL,例如 //my-bucket-eu.my-bucket-eu/original/1X/7f242572bdb45b65ded727c13366fe490541358f.jpeg

我已经添加了 DISCOURSE_CDN_URL: https://dsuxxxhrz2qn.cloudfront.net,现在正在等待应用程序重建 :crossed_fingers:

更新:不行,这也没起作用 :/\

我真的很感谢你的帮助。
谢谢你

您不能仅在 app.yml 中命名子域。没有人可以在没有正确 DNS 信息的情况下使用它。而且,由于您的文件是从 AWS 提供的,如果您想使用 CDN,也必须配置那边的设置。

但再说一次。用户可以看到 Discourse 的 URL,如果他们想看的话。除了管理员,很少有人会做其他事情。用户实际上看不到媒体或其他静态文件的 URL。

所以,我的观点是,如果您不想使用 CDN,您就是在浪费时间。如果您的受众遍布全球,并且其中一些人连接状况不佳,那么使用 CDN 可能很明智。

我住在芬兰。如果世界上另一个地方(澳大利亚)的网站使用 CDN(来自离我 1000 公里外的服务器),我不会得到任何实际的好处。真正的瓶颈在于网站的构建方式,例如是否使用了大量不必要的 PHP 调用而没有足够的资源。

但基本上,您不能在不至少在 DNS 中放置 cdn 的情况下使用 cdn.example.tld 这个 URL。

您好 @Jagster

实际上,我根本不想使用 CDN——我只想将所有用户上传都存储在 S3 中,并直接从那里提供服务,尽管 CloudFront 确实比直接使用 S3 是一个更好的解决方案。

我的问题出现在我尝试设置 S3(特别是用于提供资源,而不是上传)时,@pfaffman 建议我遵循 为上传配置 S3 兼容对象存储提供商 指南并配置 CDN。

关于您的评论:

“您不能只在 app.yml 中命名一个子域。没有人可以在没有正确 DNS 信息的情况下使用它。并且因为您的文件是从 AWS 提供的,所以如果您想使用 CDN,您也必须配置那一边。”

抱歉,我不太明白您在这段话中的意思。您能详细说明或澄清一下吗?您是说我需要为我的 S3 存储桶配置 DNS 记录,还是说我需要在 AWS 端进行一些特定的调整?

提前感谢您的帮助!

大家好,

我想跟进之前的帖子,分享一下我已经取得的一些进展。

现在可以正常工作的功能

  • 用户上传的内容现在可以正确地通过 CDN(CloudFront)从我的 S3 存储桶提供服务,这很棒!

但我仍然面临预编译资源的问题

预编译的资源仍然无法通过 CDN 正确提供服务。

当我将 DISCOURSE_CDN_URL 设置为 CloudFront URL(即 https://dsuqioxhrz2qn.cloudfront.net)时,预编译资源的 URL 变为:

问题在于,这些路径在我的 S3 存储桶中不存在。预编译的资源上传到 S3 的 /assets/* 文件夹下(例如,/assets/locales、/assets/plugins、/assets/scripts),但没有 /stylesheets/ 文件夹,因此加载这些 URL 会导致 403 Forbidden 错误。

但是,如果我将 DISCOURSE_CDN_URL 设置为 https://forum.hobiguru.com,那么我的论坛就可以正常工作,但资源是从服务器(例如 https://forum.hobiguru.com/)提供的,而不是从 CDN 提供的(例如 https://forum.hobiguru.com/stylesheets/admin_308d905aa5c03567866fec50e9a28d8721ab0463.css?__ws=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  # Ensure CDN URL points to CloudFront
DISCOURSE_CDN_URL: https://forum.hobigur.com # NOTICE THIS!
DISCOURSE_S3_BUCKET: hobiguru-s3-bucket-eu

以及预编译钩子:

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 加载时,对象的路径有点不对。