并发限制

为了避免压垮运行 Gitaly 的服务器,您可以限制以下内容的并发性:

  • RPC。
  • Pack 对象。

这些限制可以是固定的,也可以设置为自适应的。

caution在您的环境中启用限制时应谨慎行事,仅在特定情况下启用,例如保护意外流量。当达到限制时,确实会导致断开连接,对用户产生负面影响。为了获得一致且稳定的性能,您应该首先探索其他选项,例如调整节点规格和审查大型存储库或工作负载。

限制 RPC 并发性

在克隆或拉取存储库时,各种 RPC 在后台运行。特别是,Git pack RPC:

  • SSHUploadPackWithSidechannel(用于 Git SSH)。
  • PostUploadPackWithSidechannel(用于 Git HTTP)。

这些 RPC 可能会消耗大量资源,在以下情况下可能会产生显著影响:

  • 意外的高流量。
  • 运行在不遵循最佳实践的大型存储库上。

您可以通过在 Gitaly 配置文件中使用并发限制来防止这些进程压垮您的 Gitaly 服务器。例如:

# 在 /etc/gitlab/gitlab.rb 中
gitaly['configuration'] = {
   # ...
   concurrency: [
      {
         rpc: '/gitaly.SmartHTTPService/PostUploadPackWithSidechannel',
         max_per_repo: 20,
         max_queue_wait: '1s',
         max_queue_size: 10,
      },
      {
         rpc: '/gitaly.SSHService/SSHUploadPackWithSidechannel',
         max_per_repo: 20,
         max_queue_wait: '1s',
         max_queue_size: 10,
      },
   ],
}
  • rpc 是要为每个存储库设置并发限制的 RPC 的名称。
  • max_per_repo 是每个存储库中给定 RPC 的最大并发 RPC 调用数。
  • max_queue_wait 是请求在并发队列中等待被 Gitaly 接收的最大时间。
  • max_queue_size 是并发队列(每个 RPC 方法)的最大大小,在达到此大小之前请求会被 Gitaly 拒绝。

这限制了给定 RPC 的并发 RPC 调用数。限制是按存储库应用的。在上面的示例中:

  • 由 Gitaly 服务器服务的每个存储库最多可以有 20 个同时进行的 PostUploadPackWithSidechannelSSHUploadPackWithSidechannel RPC 调用。
  • 如果另一个请求进入一个已经使用了 20 个插槽的存储库,该请求将被排队。
  • 如果请求在队列中等待超过 1 秒,它将被拒绝并出现错误。
  • 如果队列增长超过 10,后续请求将被拒绝并出现错误。
note当达到这些限制时,用户将被断开连接。

您可以使用 Gitaly 日志和 Prometheus 观察此队列的行为。有关更多信息,请参阅相关文档

限制 pack-objects 并发性

  • 引入于极狐GitLab 15.11,使用名为 gitaly_pack_objects_limiting_remote_ip 的功能标志。默认禁用。
  • 在极狐GitLab 16.0 中 GA。功能标志 gitaly_pack_objects_limiting_remote_ip 被移除。

Gitaly 在处理 SSH 和 HTTPS 流量以克隆或拉取存储库时触发 git-pack-objects 进程。这些进程生成一个 pack-file 并可能消耗大量资源,特别是在意外的高流量或从大型存储库并发拉取时。在 GitLab.com 上,我们还观察到与互联网连接速度较慢的客户端有关的问题。

您可以通过在 Gitaly 配置文件中设置 pack-objects 并发限制来防止这些进程压垮您的 Gitaly 服务器。此设置限制每个远程 IP 地址的在途 pack-object 进程数。

caution仅在特定情况下谨慎启用这些限制,例如保护意外流量。当达到这些限制时,用户将被断开连接。为了获得一致且稳定的性能,您应该首先探索其他选项,例如调整节点规格和审查大型存储库或工作负载。

示例配置:

# 在 /etc/gitlab/gitlab.rb 中
gitaly['pack_objects_limiting'] = {
   'max_concurrency' => 15,
   'max_queue_length' => 200,
   'max_queue_wait' => '60s',
}
  • max_concurrency 是每个键的最大在途 pack-object 进程数。
  • max_queue_length 是并发队列(每个键)的最大大小,在达到此大小之前请求会被 Gitaly 拒绝。
  • max_queue_wait 是请求在并发队列中等待被 Gitaly 接收的最大时间。

在上面的示例中:

  • 每个远程 IP 在一个 Gitaly 节点上最多可以有 15 个同时进行的 pack-object 进程。
  • 如果另一个请求来自一个已经使用了 15 个插槽的 IP,该请求将被排队。
  • 如果请求在队列中等待超过 1 分钟,它将被拒绝并出现错误。
  • 如果队列增长超过 200,后续请求将被拒绝并出现错误。

当启用 pack-object 缓存时,pack-objects 限制仅在缓存未命中时生效。有关更多信息,请参阅 Pack-objects 缓存

您可以使用 Gitaly 日志和 Prometheus 观察此队列的行为。有关更多信息,请参阅 监控 Gitaly pack-objects 并发限制

自适应并发限制

  • 引入于极狐GitLab 16.6。

Gitaly 支持两种并发限制:

  • RPC 并发限制,允许您为每个 Gitaly RPC 配置最大并发请求数。限制是按 RPC 和存储库作用域的。
  • Pack-objects 并发限制,限制每个 IP 的并发 Git 数据传输请求数。

如果超过此限制,则:

  • 请求被放入队列中。
  • 如果队列已满或请求在队列中停留时间过长,请求将被拒绝。

这两种并发限制都可以静态配置。虽然静态限制可以产生良好的保护效果,但它们也有一些缺点:

  • 静态限制不适用于所有使用模式。没有一刀切的值。如果限制太低,大型存储库会受到负面影响。如果限制太高,则保护基本上失效。
  • 维护并发限制的合理值很繁琐,尤其是在每个存储库的工作负载随时间变化时。
  • 即使服务器空闲,请求也可能被拒绝,因为速率没有考虑到服务器的负载。

通过配置自适应并发限制,您可以克服所有这些缺点并保持并发限制的好处。自适应并发限制是可选的,并建立在两种并发限制类型之上。它使用加法增加/乘法减少 (AIMD) 算法。每个自适应限制:

  • 在典型进程运行期间逐渐增加到某个上限。
  • 当主机机器出现资源问题时迅速减少。

此机制为机器提供了一些 “呼吸” 的空间,并加速当前在途请求。

图示显示 Gitaly 自适应并发限制根据系统资源使用情况调整,遵循 AIMD 算法

自适应限制器每 30 秒校准一次限制,并且:

  • 在达到上限之前,每次增加一个限制。
  • 当顶级 cgroup 的内存使用率超过 90%(不包括高度可回收的页面缓存)或 CPU 被限制 50% 或更多观察时间时,减少一半的限制。

否则,限制会增加一个,直到达到上限。

自适应限制是为每个 RPC 或 pack-objects 缓存单独启用的。然而,限制是在同一时间校准的。

启用 RPC 并发性的自适应性

先决条件:

  • 由于自适应限制依赖于控制组,在使用自适应限制之前必须启用控制组。

以下是配置 RPC 并发性自适应限制的示例:

# 在 /etc/gitlab/gitlab.rb 中
gitaly['configuration'] = {
    # ...
    concurrency: [
        {
            rpc: '/gitaly.SmartHTTPService/PostUploadPackWithSidechannel',
            max_queue_wait: '1s',
            max_queue_size: 10,
            adaptive: true,
            min_limit: 10,
            initial_limit: 20,
            max_limit: 40
        },
        {
            rpc: '/gitaly.SSHService/SSHUploadPackWithSidechannel',
            max_queue_wait: '10s',
            max_queue_size: 20,
            adaptive: true,
            min_limit: 10,
            initial_limit: 50,
            max_limit: 100
        },
   ],
}

在此示例中:

  • adaptive 设置是否启用自适应性。如果设置,则忽略 max_per_repo 值,使用以下配置。
  • initial_limit 是极狐GitLab 启动时每个存储库的并发限制。
  • max_limit 是配置 RPC 的最低每个存储库的并发限制。极狐GitLab 增加当前限制,直到达到此数字。
  • min_limit 是配置 RPC 的最低每个存储库的并发限制。当主机机器出现资源问题时,极狐GitLab 快速减少限制,直到达到此值。

有关更多信息,请参阅 RPC 并发性

启用 pack-objects 并发性的自适应性

先决条件:

  • 由于自适应限制依赖于控制组,在使用自适应限制之前必须启用控制组。

以下是配置 pack-objects 并发性自适应限制的示例:

# 在 /etc/gitlab/gitlab.rb 中
gitaly['pack_objects_limiting'] = {
   'max_queue_length' => 200,
   'max_queue_wait' => '60s',
   'adaptive' => true,
   'min_limit' => 10,
   'initial_limit' => 20,
   'max_limit' => 40
}

在此示例中:

  • adaptive 设置是否启用自适应性。如果设置,则忽略 max_concurrency 值,使用以下配置。
  • initial_limit 是极狐GitLab 启动时每个 IP 的并发限制。
  • max_limit 是 pack-objects 的最低每个 IP 并发限制。极狐GitLab 增加当前限制,直到达到此数字。
  • min_limit 是 pack-objects 的最低每个 IP 并发限制。当主机机器出现资源问题时,极狐GitLab 快速减少限制,直到达到此值。

有关更多信息,请参阅 pack-objects 并发性