极狐 GitLab

Ruby 升级指南

我们努力使用最新的 Ruby MRI 版本运行极狐GitLab,以受益于性能和安全更新以及新的 Ruby API。在极狐GitLab 中升级 Ruby 时,我们应该以一种方式来做:

  • 对贡献者影响最小。
  • 优化 JihuLab.com 的可用性。
  • 保持极狐GitLab 各部分 Ruby 版本一致。

在对 Ruby 版本进行更改之前,请仔细完整阅读本文档,以全面了解可能需要哪些更改。很可能每次 Ruby 升级都与前一次略有不同,因此请评估记录的步骤的顺序和必要性。

Ruby 升级的范围#

升级 Ruby 时首先要考虑的是范围。通常,我们考虑以下可能需要 Ruby 更新发生的领域:

  • 主要的极狐GitLab Rails 仓库。
  • 任何辅助的 Ruby 系统仓库。
  • 这些仓库中系统使用的任何第三方库。
  • 这些仓库中系统使用的任何极狐GitLab 库。

我们可能并不总是需要涉及所有这些。例如,补丁级别的 Ruby 更新不太可能需要更新第三方 gem。

补丁、次要和主要升级#

在评估范围时,Ruby 版本级别很重要。例如,将极狐GitLab 从 Ruby 2.x 升级到 3.x 比从 Ruby 2.7.2 升级到 2.7.4 更难且风险更大,因为补丁版本通常仅限于安全或错误修复。 在准备升级时要意识到这一点并相应地进行计划。

为了帮助您估计未来升级的范围,请参阅以下升级所需的工作量:

受影响的受众和目标#

在任何升级之前,请考虑所有受众和目标,按它们受 Ruby 升级影响的即时性排序:

  1. 开发者。 我们在公司内外都有许多极狐GitLab 及相关项目的贡献者。更改如 .ruby-version 这样的文件会影响所有使用解释这些文件的工具的人。开发者在从包含合并更改的仓库拉取时就会受到影响。
  2. 极狐GitLab CI/CD。 我们重度依赖 CI/CD 进行代码集成和测试。CI/CD 作业不会解释诸如 .ruby-version 这样的文件。相反,它们使用其执行所在的 Docker 容器中安装的 Ruby,该容器在 .gitlab-ci.yml 中定义。这些作业中使用的容器镜像在 gitlab-build-images 仓库中维护。当我们合并对镜像的更新时,CI/CD 作业会在镜像构建后立即受到影响。
  3. JihuLab.com。JihuLab.com 是从使用 Cloud Native 极狐GitLab (CNG) 的 Docker 镜像的自定义 Helm Chart 部署的。与 CI/CD 一样,.ruby-version 在此环境中没有意义。相反,必须修补这些 Docker 镜像以升级 Ruby。JihuLab.com 在下次部署时受到影响。
  4. 私有化部署。通过 Omnibus 安装极狐GitLab 的客户不使用上述任何方式。相反,他们的 Ruby 版本由 Omnibus 中的 Ruby 软件包定义。私有化部署客户在升级到包含此更改的版本后立即受到影响。

Ruby 升级方法#

正确安排 Ruby 升级中所有步骤的时间至关重要。作为一般指南,请考虑以下内容:

  • 对于生产环境行为不太可能发生变化的小型升级,目标是保持仓库与生产环境之间的版本差距最小。与利益相关者协调,将所有更改紧密合并(在一两天内),以避免版本漂移。在这种情况下,可能的顺序是先升级开发者工具和环境,然后是生产环境。
  • 对于较大的更改,使用新 Ruby 上线的风险很大。在这种情况下,尽量使所有已知与新 Ruby 版本不兼容的问题都已修复,然后与生产工程师合作将新 Ruby 部署到极狐GitLab 生产集群的一个子集。在这种情况下,可能的顺序是先更新生产环境,然后是开发者工具和环境。这样可以更容易地在生产环境中出现严重回归时进行回滚。

无论如何,从以往的经验来看,我们发现以下方法效果很好,其中一些步骤可能仅对次要和主要升级有必要。请注意,其中一些步骤可以并行发生,或者其顺序可能如上所述颠倒。

创建史诗#

在一个史诗中跟踪这项工作有助于了解进度。对于较大的升级,在史诗描述中包含一个时间表,以便利益相关者知道最终切换预计何时上线。包括指定的性能测试模板,以帮助确保升级的性能标准。

将各个仓库的更改分解为此史诗下的独立议题。

传达升级意向#

特别是对于引入或弃用功能的升级,应尽早传达即将进行升级,最好附上相关的时间表。提供指向重要或值得注意更改的链接,以便开发者可以提前开始熟悉更改。

极狐GitLab 团队成员应在相关的 Slack 频道(至少 #backend#development)和工程周回顾 (EWIR) 中宣布该意向。在您的沟通中包含指向升级史诗的链接。

将新 Ruby 添加到 CI/CD 和开发环境#

要使用新 Ruby 构建和运行 Ruby gem 以及极狐GitLab Rails 应用程序,您必须首先准备 CI/CD 和开发者环境以包含新 Ruby 版本。 在此阶段,您还不能将其设为默认 Ruby,而应使其成为可选项。这样可以在一段时间内同时支持新旧 Ruby 版本,从而实现更平滑的过渡。

有两个地方需要更改:

  1. 极狐GitLab 构建镜像 这些是我们在 Runner 和其他基于 Docker 的预生产环境中使用的 Docker 镜像。所需的更改类型取决于范围。
    • 对于补丁级别更新,增加 RUBY_VERSION 的补丁级别就足够了。所有针对同一次要版本构建的项目会自动下载新的补丁版本。
    • 对于主要和次要更新,创建一组新的 Docker 镜像,这些镜像可以在升级过程中与现有镜像并列使用。重要提示:确保将 /patches 目录中的所有 Ruby 补丁文件复制到与您升级到的 Ruby 版本匹配的新文件夹中,否则它们不会被应用。
  2. 极狐GitLab 开发工具包 (GDK) 更新 GDK 以将新 Ruby 作为开发者可选的附加选项。通常这只需要将其附加到 .tool-versions 中,以便 asdf 用户受益。其他用户需要手动安装 (示例)。

对于较大的版本升级,请考虑与质量工程合作,以确定并设置测试计划。

更新第三方 gem#

对于补丁版本,这通常没有必要,但对于次要和主要版本,当 gem 将 Ruby 固定到特定版本时,可能会出现破坏性更改或 Bundler 依赖问题。一个好的方法是创建一个 gitlab-org/gitlab 中的合并请求,看看会有什么问题。

这通常是必要的,因为我们自己维护的 gem 或 Ruby 应用程序包含构建设置,例如 .ruby-version.tool-versions.gitlab-ci.yml 文件。虽然技术上并不总是需要更新这些仓库以使极狐GitLab Rails 应用程序与新 Ruby 一起工作,但保持我们所有仓库的 Ruby 版本同步是个好习惯。对于次要和主要升级,使用新 Ruby 为这些仓库添加新的 CI/CD 作业。 构建矩阵定义可以有效地做到这一点。

决定要更新的仓库#

升级 Ruby 时,请考虑同时更新 ruby/gems 群组中的仓库。 作为参考,以下列出了过去为其中一些项目更新 Ruby 的合并请求:

要评估哪些仓库与主要极狐GitLab 应用程序一起更新至关重要,请考虑:

  • Ruby 版本范围。
  • 服务或库在极狐GitLab 整体功能中所起的作用。

请参阅极狐GitLab 项目列表,以获取可能受影响的仓库的完整说明。 对于较小的版本升级,可以接受延迟更新非必要的库,或者我们确信主应用程序测试套件会在新 Ruby 版本下捕获回归的库。

请咨询相应的代码所有者,是否可以在更新极狐GitLab 应用程序之前合并这些更改。最好获得必要的批准,但在一切准备就绪之前等待合并更改。

准备极狐GitLab 应用程序合并请求#

在依赖项更新和新 gem 版本发布后,您可以像处理 gem 和相关系统一样,使用任何必要的更改更新主 Rails 应用程序。 除此之外,更新文档以反映安装和更新说明中的版本更改(示例)。

请特别注意此合并请求的时间安排,因为一旦合并,所有极狐GitLab 贡献者都会受到影响,并且更改将被部署。您必须确保此 MR 保持开放状态,直到其他一切都准备就绪,但尽早获得批准以减少交付时间可能很有用。

给开发者升级时间(宽限期)#

在新 Ruby 作为可选版本提供并且所有合并请求要么就绪要么合并后,应该有一个宽限期(至少 1 周),在此期间开发者可以在他们的机器上安装新 Ruby。对于 GDK 和 asdf 用户,这应该通过 gdk update 自动完成。

这段暂停时间是评估此升级对 JihuLab.com 风险的好时机。对于高风险升级,例如主要版本升级,建议通过变更管理请求与基础设施团队协调更改。尽早创建此议题,让每个人都有足够的时间安排和准备更改。

使其成为默认 Ruby#

如果没有已知的版本兼容性问题,并且宽限期已过,则应更新所有受影响的仓库和开发者工具,以将新 Ruby 设为默认。

此时,更新 极狐GitLab Compose Kit (GCK)。 这是一个为更喜欢在 docker-compose 中运行极狐GitLab 的用户提供的替代开发环境。 该项目依赖于与我们 Runner 相同的 Docker 镜像,因此它应与该仓库中的更改保持同步。此更改仅在次要或主要版本更改时必要 (示例)。

如上所述,如果 Ruby 升级对 SaaS 可用性的影响不确定,则谨慎的做法是跳过此步骤,直到您通过分阶段推出验证它在生产环境中平稳运行为止。在这种情况下,请先进行下一步,然后在验证期过后,将新 Ruby 提升为新的默认版本。

更新 CNG、Omnibus 和自行编译版本并合并极狐GitLab 合并请求#

最后一步是在生产环境中使用新 Ruby。这需要更新 Omnibus 和生产 Docker 镜像以使用新版本。

要在生产环境中使用新 Ruby,请更新以下项目:

极狐GitLab Helm Chart 这样的 Chart,如果它们在某种程度上使用 Ruby,例如运行测试(参见此示例),也应该更新,尽管这可能不是严格必需的。

如果您提交了变更管理请求,请与基础设施工程师协调推出。处理较大的升级时,请让发布经理参与推出计划。

为安全补丁创建补丁版本和向后移植#

如果升级是补丁版本并且包含重要的安全修复,则应将其作为极狐GitLab 补丁版本发布给私有化部署客户。请咨询我们的发布经理以了解如何进行。

Ruby 升级工具#

有几种工具可以简化升级过程。

废弃日志记录器#

我们还将 Ruby 和 Rails 废弃警告记录到专用的日志文件 log/deprecation_json.log 中(请参阅极狐GitLab 开发者日志记录指南以了解在哪里找到极狐GitLab 日志文件),当存在未充分测试的代码时,它可以提供线索。

对于 JihuLab.com,极狐GitLab 团队成员可以在 Kibana 中检查这些日志事件(https://log.gprd.gitlab.net/goto/f7cebf1ff05038d901ba2c45925c7e01)。

建议#

在升级过程中,请考虑以下建议:

  • 尽可能提前进行更改。 特别是对于次要和主要版本,应用程序代码很可能会破坏或更改。任何向后兼容的更改都应合并到主分支中,并在 Ruby 版本升级之前独立发布。这确保我们以小增量移动,并尽早从生产环境中获得反馈。
  • 为较大的更新创建实验分支。 我们通常试图避免长期运行的主题分支,但为了反馈和实验的目的,拥有这样一个分支来在运行较新的 Ruby 时从 CI/CD 获得定期反馈可能会很有用。正如这个 MR 所展示的,这在首次评估我们可能遇到哪些问题时很有帮助。这些实验分支并非旨在被合并;一旦所有必需的更改都已拆分出来并独立合并回来,就可以关闭它们。
  • 给自己足够的时间在里程碑版本之前修复问题。 极狐GitLab 发展迅速。由于 Ruby 升级需要发送和审查许多 MR,请确保所有更改至少在发布日前一周合并。这给了我们额外的时间在出现问题时采取行动。如果有疑问,最好将升级推迟到下个月,因为我们优先考虑可用性而非速度