零停机升级

我们成功在几个月前完成了升级。我将我们的经验发布在这里,供将来可能遇到同样问题的人参考。

Discourse 通过部署后迁移在一定程度上支持零停机升级。据我们理解,整体流程可分为两个步骤。

步骤 1:升级到新版本并运行安全迁移:

  • 更新所有插件和主题以兼容新版本。(根据具体设置,这实际上是一项不小的工作量)。
  • 生成包含以下内容的 web_only.yml 文件:
    version: <NEW_VERSION>
    SKIP_POST_DEPLOYMENT_MIGRATIONS=1
  • 引导启动(./launcher bootstrap web_only
  • 重启服务器

步骤 2:运行部署后迁移(高风险迁移,例如列和表的删除等)。此时此步骤应该是安全的,因为所有服务器都已运行新版本的代码,不应依赖于将被删除的数据:

  • 生成包含以下内容的 web_only.yml 文件:
    version: <NEW_VERSION>
    SKIP_POST_DEPLOYMENT_MIGRATIONS=0
  • 引导启动(./launcher bootstrap web_only
  • 重启服务器

复杂情况
我们决定在不同日期执行步骤 1步骤 2,这导致了一些问题。在 2.1 和 2.3 版本之间,polls 模型经历了一次重大的重构。似乎最初大多数投票数据是以 JSON 对象的形式存储在 post_custom_fields 表中的。到了 2.3 版本,它们被移到了独立的 polls 表中。这需要一些数据迁移,该迁移是作为部署后迁移(步骤 2)的一部分完成的。

我们的具体问题在于,在升级到 2.3 之后,升级前创建的投票出现了故障,很可能是因为渲染它们的代码假设了新的数据模型。一些用户注意到了这一点,并尝试通过 UI 手动更新投票,结果在 polls 表中创建了记录。我们后来不幸地发现,此类记录在引导启动过程中会触发 Postgres 唯一约束,从而导致引导启动失败。

为了规避这个问题,我们决定应用一个补丁,如果某个特定的投票迁移在数据库中已存在,则跳过该迁移。这并不是一个完美的解决方案,因为某些 post_custom_fields 中的数据可能永远不会被迁移。但在我们的案例中,这仍然是一个不错的权衡,因为系统范围内此类情况非常少(我们观察到的只有 2 个实例),并且使我们能够继续执行引导启动过程。现在,测试和应用该补丁又引发了两个问题:

  • 我们如何在提交 PR 之前测试更改?
    我们希望在代码实际运行的相同条件下进行测试,这意味着需要在引导启动过程中进行测试。这不像测试运行时代码更改那样简单,后者可以通过在本地运行独立的 Discourse 应用来完成。

  • 我们如何将更改整合到系统中?
    我们不想等待补丁进入官方的 Discourse 发布版本,因为这可能是一个漫长的过程,并且基本上会迫使我们进行另一次升级。我们的客户已经对现有投票问题提出了投诉,等待时间越长,客户手动修改投票的可能性就越大,从而加剧数据完整性问题。

因此,我们找到了一种方法来测试我们引入的更改,即更改引导启动脚本所使用的仓库来源。这需要一些尝试和错误,因为我们对围绕此过程的 pups 和其他工具并不熟悉。最终,我们做了类似这样的操作。

这使我们能够验证我们的修复是否按预期工作。我们还能够从包含该补丁的我们自己修改的 Discourse 版本中,在生产环境引导启动一个新的镜像,而无需等待官方的 Discourse 发布。