Rake uploads:migrate_from_s3 失败

我按照这里的步骤操作,备份了整个网站,克隆了我的 AWS S3 存储桶,在 Discourse 设置中将存储桶名称从原始存储桶更改为备份存储桶,并在设置中关闭了“上传到 S3”复选框。

所以现在我终于准备好开始从 S3 迁移了……但它失败了。:frowning:

错误消息

root@ubuntu:/var/www/discourse# rake uploads:migrate_from_s3
Migrating uploads from S3 to local storage for 'default'...
rake aborted!
NoMethodError: undefined method `downcase' for nil:NilClass
/var/www/discourse/app/models/global_setting.rb:107:in `s3_bucket_name'
/var/www/discourse/app/models/site_setting.rb:157:in `absolute_base_url'
/var/www/discourse/lib/tasks/uploads.rake:138:in `migrate_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:118:in `block in migrate_all_from_s3'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.2.2/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.2.2/lib/rails_multisite/connection_management.rb:78:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:118:in `migrate_all_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:93:in `block in <top (required)>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => uploads:migrate_from_s3
(See full trace by running task with --trace)

(这是 GitHub 中出错的行——我猜它无法获取 s3_bucket 的值?)

我尝试过的其他方法

  • 我尝试在命令行中添加凭据,但这没有区别。例如:
    DISCOURSE_S3_BUCKET="dn-forum-storage-backup" DISCOURSE_S3_REGION="us-east-1" DISCOURSE_S3_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxxxx" DISCOURSE_S3_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxx" DISCOURSE_S3_CDN_URL="https://dn-forum-storage-backup.s3.us-east-1.amazonaws.com" rake uploads:migrate_from_s3

  • 我还尝试将设置中的 S3 存储桶名称改回原始存储桶名称,仍然没有成功,结果相同。

  • 我还尝试重新构建应用程序。结果相同。

@vinothkannans 你知道是怎么回事吗?

请帮忙,Discourse 的朋友们!

附注:一个小提示:rake --tasks 没有列出此任务或任何以 uploads 开头的任务,不确定这是否意味着什么。

可能的关联问题?cc @mcdanlj

@pnoeric 是的,这看起来完全一样。我还没有收到关于 SiteSettingsGlobalSettings 在 S3 方面意图的明确回复,因此目前除了通过配置将其添加到 SiteSettings(我帖子中的第 1 点)之外,我无法提供进一步的帮助。

嘿,谢谢你的回复……我甚至不确定 SiteSettingsGlobalSettings 有什么区别——我的 RoR 编程水平还不够,对整体设置的理解也不够深入。我只是按照基本说明操作。:wink:

不过希望 @vinothkannans 也能加入讨论;我想是他写了迁移任务的代码。或者 @team 中其他可能了解的人也可以……

让我们持续关注这个话题……

s3_bucket 位于 GlobalSettings 中,该设置通常由 config/discourse.conf 文件配置,而该文件又由 app.yml 文件中的环境变量生成。SiteSettings 则是您在应用的管理设置中可修改的配置项。

看起来在最初创建时,您只能通过重建应用来更改 S3 设置,而最近才变得可以在管理设置中填写相关数据。我不确定在将 S3 设置添加到 SiteSettings 时,为何没有进行全面的迁移。

[编辑:我在首次发布此回复时不小心将两者弄反了]

1 个赞

嗯,@mcdanlj,只是想确认一下,你确实还没搞清楚如何让 migrate_from_s3 真正生效,对吧?

我完全可以编辑任何必要的设置、底层文件或其他东西……我只是需要尽快从 S3 迁移出去,因为它的费用实在太高了。

@pnoeric@mcdanlj

一种调试方法可能是进入 Rails 控制台,然后查看 S3 站点设置?

例如,在一个标准的开箱即用(OOTB)Discourse Docker 单容器独立应用中:

root@localhost:~# docker exec -it app rails c
[1] pry(main)> SiteSetting.s3_upload_bucket
=> ""
[2] pry(main)> SiteSetting.enable_s3_uploads
=> false
[3] pry(main)> 

将 Rails 控制台中的 SiteSettings 与默认值进行比较,默认值列在这里:

也许以这种方式进行调试会有所帮助(其实我们并不使用 AWS 或 S3,所以不太确定)?或许 Rails 控制台能提供一些帮助?

1 个赞

rake --tasks 仅显示带有描述的任务。你可以通过 rake -AT 查看所有可用任务。

我认为这不会有太大帮助,因为我最近刚在测试站点上运行过这些任务。这两个任务似乎都依赖于在 env 中定义的 S3 变量,不过那是几个月前的事了,而且 migrate_from_s3 对我来说并没有真正起作用。

1 个赞

这是个棘手的问题。我确实按照你链接的帖子中所说,在 config/discourse.conf 中设置了 s3_bucket,正如我当时指出的那样,这确实解决了这个特定的错误。

该文件位于容器内部(通过 ./launcher enter app 进入)。请注意,若要使该设置在 ./launcher rebuild app 后仍然生效,你还需要在 containers/app.yml 文件的 env 部分添加 DISCOURSE_S3_BUCKET

我之所以修复了这个问题,才将此帖作为开发讨论而非支持请求发布;我想知道开发人员认为什么是正确的解决方案,因为我在继续探索这个问题。

由于 S3 中约有 100GB 的文件,所以我非常谨慎。我已经实现了对要查看的帖子数量进行限制,接下来需要实现要对要修改的帖子数量进行限制。我一直是一次尝试一个功能。鉴于这段代码似乎很少被使用,而我却反复遇到这个错误,这让我担心代码腐化,我不想因为一个 bug 而突然破坏整个网站,而这个问题看起来很容易导致这种错误。

  • 对于 upload://(对我来说,这意味着非视频)上传,目前看来运行正常。我是一次处理一个,然后检查受影响的帖子,确保一切正常。

  • 对于不使用 upload:// 语法的上传(对我来说,据我所知这意味着视频上传),其中在 S3 中直接引用了 URL,这些 URL 会被错误地修改。一旦我确定应该将它们改成什么,这个 bug 并不难修复,但我目前还没有做到这一点。因此,这很可能成为我即将发布的 PR 之一。

这是我利用业余时间进行的项目,所以无法保证具体时间。

1 个赞

啊哈,谢谢!我会试试看。

唉,还是没有成功,@neounix @mcdanlj @vinothkannans。仍然失败。但至少出现了一个新的/不同的错误信息……

这是我今天尝试的步骤:

  1. 升级到最新版的 Discourse,以确保万无一失。

  2. config/discourse.conf 中添加我的 s3_bucket

  3. 运行 ./launcher enter app

  4. 编辑 containers/app.yml 并添加 DISCOURSE_S3_BUCKET 变量。

  5. 尝试运行 rake uploads:migrate_from_s3,现在它又失败了,并显示新的错误信息(之前是 downcase 导致的问题,现在看起来是 start_with?):

/var/www/discourse# rake uploads:migrate_from_s3
Migrating uploads from S3 to local storage for 'default'...
rake aborted!
NoMethodError: undefined method `start_with?' for nil:NilClass
/var/www/discourse/app/models/site_setting.rb:161:in `absolute_base_url'
/var/www/discourse/lib/tasks/uploads.rake:138:in `migrate_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:118:in `block in migrate_all_from_s3'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:78:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:118:in `migrate_all_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:93:in `block in <main>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => uploads:migrate_from_s3
(See full trace by running task with --trace)
  1. 于是我又尝试了 ./launcher rebuild app

  2. 再次运行 ./launcher enter app,然后执行 rake uploads:migrate_from_s3

问题完全一样:

/var/www/discourse# rake uploads:migrate_from_s3
Migrating uploads from S3 to local storage for 'default'...
rake aborted!
NoMethodError: undefined method `start_with?' for nil:NilClass
/var/www/discourse/app/models/site_setting.rb:161:in `absolute_base_url'
/var/www/discourse/lib/tasks/uploads.rake:138:in `migrate_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:118:in `block in migrate_all_from_s3'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:68:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/rails_multisite-2.3.0/lib/rails_multisite/connection_management.rb:78:in `each_connection'
/var/www/discourse/lib/tasks/uploads.rake:118:in `migrate_all_from_s3'
/var/www/discourse/lib/tasks/uploads.rake:93:in `block in <main>'
/usr/local/bin/bundle:23:in `load'
/usr/local/bin/bundle:23:in `<main>'
Tasks: TOP => uploads:migrate_from_s3
(See full trace by running task with --trace)

还有其他建议吗?

顺便说一句,执行这个过程真的很烦人——我不得不提前几天安排并宣布论坛停机,然后在当天修改主站,让用户无法访问论坛,接着关闭 DigitalOcean 上的论坛服务器并创建快照,然后再继续。这就要花大约 30 分钟。之后我再启动服务器,然后才能尝试上述步骤。我真的很后悔当初选择 Amazon S3 作为媒体存储!我已经花了数小时试图撤销这个决定,但仍然没有成功(而且每个月还要付一大笔钱给 Amazon)。我很想彻底解决这个问题。我该如何协助?

那行代码是:

        if SiteSetting.Upload.s3_region.start_with?("cn-")

看起来它也需要 s3_region;我不清楚为什么我没有遇到这个问题。

我不太理解你的逻辑;我计划在对约 100GB 内容进行正常站点备份后,在线执行迁移。但我打算从小规模开始,这就是为什么我一直在努力限制每次迁移的数据量。一个警告:对于字面 URL 转换,代码似乎有误,正如我在视频上传中看到的,因此如果你允许视频上传,当前状态的代码可能会在那里出现问题。

2 个赞

所以,也许我应该重复上面所有的步骤,但这次我会把 s3_bucket、s3_region、s3_cdn_url、s3_secret_access_key 等等(基本上是我拥有的所有变量)都放入 conf 和 yml 文件中?我宁愿提供比(可能)需要的更多的配置,只要确保它能真正运行起来。

我看到 Discourse 团队中的某人建议在开始此过渡之前备份整个本地站点。这意味着我需要将 Digital Ocean 服务器下线。:frowning:

没错。我也是从小规模开始……每次尝试时,我迁移的文件数量都是 0。:grin:

幸运的是,在我的论坛中,成员只被允许上传 JPG、GIF 和 PNG 格式的文件,所以应该没问题。

祝好运。

备份和快照并不相同。快照是最粗糙的备份形式。管理控制台具备备份功能。请确保先在配置中将其设置为备份缩略图。

既然你已经知道无需让站点停机,应该可以放松一些了。你可以使用 batch_migrate_from_s3 来迁移最多一定数量的上传文件。目前它限制的是被考虑的帖子数量,而不是实际完成的迁移次数,这是我需要在未来的 PR 中修复的一个问题。但我还需要解决视频上传的 bug,并且我希望能考虑添加反馈输出,因为设置限制的一个目的就是为了能够在受影响的帖子中确认迁移是否成功。

我可能在接下来的 1-2 个月内完成所有这些工作,所以如果你想等待,多支付几个月的 S3 费用可能是值得的。这取决于你,我并非做出承诺,只是说明意图。

2 个赞

@pnoeric 既然你担心站点正常运行时间,我想把我目前了解到的情况告诉你。

正如我所提到的,我是实时进行迁移的。如果不对迁移进行速率限制,负责通知用户彼此活动等任务的队列就会被堵塞,从而降低站点的用户体验。

我迁移了大约 500 篇包含视频的帖子和约 30,000 篇包含图片的帖子,整个过程耗时约两周。

如果你想尝试我使用的代码,它目前位于:

你可以下载它并复制到你的应用中,以替换当前 lib/tasks/uploads.rake 的内容。

使用这段代码,你可以执行类似以下的操作:

bin/rake uploads:batch_migrate_from_s3[100,1000]

这将仅考虑总共 1000 篇包含上传文件的帖子,并迁移最多 100 个文件后停止;每次在迁移上传文件后实际修改帖子时,它都会等待队列清空后再开始下一个。

如果你复制该文件,在撤销更改之前,它会破坏未来的站点更新。在你满意之后,最简单的撤销方法就是运行 ./launcher rebuild app(不过作为开发者,我通常使用 git checkout HEAD lib/tasks/uploads.rake 来撤销我的更改……)

我注意到,至少在使用 DigitalOcean Spaces 时,有时需要重试几次迁移才能成功。目前的脚本在这种情况下不会发出任何警告,你只能不断运行它并等待观察结果。我确实有一个等待审查的 PR,它会在出现这种情况时打印出错误信息,这样你至少能知道出了什么问题。

……

我添加了一个简单的短重试循环以及错误消息,看起来重试循环解决了这个问题。此外,之前针对当前规则进行的验证是基于过去帖子的原始内容,这可能会破坏迁移并静默留下需要重新烘焙的帖子;我也修复了这个问题。你绝对不应该在没有至少获得验证修复的情况下进行迁移,而该修复是我当前等待审查的 PR 中的其中一个提交。

……

据我所知,我的迁移已经完成。我的 PR 包含了我为完成迁移所使用的全部代码。该 PR 尚未经过审查。如果你想了解进展,我建议关注 https://meta.discourse.org/t/migrate-from-s3-problems/119064。

2 个赞

谢谢!我打算在接下来几天试试这个。

我刚刚在那篇帖子中补充了一条说明:我们今天发现还剩下一个 Bug,部分用户的头像丢失了,原因尚不清楚。我们耸了耸肩,并一直请受影响的用户进行恢复,同时为这个问题表示歉意。

在这个过程中,我确实进行了频繁的备份!:smiling_face:

祝好运!

1 个赞

说真的,这会不会让我更疯狂?:crazy_face:

我做了以下操作:

  1. 将你们新的 lib/tasks/upload.rake 代码复制到我的 Discourse 中
  2. 将所有 Amazon 的 s3_ 变量添加到 config/discourse.conf
  3. 同时也将它们添加到了 app.yml 中(不确定这是否有用,但试试无妨)
  4. 运行了该命令,结果如下:
root@:/var/www/discourse/config# rake uploads:batch_migrate_from_s3[100,1000]
You must disable S3 uploads before running that task.

并确认了:

好吧。于是我编辑了 uploads.rake 文件,直接移除了该检查。

现在运行后得到:

root@:/var/www/discourse/lib/tasks# rake uploads:batch_migrate_from_s3[100,1000]
Migrating uploads from S3 to local storage for 'default'...
Migrating up to 100 of 1000 posts...
... (此处有大量输出) ...
Modified 91/100: 28795: 28486/1 - https://example.com/t/topic-title-here/28486/1
... (此处有大量输出) ...

看起来正在工作!太好了!

在完成第一批 100 条后,我检查了 Sidekiq,看到我的测试帖子已被加入队列,于是等待其完成:

……然后回去检查,发现该帖子仍然从 Amazon S3 加载图片。:frowning: 我尝试了对该帖子执行“重建 HTML”,但没有任何变化。

于是我又重新走了一遍整个流程,从 rake 命令开始一直到结束,结果依然相同——同样是那 100 条帖子被处理,同样的任务在 Sidekiq 中排队,运行结束后,测试帖子中的图片仍然来自 S3。

嗯……我不确定下一步该尝试什么。:man_shrugging:t2:

@mcdanlj 非常感谢您的任何建议或指导 :wink:

1 个赞

如果你移除了那个检查,这正是我所预期的。我不确定你为何决定移除它。这是有意为之。在开始迁移之前,请关闭到 S3 的上传。

1 个赞

它们没生效——完全没生效。(我帖子中的复选框图片显示的是正确的设置,对吗?)我甚至把它们打开又关闭了,但没用。