Gitaly 集群 (Praefect)
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed
Git 存储通过 GitLab 中的 Gitaly 服务提供,是 GitLab 运行的关键组成部分。当用户、仓库和活动数量增长时,需要通过以下方式适当扩展 Gitaly:
- 在资源耗尽导致 Git、Gitaly 和 GitLab 应用程序性能下降之前,增加 Git 可用的 CPU 和内存资源。
- 在达到存储限制导致写入操作失败之前,增加可用存储。
- 消除单点故障以提高容错能力。如果服务降级会阻止您将更改部署到生产环境,则应将 Git 视为关键任务。
Gitaly 可以在集群配置中运行,以:
- 扩展 Gitaly 服务。
- 提高容错能力。
在此配置中,每个 Git 仓库都可以存储在集群中的多个 Gitaly 节点上。
使用 Gitaly 集群 (Praefect) 通过以下方式提高容错能力:
- 将写入操作复制到热备用 Gitaly 节点。
- 检测 Gitaly 节点故障。
- 自动将 Git 请求路由到可用的 Gitaly 节点。
Gitaly 集群 (Praefect) 的技术支持仅限于 GitLab Premium 和 Ultimate 客户。
下图显示了设置为访问 storage-1(由 Gitaly 集群 (Praefect) 提供的虚拟存储)的 GitLab:
在此示例中:
- 仓库存储在名为
storage-1的虚拟存储上。 - 三个 Gitaly 节点提供
storage-1访问:gitaly-1、gitaly-2和gitaly-3。 - 三个 Gitaly 节点在三个单独的哈希存储位置共享数据。
- 复制因子 为
3。每个仓库维护三个副本。
假设单个节点故障,Gitaly 集群 (Praefect) 的可用性目标为:
-
恢复点目标 (RPO):小于 1 分钟。
写入操作是异步复制的。任何尚未复制到新提升的主节点的写入都会丢失。
强一致性 在某些情况下可防止丢失。
-
恢复时间目标 (RTO):小于 10 秒。 每个 Praefect 节点每秒运行一次健康检查来检测中断。故障转移需要在每个 Praefect 节点上连续十次失败的健康检查。
RPO 和 RTO 的改进已在 epic 8903 中提出。
如果发生完全的集群故障,应执行灾难恢复计划。这些可能会影响前面讨论的 RPO 和 RTO。
与 Geo 的比较
Gitaly 集群 (Praefect) 和 Geo 都提供冗余。但是冗余的:
- Gitaly 集群 (Praefect) 为数据存储提供容错能力,对用户不可见。用户不知道何时使用了 Gitaly 集群 (Praefect)。
- Geo 为整个 GitLab 实例提供复制和灾难恢复。用户知道他们何时使用 Geo 进行复制。Geo复制多种数据类型,包括 Git 数据。
下表概述了 Gitaly 集群 (Praefect) 和 Geo 之间的主要区别:
| 工具 | 节点 | 位置 | 延迟容忍度 | 故障转移 | 一致性 | 为…提供冗余 |
|---|---|---|---|---|---|---|
| Gitaly 集群 (Praefect) | 多个 | 单个 | 小于 1 秒,理想情况下为个位数毫秒 | 自动 | 强 | Git 中的数据存储 |
| Geo | 多个 | 多个 | 最多一分钟 | 手动 | 最终 | 整个 GitLab 实例 |
更多信息,请参见:
虚拟存储
虚拟存储使得在 GitLab 中拥有单个仓库存储成为可能,从而简化仓库管理。
使用 Gitaly 集群 (Praefect) 的虚拟存储通常可以替代直接的 Gitaly 存储配置。然而,这需要额外的存储空间来在每个 Gitaly 节点上存储每个仓库的副本。使用 Gitaly 集群 (Praefect) 虚拟存储相对于直接 Gitaly 存储的好处是:
- 提高容错能力,因为每个 Gitaly 节点都有每个仓库的副本。
- 提高资源利用率,减少为分片特定峰值负载过度配置的需求,因为读取负载分布在 Gitaly 节点上。
- 不需要为性能进行手动重新平衡,因为读取负载分布在 Gitaly 节点上。
- 管理更简单,因为所有 Gitaly 节点都是相同的。
仓库副本的数量可以使用复制因子进行配置。
对所有仓库使用相同的复制因子可能不经济。为超大型 GitLab 实例提供更大的灵活性,可变复制因子在此问题中跟踪。
与标准 Gitaly 存储一样,虚拟存储也可以进行分片。
存储布局
存储布局是 Gitaly 集群 (Praefect) 的内部细节,不保证在版本之间保持稳定。此处信息仅供参考并帮助调试。直接在磁盘上对仓库进行更改不受支持,可能导致损坏或更改被覆盖。
Gitaly 集群 (Praefect) 虚拟存储提供了一个看起来像单个存储但实际上由多个物理存储组成的抽象。Gitaly 集群 (Praefect) 必须将每个操作复制到每个物理存储。操作可能在某些物理存储上成功,但在其他存储上失败。
部分应用的操作可能会导致其他操作出现问题,并使系统处于无法恢复的状态。为避免这类问题,每个操作应该要么完全应用,要么完全不应用。这种操作特性称为原子性。
GitLab 控制仓库存储上的存储布局。GitLab 指示仓库存储在哪里创建、删除和移动仓库。当这些操作应用于多个物理存储时,它们会产生原子性问题。例如:
- GitLab 删除一个仓库,而其某个副本不可用。
- GitLab 稍后重新创建该仓库。
结果,在删除时不可用的过时副本可能会导致冲突并阻止仓库的重新创建。
这些原子性问题在过去导致了多个问题:
- Geo 同步到具有 Gitaly 集群 (Praefect) 的辅助站点。
- 备份恢复。
- 仓库在仓库存储之间的移动。
Gitaly 集群 (Praefect) 通过在磁盘上以特殊布局存储仓库来为这些操作提供原子性,该布局可防止由于部分应用的操作而可能发生的冲突。
客户端生成的副本路径
仓库存储在存储中,其相对路径由 Gitaly 客户端 确定。这些路径可以通过不以 @cluster 前缀开头来识别。相对路径遵循哈希存储模式。
Praefect 生成的副本路径
当 Gitaly 集群 (Praefect) 创建仓库时,它会为仓库分配一个唯一的永久 ID,称为_仓库 ID_。仓库 ID 是 Gitaly 集群 (Praefect) 内部的,与 GitLab 中其他地方的任何 ID 无关。如果仓库从 Gitaly 集群 (Praefect) 中删除并稍后移回,仓库将被分配一个新的仓库 ID,并且从 Gitaly 集群 (Praefect) 的角度来看是一个不同的仓库。仓库 ID 序列始终增加,但序列中可能有间隔。
仓库 ID 用于为集群中的每个仓库派生一个唯一的存储路径,称为_副本路径_。仓库的副本都存储在存储上的相同副本路径中。副本路径与_相对路径_不同:
- 相对路径是 Gitaly 客户端用来标识仓库的名称,与其虚拟存储一起,对它们是唯一的。
- 副本路径是物理存储中的实际物理路径。
Praefect 在处理客户端请求时,将 RPC 中的仓库从虚拟 (虚拟存储, 相对路径) 标识符转换为物理仓库 (存储, 副本路径) 标识符。
副本路径的格式为:
- 对象池是
@cluster/pools/<xx>/<xx>/<仓库 ID>。对象池存储在与其他仓库不同的目录中。它们必须可被 Gitaly 识别,以避免作为维护的一部分被修剪。修剪对象池可能导致链接仓库中的数据丢失。 - 其他仓库是
@cluster/repositories/<xx>/<xx>/<仓库 ID>
例如,@cluster/repositories/6f/96/54771。
副本路径的最后一个组件 54771 是仓库 ID。这可用于在磁盘上识别仓库。
<xx>/<xx> 是仓库 ID 字符串表示的 SHA256 哈希的前四个十六进制数字。这些数字用于将仓库均匀地分布到子目录中,以避免在某些文件系统上可能导致问题的过大目录。在这种情况下,54771 哈希为 6f960ab01689464e768366d3315b3d3b2c28f38761a58a70110554eb04d582f7,所以前四个数字是 6f 和 96。
在磁盘上识别仓库
使用 praefect metadata 子命令来:
- 从元数据存储中检索仓库的虚拟存储和相对路径。获得哈希存储路径后,可以使用 Rails 控制台检索项目路径。
- 使用以下任一方法查找仓库在集群中的存储位置:
- 虚拟存储和相对路径。
- 仓库 ID。
磁盘上的仓库还在 Git 配置文件中包含项目路径。即使仓库的元数据已被删除,也可以使用配置文件来确定项目路径。按照哈希存储文档中的说明。
操作的原子性
Gitaly 集群 (Praefect) 使用 PostgreSQL 元数据存储和存储布局来确保仓库创建、删除和移动操作的原子性。磁盘操作不能跨多个存储原子应用。但是,PostgreSQL 保证元数据操作的原子性。Gitaly 集群 (Praefect) 以一种方式对操作进行建模,使失败的操作始终使元数据保持一致。即使在成功操作后,磁盘也可能包含过时状态。这种情况是预期的,剩余状态不会干扰未来的操作,但可能会不必要地占用磁盘空间,直到执行清理。
有一个正在进行的后台爬虫工作,用于清理存储中剩余的仓库。
仓库创建
创建仓库时,Praefect:
- 从 PostgreSQL 预留一个仓库 ID,这是原子操作,没有两个创建会收到相同的 ID。
- 在从仓库 ID 派生的副本路径中在 Gitaly 存储上创建副本。
- 在仓库成功在磁盘上创建后创建元数据记录。
即使两个并发操作创建相同的仓库,它们也会存储在存储上的不同目录中,不会冲突。第一个完成的操作创建元数据记录,另一个操作以"已存在"错误失败。失败的创建在存储上留下剩余的仓库。有一个正在进行的后台爬虫工作,用于清理存储中剩余的仓库。
仓库 ID 是从 PostgreSQL 中的 repositories_repository_id_seq 生成的。在前面的示例中,失败的操作占用了一个仓库 ID,但没有成功创建使用该 ID 的仓库。失败的仓库创建预计会导致仓库 ID 中出现间隔。
仓库删除
通过删除其元数据记录来删除仓库。一旦元数据记录被删除,仓库就不再逻辑存在。PostgreSQL 保证删除的原子性,并发删除会以"未找到"错误失败。成功删除元数据记录后,Praefect 尝试从存储中删除副本。这可能会失败并在存储中留下剩余状态。剩余状态最终会被清理。
仓库移动
与 Gitaly 不同,Gitaly 集群 (Praefect) 不在存储中移动仓库,而是仅通过更新元数据存储中仓库的相对路径来虚拟移动仓库。
组件
Gitaly 集群 (Praefect) 由多个组件组成:
- 负载均衡器,用于分发请求并为 Praefect 节点提供容错访问。
- Praefect 节点,用于管理集群并将请求路由到 Gitaly 节点。
- PostgreSQL 数据库,用于持久化集群元数据和 PgBouncer,建议用于池化 Praefect 的数据库连接。
- Gitaly 节点,用于提供仓库存储和 Git 访问。
架构
Praefect 是 Gitaly 的路由器和事务管理器,是运行 Gitaly 集群 (Praefect) 的必需组件。
更多信息,请参见 Gitaly 高可用性 (HA) 设计。
功能
Gitaly 集群 (Praefect) 提供以下功能:
请关注 epic 1489 了解提出的改进,包括水平分布读取。
分布式读取
Gitaly 集群 (Praefect) 支持在为虚拟存储配置的 Gitaly 节点之间分发读取操作。
所有标记为 ACCESSOR 选项的 RPC 都被重定向到最新且健康的 Gitaly 节点。例如,GetBlob。
在此上下文中,“最新"意味着:
- 没有为该 Gitaly 节点安排复制操作。
- 最后一个复制操作处于完成状态。
如果满足以下条件,则选择主节点为请求提供服务:
- 没有最新的节点。
- 在节点选择期间发生任何其他错误。
如果您有一个大型、频繁修改的仓库(如多吉字节的单一代码库),如果更改到来的速度比 Praefect 可以复制到辅助节点的速度快,主节点可能会为大部分或所有请求提供服务。当这种情况发生时,CI/CD 作业和其他仓库流量会受到主节点容量的瓶颈限制。
您可以使用 Prometheus 监控读取的分布。
强一致性
Gitaly 集群 (Praefect) 通过将更改同步写入所有健康、最新的副本来提供强一致性。如果副本在事务时过时或不健康,则写入将异步复制到该副本。
强一致性是主要的复制方法。一部分操作仍然使用复制作业(最终一致性)而不是强一致性。有关更多信息,请参考强一致性 epic。
如果强一致性不可用,Gitaly 集群 (Praefect) 保证最终一致性。在这种情况下,Gitaly 集群 (Praefect) 在写入主 Gitaly 节点发生后,将所有写入复制到辅助 Gitaly 节点。
有关监控强一致性的更多信息,请参见监控 Gitaly 集群 (Praefect)。
复制因子
复制因子是 Gitaly 集群 (Praefect) 为给定仓库维护的副本数量。更高的复制因子:
- 提供更好的冗余和读取工作负载的分布。
- 导致更高的存储成本。
默认情况下,Gitaly 集群 (Praefect) 将仓库复制到虚拟存储中的每个存储。
有关配置信息,请参见配置复制因子。
升级 Gitaly 集群 (Praefect)
要升级 Gitaly 集群 (Praefect),请遵循零停机升级的文档。
将 Gitaly 集群 (Praefect) 降级到以前的版本
如果需要将 Gitaly 集群 (Praefect) 回滚到早期版本,可能需要还原一些 Praefect 数据库迁移。
要降级 Gitaly 集群 (Praefect),假设有多个 Praefect 节点:
-
在所有 Praefect 节点上停止 Praefect 服务:
gitlab-ctl stop praefect -
在其中一个 Praefect 节点上将 GitLab 软件包降级到旧版本。
-
在降级的节点上,检查 Praefect 迁移的状态:
sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status -
计算
APPLIED列中具有unknown migration的迁移数量。 -
在尚未降级的 Praefect 节点上,执行回滚的试运行以验证要还原的迁移。
<CT_UNKNOWN>是降级节点报告的未知迁移数量。sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate <CT_UNKNOWN> -
如果结果看起来正确,使用
-f选项运行相同的命令以还原迁移:sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate -f <CT_UNKNOWN> -
在其余的 Praefect 节点上降级 GitLab 软件包并重新启动 Praefect 服务:
gitlab-ctl start praefect
迁移到 Gitaly 集群 (Praefect)
Gitaly 集群 (Praefect) 中存在一些已知问题。在继续之前,请查看以下信息。
在迁移到 Gitaly 集群 (Praefect) 之前:
- 查看部署 Gitaly 集群 (Praefect) 之前。
- 升级到最新版本的 GitLab,以利用改进和错误修复。
要迁移到 Gitaly 集群 (Praefect):
- 创建所需的存储。请参阅仓库存储建议。
- 创建和配置 Gitaly 集群 (Praefect)。
- 配置现有的 Gitaly 实例使用 TCP,如果尚未这样配置。
- 移动仓库。要迁移到 Gitaly 集群 (Praefect),必须移动存储在 Gitaly 集群 (Praefect) 外部的现有仓库。没有自动迁移,但可以使用 GitLab API 安排移动。
即使您不使用 default 仓库存储,也必须确保它已配置。阅读有关此限制的更多信息。
从 Gitaly 集群 (Praefect) 迁移出
如果发现 Gitaly 集群 (Praefect) 的限制和权衡不适合您的环境,您可以从 Gitaly 集群 (Praefect) 迁移到分片的 Gitaly 实例:
- 创建和配置新的 Gitaly 服务器。
- 将仓库移动到新创建的存储。您可以按分片或按组移动它们,这使您有机会将它们分布在多个 Gitaly 服务器上。
部署 Gitaly 集群 (Praefect) 之前
Gitaly 集群 (Praefect) 提供容错能力的好处,但带来了设置和管理的额外复杂性。在部署 Gitaly 集群 (Praefect) 之前,请参见:
如果您尚未迁移到 Gitaly 集群 (Praefect),您有两个选择:
- 分片的 Gitaly 实例。
- Gitaly 集群 (Praefect)。
如果您有任何问题,请联系您的客户成功经理或客户支持。
已知问题
下表概述了当前影响 Gitaly 集群 (Praefect) 使用的已知问题。有关这些问题的当前状态,请参考引用的问题和 epic。
| 问题 | 摘要 | 如何避免 |
|---|---|---|
| Gitaly 集群 (Praefect) + Geo - 重试失败同步的问题 | 如果在 Geo 辅助站点上使用 Gitaly 集群 (Praefect),未能同步的仓库在 Geo 尝试重新同步时可能会继续失败。从此状态恢复需要支持团队协助执行手动步骤。 | 在 GitLab 15.0 到 15.2 中,在您的 Geo 主站点上启用 gitaly_praefect_generated_replica_paths 功能标志。在 GitLab 15.3 中,该功能标志默认启用。 |
| 由于升级后未应用迁移,Praefect 无法向数据库插入数据 | 如果数据库未通过已完成的迁移保持最新,则 Praefect 节点无法执行标准操作。 | 确保 Praefect 数据库已启动并运行,所有迁移已完成(例如:sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status 应显示所有已应用迁移的列表)。考虑请求升级协助,以便支持团队可以审查您的升级计划。 |
| 在运行集群中从快照恢复 Gitaly 集群 (Praefect) 节点 | 因为 Gitaly 集群 (Praefect) 以一致状态运行,引入一个落后的单个节点会导致集群无法协调节点数据和其他节点数据 | 不要从备份快照恢复单个 Gitaly 集群 (Praefect) 节点。如果必须从备份恢复: 1. 关闭 GitLab。 2. 同时快照所有 Gitaly 集群 (Praefect) 节点。 3. 获取 Praefect 数据库的数据库转储。 |
| 在 Kubernetes、Amazon ECS 或类似环境中运行时的限制 | 不支持 Gitaly 集群 (Praefect),Gitaly 有已知的限制。更多信息,请参见 epic 6127。 | 使用我们的参考架构。 |
快照备份和恢复
Gitaly 集群 (Praefect) 不支持快照备份。快照备份可能导致 Praefect 数据库与磁盘存储不同步的问题。由于 Praefect 在恢复期间重建 Gitaly 磁盘信息的复制元数据的方式,您应该使用官方备份和恢复 Rake 任务。
增量备份方法可用于加速 Gitaly 集群 (Praefect) 备份。
如果您无法使用任一方法,请联系客户支持以获取恢复帮助。
如果您在使用 Gitaly 集群 (Praefect) 时遇到问题或限制该怎么办
联系客户支持以立即获得恢复或恢复的帮助。