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 升级影响的即时性排序:
- 开发者。 我们在公司内外都有许多极狐GitLab 及相关项目的贡献者。更改如 .ruby-version 这样的文件会影响所有使用解释这些文件的工具的人。开发者在从包含合并更改的仓库拉取时就会受到影响。
- 极狐GitLab CI/CD。 我们重度依赖 CI/CD 进行代码集成和测试。CI/CD 作业不会解释诸如 .ruby-version 这样的文件。相反,它们使用其执行所在的 Docker 容器中安装的 Ruby,该容器在 .gitlab-ci.yml 中定义。这些作业中使用的容器镜像在 gitlab-build-images 仓库中维护。当我们合并对镜像的更新时,CI/CD 作业会在镜像构建后立即受到影响。
- JihuLab.com。JihuLab.com 是从使用 Cloud Native 极狐GitLab (CNG) 的 Docker 镜像的自定义 Helm Chart 部署的。与 CI/CD 一样,.ruby-version 在此环境中没有意义。相反,必须修补这些 Docker 镜像以升级 Ruby。JihuLab.com 在下次部署时受到影响。
- 私有化部署。通过 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 版本,从而实现更平滑的过渡。
有两个地方需要更改:
- 极狐GitLab 构建镜像。 这些是我们在 Runner 和其他基于 Docker 的预生产环境中使用的 Docker 镜像。所需的更改类型取决于范围。
- 极狐GitLab 开发工具包 (GDK)。 更新 GDK 以将新 Ruby 作为开发者可选的附加选项。通常这只需要将其附加到 .tool-versions 中,以便 asdf 用户受益。其他用户需要手动安装 (示例)。
对于较大的版本升级,请考虑与质量工程合作,以确定并设置测试计划。
更新第三方 gem
对于补丁版本,这通常没有必要,但对于次要和主要版本,当 gem 将 Ruby 固定到特定版本时,可能会出现破坏性更改或 Bundler 依赖问题。一个好的方法是创建一个 gitlab-org/gitlab 中的合并请求,看看会有什么问题。
更新极狐GitLab gem 和相关系统
这通常是必要的,因为我们自己维护的 gem 或 Ruby 应用程序包含构建设置,例如 .ruby-version、.tool-versions 或 .gitlab-ci.yml 文件。虽然技术上并不总是需要更新这些仓库以使极狐GitLab Rails 应用程序与新 Ruby 一起工作,但保持我们所有仓库的 Ruby 版本同步是个好习惯。对于次要和主要升级,使用新 Ruby 为这些仓库添加新的 CI/CD 作业。 构建矩阵定义可以有效地做到这一点。
决定要更新的仓库
升级 Ruby 时,请考虑同时更新 ruby/gems 群组中的仓库。 作为参考,以下列出了过去为其中一些项目更新 Ruby 的合并请求:
- 极狐GitLab LabKit(示例)
- 极狐GitLab Exporter(示例)
- 极狐GitLab Experiment(示例)
- Gollum Lib(示例)
- 极狐GitLab Sidekiq fetcher(示例)
- Prometheus Ruby Mmap Client(示例)
- 极狐GitLab-mail_room(示例)
要评估哪些仓库与主要极狐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,请确保所有更改至少在发布日前一周合并。这给了我们额外的时间在出现问题时采取行动。如果有疑问,最好将升级推迟到下个月,因为我们优先考虑可用性而非速度。