Help us learn about your current experience with the documentation. Take the survey.

并发限制

为避免压垮运行 Gitaly 的服务器,你可以限制以下操作的并发性:

  • RPC 调用。
  • 打包对象(pack objects)。

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

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

限制 RPC 并发

在克隆或拉取仓库时,各种 RPC 在后台运行。特别是 Git 打包 RPC:

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

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

  • 意外的高流量
  • 运行不符合最佳实践的大型仓库

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

# in /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,后续请求将被拒绝并返回错误。

当达到这些限制时,用户会被断开连接。

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

限制 pack-objects 并发

Gitaly 在处理 SSH 和 HTTPS 流量以克隆或拉取仓库时会触发 git-pack-objects 进程。这些进程生成 pack-file 并可能消耗大量资源,特别是在意外高流量或从大型仓库并发拉取的情况下。在 GitLab.com 上,我们还观察到客户端网络连接缓慢的问题。

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

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

示例配置:

# in /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 并发限制

校准并发限制

在设置并发限制时,您应根据特定的工作负载模式选择适当的值。本节提供如何有效校准这些限制的指导。

使用 Prometheus 指标和日志进行校准

Prometheus 指标提供了使用模式的定量洞察,以及每种类型 RPC 对 Gitaly 节点资源的影响。几个关键指标对此分析特别有价值:

  • 每个 RPC 的资源消耗指标。Gitaly 将大部分繁重操作卸载到 git 进程,因此通常调用的命令是 Git 二进制文件。Gitaly 将从这些命令收集的指标暴露为日志和 Prometheus 指标。
    • gitaly_command_cpu_seconds_total - 通过 shell 调用花费的 CPU 时间总和,带有 grpc_servicegrpc_methodcmdsubcmd 标签。
    • gitaly_command_real_seconds_total - 通过 shell 调用花费的实际时间总和,带有类似标签。
  • 每个 RPC 的最近限制指标:
    • gitaly_concurrency_limiting_in_progress - 正在处理的并发请求数量。
    • gitaly_concurrency_limiting_queued - 给定仓库的 RPC 处于等待状态的请求数量。
    • gitaly_concurrency_limiting_acquiring_seconds - 请求因并发限制而等待的处理时间。

这些指标提供了特定时间点的资源利用情况高级视图。gitaly_command_cpu_seconds_total 指标对于识别消耗大量 CPU 资源的特定 RPC 特别有效。如 监控 Gitaly 中所述,还有更多指标可用于更详细的分析。

虽然指标捕获了整体资源使用模式,但它们通常不提供每个仓库的细分。因此,日志作为补充数据源。要分析日志:

  1. 根据已识别的高影响 RPC 过滤日志。
  2. 按仓库或项目聚合过滤后的日志。
  3. 在时间序列图上可视化聚合结果。

这种结合使用指标和日志的方法提供了系统级资源使用和仓库特定模式的全面可见性。Kibana 或类似的日志聚合平台等分析工具可以促进此过程。

调整限制

如果您发现初始限制不够高效,可能需要调整它们。使用自适应限制,精确限制不那么重要,因为系统会根据资源使用情况自动调整。

请记住,并发限制按仓库范围应用。30 的限制意味着每个仓库最多允许 30 个并行的进行中请求。如果达到限制,请求将被排队,仅当队列已满或达到最大等待时间时才会被拒绝。

自适应并发限制

Gitaly 支持两种并发限制:

  • RPC 并发限制,允许您为每个 Gitaly RPC 配置最大并行进行中请求数量。限制按 RPC 和仓库范围应用。
  • Pack-objects 并发限制,按 IP 限制并发 Git 数据传输请求数量。

如果超过此限制,则:

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

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

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

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

  • 在正常运行期间逐渐增加到某个上限。
  • 当主机出现资源问题时快速减少。

这种机制为机器提供了一些"喘息"空间,并加快了当前进行中的请求处理速度。

图表显示 Gitaly 自适应并发限制根据系统资源使用情况通过 AIMD 算法进行调整

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

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

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

自适应限制是单独为每个 RPC 或 pack-objects 缓存启用的。但是,限制同时校准。自适应限制具有以下配置:

  • adaptive 设置是否启用自适应。
  • max_limit 是最大并发限制。Gitaly 增加当前限制,直到达到此数值。这应该是一个系统在典型条件下完全支持的宽松值。
  • min_limit 是配置 RPC 的最小并发限制。当主机出现资源问题时,Gitaly 快速减少限制,直到达到此值。将 min_limit 设置为 0 可能会完全关闭处理,这通常是不希望的。
  • initial_limit 提供了这两个极端之间的合理起点。

为 RPC 并发启用自适应

先决条件:

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

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

# in /etc/gitlab/gitlab.rb
gitaly['configuration'] = {
    # ...
    cgroups: {
        # 启用 cgroups 支持所需的最小配置。
        repositories: {
            count: 1
        },
    },
    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
        },
   ],
}

更多信息请参阅 RPC 并发

为 pack-objects 并发启用自适应

先决条件:

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

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

# in /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
}

更多信息请参阅 pack-objects 并发

校准自适应并发限制

自适应并发限制与 GitLab 保护 Gitaly 资源的常规方式有很大不同。它不依赖于可能过于严格或过于宽松的静态阈值,而是智能地实时响应实际资源条件。

这种方法消除了通过广泛校准寻找"完美"阈值值的需求,如 校准并发限制 中所述。在故障场景中,自适应限制器指数级减少限制(例如,60 → 30 → 15 → 10),然后在系统稳定时通过逐步提高限制来自动恢复。

在校准自适应限制时,您可以优先考虑灵活性而非精确性。

RPC 类别和配置示例

应受保护的高成本 Gitaly RPC 可以分为两种一般类型:

  • 纯 Git 数据操作。
  • 时间敏感的 RPC。

每种类型都有不同的特征,影响如何配置并发限制。以下示例说明了限制配置背后的推理。它们也可以用作起点。

纯 Git 数据操作

这些 RPC 涉及 Git 拉取、推送和获取操作,并具有以下特征:

  • 长时间运行的进程。
  • 大量资源利用。
  • 计算密集型。
  • 不敏感时间。额外的延迟通常是可以接受的。

SmartHTTPServiceSSHService 中的 RPC 属于纯 Git 数据操作类别。配置示例:

{
  rpc: "/gitaly.SmartHTTPService/PostUploadPackWithSidechannel", # 或 `/gitaly.SmartHTTPService/SSHUploadPackWithSidechannel`
  adaptive: true,
  min_limit: 10,  # 即使在极端负载下也要保持的最小并发数
  initial_limit: 40,  # 服务初始化时的起始并发数
  max_limit: 60,  # 理想条件下的最大并发数
  max_queue_wait: "60s",
  max_queue_size: 300
}
时间敏感的 RPC

这些 RPC 为 GitLab 本身和其他具有不同特征的客户端提供服务:

  • 通常是在线 HTTP 请求或 Sidekiq 后台作业的一部分。
  • 较短的延迟配置文件。
  • 通常资源密集度较低。

对于这些 RPC,GitLab 中的超时配置应指导 max_queue_wait 参数。例如,get_tree_entries 在 GitLab 中通常有 30 秒的中等超时:

{
  rpc: "/gitaly.CommitService/GetTreeEntries",
  adaptive: true,
  min_limit: 5,  # 在资源压力下保持的最小吞吐量
  initial_limit: 10,  # 初始并发设置
  max_limit: 20,  # 最佳条件下的最大并发数
  max_queue_size: 50,
  max_queue_wait: "30s"
}

监控自适应限制

要观察自适应限制在生产环境中的行为,请参阅 监控 Gitaly 自适应并发限制 中描述的监控工具和指标。观察自适应限制行为有助于确认限制是否正确响应资源压力并按预期调整。