Gitaly 和 Gitaly 集群

  • Tier: 基础版, 专业版, 旗舰版
  • Offering: 私有化部署

Gitaly 提供对 Git 仓库的高级 RPC 访问。它被极狐GitLab 用于读取和写入 Git 数据。

Gitaly 存在于每个极狐GitLab 安装中,并协调 Git 仓库的存储和检索。Gitaly 可以是:

  • 在单个实例的 Linux 软件包安装上运行的后台服务(所有极狐GitLab 在一台机器上)。
  • 根据扩展和可用性要求,分离到其自己的实例并配置为完整的集群配置。

Gitaly 实现了客户端-服务器架构:

Gitaly 仅管理极狐GitLab 的 Git 仓库访问。其他类型的极狐GitLab 数据不通过 Gitaly 访问。

极狐GitLab 通过配置的仓库存储访问仓库。根据其配置权重,每个新仓库都存储在其中一个仓库存储上。每个仓库存储要么是:

  • 使用存储路径直接访问仓库的 Gitaly 存储,其中每个仓库存储在单个 Gitaly 节点上。所有请求都路由到该节点。
  • Gitaly Cluster提供的虚拟存储,每个仓库可以存储在多个 Gitaly 节点上以提高容错性。在 Gitaly 集群中:
    • 读取请求分布在多个 Gitaly 节点之间,可以提高性能。
    • 写入请求广播到仓库副本。

部署 Gitaly 集群之前#

Gitaly 集群提供了容错的好处,但增加了设置和管理的复杂性。部署 Gitaly 集群之前,请参阅:

如果您尚未迁移到 Gitaly Cluster,您有两个选择:

  • 分片的 Gitaly 实例。
  • Gitaly Cluster。

如果您有任何问题,请联系我们或客户支持。

已知问题#

下表概述了当前影响使用 Gitaly 集群的已知问题。有关这些问题的当前状态,请参考引用的议题和史诗。

议题概述如何避免
Gitaly 集群+ Geo - 议题重试失败的同步如果在 Geo 次级站点上使用 Gitaly Cluster,极狐GitLab 尝试重新同步时,可能会继续失败。恢复此状态需要支持协助以运行手动步骤。在极狐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 集群节点的快照由于 Gitaly 集群运行一致状态,引入落后的单个节点导致集群无法协调节点数据和其他节点数据不要从备份快照中恢复单个 Gitaly 集群节点。如果您必须从备份中恢复:

1. 关闭极狐GitLab
2. 同时快照所有 Gitaly 集群节点。
3. 获取 Praefect 数据库的数据库转储。
在 Kubernetes、Amazon ECS 或类似环境中运行时的限制Praefect (Gitaly Cluster) 不受支持且 Gitaly 有已知限制。有关更多信息,请参阅史诗 6127使用我们的参考架构

快照备份和恢复#

Gitaly 集群不支持快照备份。快照备份可能导致 Praefect 数据库与磁盘存储不同步的问题。由于 Praefect 在恢复期间重建 Gitaly 磁盘信息的复制元数据,您应该使用官方备份和恢复 Rake 任务

可以使用增量备份方法来加速 Gitaly 集群备份。

如果您无法使用任一方法,请联系客户支持以获取恢复帮助。

如果您在 Gitaly 集群上遇到问题或限制该怎么办#

联系客户支持以立即帮助恢复或恢复。

磁盘要求#

Gitaly 和 Gitaly 集群需要快速的本地存储才能有效执行,因为它们是基于 I/O 的重负载进程。因此,我们强烈建议所有 Gitaly 节点使用固态硬盘(SSD)。

这些 SSD 的吞吐量至少应为:

  • 8,000 每秒输入/输出操作 (IOPS) 用于读取操作。
  • 2,000 IOPS 用于写入操作。

这些 IOPS 值是初始建议,可以根据环境的工作负载规模调整为更大或更小的值。如果您在云提供商上运行环境,请参考他们的文档以正确配置 IOPS。

对于仓库数据,仅支持 Gitaly 和 Gitaly 集群的本地存储以提高性能和一致性。替代方案如 NFS基于云的文件系统不受支持。

直接访问仓库#

极狐GitLab 不建议使用 Git 客户端或任何其他工具直接访问存储在磁盘上的 Gitaly 仓库,因为 Gitaly 正在不断改进和变化。这些改进可能会使您的假设失效,导致性能下降、不稳定甚至数据丢失。例如:

  • Gitaly 有优化,例如 info/refs 广告缓存,依赖于 Gitaly 使用官方 gRPC 接口控制和监控对仓库的访问。
  • Gitaly Cluster 有优化,例如容错和分布式读取,依赖于 gRPC 接口和数据库来确定仓库状态。

直接访问 Git 仓库风险自负,不受支持。

Gitaly#

以下显示了极狐GitLab 设置为使用直接访问 Gitaly:

极狐GitLab 应用与 Gitaly 存储分片交互

在这个例子中:

  • 每个仓库存储在三个 Gitaly 存储之一:storage-1storage-2storage-3
  • 每个存储由一个 Gitaly 节点提供服务。
  • 三个 Gitaly 节点在其文件系统上存储数据。

Gitaly 架构#

以下说明了 Gitaly 客户端-服务器架构:

Rendering chart...

配置 Gitaly#

Gitaly 随 Linux 软件包安装预配置,这是适用于最多 20 RPS / 1,000 用户的配置。对于:

极狐GitLab 安装超过 2000 名活跃用户执行每日 Git 写操作可能最适合使用 Gitaly Cluster。

Gitaly CLI#

History
    • gitaly git 子命令在极狐GitLab 17.4 引入。

gitaly 命令是一个命令行接口,为 Gitaly 管理员提供额外的子命令。例如,Gitaly CLI 用于:

有关其他子命令的更多信息,请运行 sudo -u git -- /opt/gitlab/embedded/bin/gitaly --help

备份仓库#

使用极狐GitLab 之外的工具备份或同步仓库时,您必须在复制仓库数据时防止写入

捆绑 URI#

您可以在 Gitaly 中使用 Git 捆绑 URI。有关更多信息,请参阅捆绑 URI 文档

Gitaly Cluster#

Git 存储通过极狐GitLab 中的 Gitaly 服务提供,对极狐GitLab 的操作至关重要。当用户、仓库和活动数量增长时,重要的是通过以下方式适当扩展 Gitaly:

  • 在资源耗尽导致 Git、Gitaly 和极狐GitLab 应用性能下降之前增加 Git 的可用 CPU 和内存资源。
  • 在达到存储限制导致写入操作失败之前增加可用存储。
  • 消除单点故障以提高容错能力。Git 应被视为关键任务,如果服务降级会阻止您将更改部署到生产环境。

Gitaly 可以在集群配置中运行以:

  • 扩展 Gitaly 服务。
  • 增加容错能力。

在这种配置中,每个 Git 仓库可以存储在集群中的多个 Gitaly 节点上。

使用 Gitaly 集群增加容错能力通过:

  • 将写入操作复制到热备用 Gitaly 节点。
  • 检测 Gitaly 节点故障。
  • 自动将 Git 请求路由到可用的 Gitaly 节点。

Gitaly 集群的技术支持仅限于极狐GitLab 专业版和旗舰版客户。

以下显示了极狐GitLab 设置为访问 storage-1,由 Gitaly 集群提供的虚拟存储:

极狐GitLab 应用与虚拟 Gitaly 存储交互,虚拟存储与物理 Gitaly 存储交互

在这个例子中:

  • 仓库存储在一个名为 storage-1 的虚拟存储上。
  • 三个 Gitaly 节点提供 storage-1 访问:gitaly-1gitaly-2gitaly-3
  • 三个 Gitaly 节点在三个单独的哈希存储位置共享数据。
  • 复制因子3。维护每个仓库的三个副本。

假设单节点故障的 Gitaly 集群可用性目标为:

  • **恢复点目标 (RPO):**小于 1 分钟。

    写入是异步复制的。任何未复制到新晋升主节点的写入都会丢失。

    强一致性在某些情况下防止丢失。

  • **恢复时间目标 (RTO):**小于 10 秒。 每个 Praefect 节点每秒运行的健康检查检测中断。故障转移需要每个 Praefect 节点的十次连续失败的健康检查。

有关 RPO 和 RTO 的改进建议在史诗 8903 中提出。

如果发生完整集群故障,应执行灾难恢复计划。这可能会影响上述的 RPO 和 RTO。

与 Geo 的比较#

Gitaly 集群和 Geo 都提供冗余。然而:

  • Gitaly 集群为数据存储提供容错性,用户无法察觉。用户不知道何时使用 Gitaly Cluster。
  • Geo 为整个极狐GitLab 实例提供复制灾难恢复。用户知道何时使用 Geo 进行复制。Geo 复制多种数据类型,包括 Git 数据。

下表概述了 Gitaly 集群和 Geo 的主要区别:

工具节点位置延迟容忍度故障转移一致性提供冗余的对象
Gitaly 集群多个单个小于 1 秒,理想情况下为个位数毫秒自动强一致性Git 数据存储
Geo多个多个达到一分钟手动最终整个极狐GitLab 实例

有关更多信息,请参阅:

虚拟存储#

虚拟存储使极狐GitLab 的单个仓库存储成为可能,以简化仓库管理。

通常,使用 Gitaly 集群的虚拟存储可以替代直接 Gitaly 存储配置。然而,这需要额外的存储空间来将每个仓库存储在多个 Gitaly 节点上。使用 Gitaly 集群虚拟存储而非直接 Gitaly 存储的好处是:

  • 提高容错性,因为每个 Gitaly 节点都有每个仓库的副本。
  • 改善资源利用率,减少为特定分片峰值负载过度配置的需求,因为读取负载在 Gitaly 节点之间分布。
  • 不需要手动重新平衡性能,因为读取负载在 Gitaly 节点之间分布。
  • 简化管理,因为所有 Gitaly 节点都是相同的。

可以使用复制因子配置仓库副本的数量。

对于极大的极狐GitLab 实例,具有相同复制因子的配置可能是不经济的。

与标准 Gitaly 存储一样,虚拟存储可以被分片。

存储布局#

存储布局是 Gitaly 集群的内部细节,不能保证在发行版之间保持稳定。此处的信息仅供参考,并帮助调试。直接在磁盘上的仓库进行更改不受支持,可能导致损坏或更改被覆盖。

Gitaly 集群的虚拟存储提供了一个看似单一的存储,但实际上由多个物理存储组成。Gitaly 集群必须将每个操作复制到每个物理存储。操作可能在某些物理存储上成功,但在其他存储上失败。

部分应用的操作可能导致其他操作出现问题,并使系统处于无法恢复的状态。为了避免这些问题,每个操作应该完全应用或完全不应用。这种操作特性称为原子性。

极狐GitLab 控制仓库存储上的存储布局。极狐GitLab 指示仓库存储在哪里创建、删除和移动仓库。这些操作在应用于多个物理存储时会产生原子性问题。例如:

  • 极狐GitLab 删除一个仓库,而其副本之一不可用。
  • 极狐GitLab 稍后重新创建该仓库。

结果是,不可用时的陈旧副本可能导致冲突并阻止仓库的重新创建。

这些原子性问题在过去导致了多个问题,包括:

  • Geo 同步到具有 Gitaly 集群的次级站点。
  • 备份恢复。
  • 仓库在仓库存储之间移动。

Gitaly 集群通过在磁盘上以特殊布局存储仓库来提供这些操作的原子性,以防止由于部分应用的操作可能导致的冲突。

客户端生成的副本路径#

仓库存储在由Gitaly 客户端确定的相对路径的存储中。这些路径可以通过它们不以 @cluster 前缀开头来识别。相对路径遵循哈希存储模式。

Praefect 生成的副本路径#

History
    • 在极狐GitLab 15.0 引入,使用名为 gitaly_praefect_generated_replica_paths 的标志。默认禁用。
    • 在极狐GitLab 15.2 在 JihuLab.com 上启用。
    • 在极狐GitLab 15.3 在极狐GitLab 私有化部署上启用。
    • 在极狐GitLab 15.6 GA。功能标志 gitaly_praefect_generated_replica_paths 被移除。

当 Gitaly 集群创建仓库时,它为仓库分配一个称为 仓库 ID 的唯一永久 ID。仓库 ID 是 Gitaly 集群的内部 ID,与极狐GitLab 中的任何其他 ID 无关。如果仓库从 Gitaly 集群中删除并稍后迁移回来,则仓库会被分配一个新的仓库 ID,并且从 Gitaly 集群的角度来看是不同的仓库。仓库 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,因此前四位是 6f96

磁盘上的仓库识别#

使用 praefect metadata 子命令来:

  • 从元数据存储中检索仓库的虚拟存储和相对路径。获得哈希存储路径后,您可以使用 Rails 控制台检索项目路径。
  • 使用以下任一方法查找仓库在集群中的存储位置:
    • 虚拟存储和相对路径。
    • 仓库 ID。

磁盘上的仓库还包含项目路径在 Git 配置文件中。即使仓库的元数据已被删除,配置文件也可用于确定项目路径。请按照哈希存储文档中的说明

操作的原子性#

Gitaly 集群使用 PostgreSQL 元数据存储和存储布局来确保仓库创建、删除和移动操作的原子性。这些磁盘操作不能跨多个存储原子地应用。然而,PostgreSQL 保证了元数据操作的原子性。Gitaly Gitaly 集群将操作建模为失败的操作始终保持元数据一致。即使成功操作后磁盘可能包含陈旧状态。这种情况是预期的,剩余状态不会干扰未来的操作,但可能会在进行清理之前不必要地占用磁盘空间。

正在进行后台爬虫的工作,用于从存储中清理剩余的仓库。

仓库创建#

创建仓库时,Praefect:

  1. 从 PostgreSQL 保留一个仓库 ID,这是原子的,没有两个创建收到相同的 ID。
  2. 在 Gitaly 存储中创建副本,位于从仓库 ID 派生的副本路径。
  3. 在磁盘上成功创建仓库后创建元数据记录。

即使两个并发操作创建相同的仓库,它们也会被存储在存储中的不同目录中,并且不会冲突。第一个完成的创建元数据记录,另一个操作失败并出现“已存在”错误。失败的创建在存储中留下剩余仓库。正在进行后台爬虫的工作,用于从存储中清理剩余仓库。

仓库 ID 是从 PostgreSQL 中的 repositories_repository_id_seq 生成的。在上述例子中,失败的操作使用了一个仓库 ID 而没有成功创建仓库。失败的仓库创建预期会导致仓库 ID 中的间隙。

仓库删除#

通过删除其元数据记录来删除仓库。一旦元数据记录被删除,仓库就不再逻辑存在。PostgreSQL 保证了删除的原子性,并且并发删除失败并出现“未找到”错误。在成功删除元数据记录后,Praefect 尝试从存储中删除副本。这可能会失败并留下存储中的剩余状态。剩余状态最终会被清理。

仓库移动#

与 Gitaly 不同,Gitaly 集群不会在存储中移动仓库,而只是通过更新元数据存储中的仓库相对路径来虚拟移动仓库。

组件#

Gitaly 集群包含多个组件:

  • 负载均衡器用于分配请求并提供容错访问到 Praefect 节点。
  • Praefect 节点用于管理集群并将请求路由到 Gitaly 节点。
  • PostgreSQL 数据库用于持久化集群元数据,PgBouncer推荐用于池化 Praefect 的数据库连接。
  • Gitaly 节点提供仓库存储和 Git 访问。

架构#

Praefect 是 Gitaly 的路由器和事务管理器,是运行 Gitaly 集群的必需组件。

Praefect 将传入连接分配到 Gitaly 集群节点

有关更多信息,请参阅Gitaly 高可用性 (HA) 设计

功能#

Gitaly 集群提供以下功能:

分布式读取#

Gitaly 集群支持在配置为虚拟存储的 Gitaly 节点之间分布读取操作。

所有使用 ACCESSOR 选项标记的 RPC 都会重定向到最新且健康的 Gitaly 节点。例如,GetBlob

在此上下文中,_最新_意味着:

  • 没有为此 Gitaly 节点安排的复制操作。
  • 最后一次复制操作处于_完成_状态。

如果发生以下情况,则选择主节点来处理请求:

  • 不存在最新节点。
  • 在节点选择期间发生任何其他错误。

如果您有一个大型、频繁修改的仓库(如多千兆字节的单仓库),并且更改比 Praefect 可以复制到次级节点更快到来,则主节点可以服务大部分或所有请求。当发生这种情况时,CI/CD 作业和其他仓库流量被主节点的容量限制。

您可以使用 Prometheus 监控分布读取

强一致性#

Gitaly 集群通过将更改同步写入所有健康、最新的副本来提供强一致性。如果副本在事务时过时或不健康,则写入会异步复制到副本。

强一致性是主要的复制方法。操作的子集仍使用复制作业(最终一致性)而不是强一致性。

如果强一致性不可用,Gitaly 集群保证最终一致性。在这种情况下,Gitaly 集群在写入主 Gitaly 节点后将所有写入复制到次级 Gitaly 节点。

有关监控强一致性的信息,请参阅 Gitaly 集群的Prometheus 指标文档

复制因子#

复制因子是 Gitaly 集群维护给定仓库的副本数量。更高的复制因子:

  • 提供更好的冗余和读取工作负载分布。
  • 导致更高的存储成本。

默认情况下,Gitaly 集群将仓库复制到虚拟存储中的每个存储。

有关配置信息,请参阅配置复制因子

配置 Gitaly 集群#

有关配置 Gitaly 集群 的更多信息,请参阅配置 Gitaly 集群

升级 Gitaly 集群#

要升级 Gitaly 集群,请按照文档进行零停机时间升级

降级 Gitaly 集群到以前版本#

如果需要将 Gitaly 集群回滚到较早版本,可能需要恢复一些 Praefect 数据库迁移。

要降级 Gitaly 集群(假设多个 Praefect 节点):

  1. 在所有 Praefect 节点上停止 Praefect 服务:

    shell
    gitlab-ctl stop praefect
  2. 将 GitLab 软件包降级到旧版本之一的 Praefect 节点。

  3. 在降级的节点上检查 Praefect 迁移的状态:

    shell
    sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status
  4. 计算 APPLIED 列中 unknown migration 的迁移数。

  5. 在一个未降级的 Praefect 节点上,执行回滚的试运行以验证要恢复的迁移。<CT_UNKNOWN> 是降级节点报告的未知迁移数量。

    shell
    sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate <CT_UNKNOWN>
  6. 如果结果看起来正确,请使用 -f 选项运行相同命令以恢复迁移:

    shell
    sudo -u git -- /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate -f <CT_UNKNOWN>
  7. 降级剩余的 Praefect 节点上的 GitLab 软件包并重新启动 Praefect 服务:

    shell
    gitlab-ctl start praefect

迁移到 Gitaly 集群#

Gitaly 集群存在一些已知问题。在继续之前,请查看以下信息。

迁移到 Gitaly 集群之前:

要迁移到 Gitaly 集群:

  1. 创建所需的存储。参考仓库存储建议
  2. 创建并配置Gitaly 集群
  3. 如果尚未配置,请配置现有的 Gitaly 实例以使用 TCP
  4. 移动仓库。要迁移到 Gitaly 集群,存储在 Gitaly 集群之外的现有仓库必须被移动。没有自动迁移,但可以通过极狐GitLab API 调度移动。

即使您不使用 default 仓库存储,也必须确保它已被配置。阅读有关此限制的更多信息

从 Gitaly 集群迁移出去#

如果发现 Gitaly 集群的限制和权衡不适合您的环境,您可以从 Gitaly 集群迁移到分片的 Gitaly 实例:

  1. 创建并配置一个新的Gitaly 服务器
  2. 将仓库移动到新创建的存储。您可以按分片或按群组移动它们,这为您提供了在多个 Gitaly 服务器上分布它们的机会。

转换到 Gitaly 集群#

为了消除复杂性,我们必须在极狐GitLab 中移除直接 Git 访问。然而,只要某些极狐GitLab 安装需要 NFS 上的 Git 仓库,我们就无法移除。

我们努力消除极狐GitLab 中直接 Git 访问的两个方面是:

  • 减少极狐GitLab 发出的低效 Gitaly 查询数量。
  • 说服容错或水平扩展的极狐GitLab 实例的管理员迁移出 NFS。

第二个方面提供了唯一的真正解决方案。为此,我们开发了Gitaly 集群