{{< details >}}

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

{{< /details >}}

默认情况下,极狐GitLab Runner 会定期轮询极狐GitLab 实例以获取新的 CI/CD 作业。实际的轮询间隔取决于 runner 配置文件中配置的 check_interval 和 runner 数量

在处理许多 runners 的服务器上,这种轮询可能导致以下性能问题:

  • 排队时间更长。
  • 极狐GitLab 实例上的 CPU 使用率更高。

为了缓解这些问题,您应该启用长轮询。

先决条件:

  • 您必须是管理员。

启用长轮询

您可以配置极狐GitLab 实例,以在 runners 的作业请求中进行长轮询,直到有新作业准备好。

为此,通过配置极狐GitLab Workhorse 的长轮询持续时间 (apiCiLongPollingDuration) 来启用长轮询:

{{< tabs >}}

{{< tab title=”Linux package (Omnibus)” >}}

  1. 编辑 /etc/gitlab/gitlab.rb

    gitlab_workhorse['api_ci_long_polling_duration'] = "50s"
    
  2. 保存文件并重新配置极狐GitLab:

    sudo gitlab-ctl reconfigure
    

{{< /tab >}}

{{< tab title=”Helm chart (Kubernetes)” >}}

使用 gitlab.webservice.workhorse.extraArgs 设置启用长轮询。

  1. 导出 Helm 值:

    helm get values gitlab > gitlab_values.yaml
    
  2. 编辑 gitlab_values.yaml

    gitlab:
      webservice:
        workhorse:
          extraArgs: "-apiCiLongPollingDuration 50s"
    
  3. 保存文件并应用新值:

    helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
    

{{< /tab >}}

{{< tab title=”Docker” >}}

  1. 编辑 docker-compose.yml

    version: "3.6"
    services:
      gitlab:
        image: 'gitlab/gitlab-ee:latest'
        restart: always
        hostname: 'gitlab.example.com'
        environment:
          GITLAB_OMNIBUS_CONFIG: |
            gitlab_workhorse['api_ci_long_polling_duration'] = "50s"
    
  2. 保存文件并重新启动极狐GitLab:

    docker compose up -d
    

{{< /tab >}}

{{< /tabs >}}

指标

启用长轮询后,极狐GitLab Workhorse 会订阅 Redis PubSub 通道并等待通知。当 runner 密钥更改或达到 apiCiLongPollingDuration 时,作业请求将从长轮询中释放。您可以监控以下 Prometheus 指标:

Metric Type Description Labels
gitlab_workhorse_keywatcher_keywatchers Gauge 极狐GitLab Workhorse 正在监视的密钥数量  
gitlab_workhorse_keywatcher_redis_subscriptions Gauge Redis PubSub 订阅的数量  
gitlab_workhorse_keywatcher_total_messages Counter 极狐GitLab Workhorse 在 PubSub 通道上收到的消息总数  
gitlab_workhorse_keywatcher_actions_total Counter 各种密钥监视器操作的计数 action
gitlab_workhorse_keywatcher_received_bytes_total Counter 在 PubSub 通道上收到的总字节数  

长轮询工作流程

下图显示了启用长轮询后单个 runner 如何获取作业:

%%{init: { "fontFamily": "GitLab Sans" }}%% sequenceDiagram accTitle: Long polling workflow accDescr: The flow of a single runner getting a job with long polling enabled autonumber participant C as Runner participant W as Workhorse participant Redis as Redis participant R as Rails participant S as Sidekiq C->>+W: POST /api/v4/jobs/request W->>+Redis: New job for runner A? Redis->>+W: Unknown W->>+R: POST /api/v4/jobs/request R->>+Redis: Runner A: last_update = X R->>W: 204 No job, X-GitLab-Last-Update = X W->>C: 204 No job, X-GitLab-Last-Update = X C->>W: POST /api/v4/jobs/request, X-GitLab-Last-Update: X W->>Redis: Notify when last_update change Note over W: Request held in long poll Note over S: CI job created Note over S, Redis: Update all registered runners S->>Redis: Runner A: last_update = Z Redis->>W: Runner: last_update changed Note over W: Request released from long poll W->>Rails: POST /api/v4/jobs/request Rails->>W: 201 Job was scheduled W->>C: 201 Job was scheduled

在步骤 1 中,当 runner 请求新作业时,它会向极狐GitLab 服务器发出 POST 请求 (/api/v4/jobs/request),该请求首先由 Workhorse 处理。

Workhorse 读取 runner 令牌和 X-GitLab-Last-Update HTTP 头中的值,构造一个密钥,并订阅具有该密钥的 Redis PubSub 通道。如果密钥没有值,则 Workhorse 会立即将请求转发给 Rails(步骤 3 和 4)。

Rails 检查作业队列。如果 runner 没有可用的作业,Rails 会返回一个 204 No job,并将 last_update 令牌返回给 runner(步骤 5 到 7)。

runner 使用该 last_update 令牌,并发出另一个作业请求,在 X-GitLab-Last-Update HTTP 头中填充此令牌。这次,Workhorse 检查 runner 的 last_update 令牌是否已更改。如果没有,Workhorse 会将请求保留到 apiCiLongPollingDuration 指定的持续时间内。

如果用户触发了新的流水线或作业运行,Sidekiq 中的后台任务将更新作业可用的所有 runners 的 last_update 值。runners 可以为项目、群组和/或实例注册。

在步骤 10 和 11 中的这个“滴答”会将作业请求从 Workhorse 长轮询队列中释放,并将请求发送到 Rails(步骤 12)。Rails 寻找可用的作业,并将 runner 分配给该作业(步骤 13 和 14)。

通过长轮询,runner 在新作业可用后会立即收到通知。这不仅有助于提高减少作业排队时间,还减少了服务器开销,因为作业请求仅在有新工作时才会到达 Rails。

故障排除

使用长轮询时,您可能会遇到以下问题。

作业拾取缓慢

长轮询默认未启用,因为在某些 runner 配置中,runner 无法及时拾取作业。

如果 runner config.toml 中的 concurrent 设置值低于定义的 runners 数量,则可能会发生这种情况。要解决此问题,请确保 concurrent 的值等于或大于 runners 的数量。

例如,如果在 config.toml 中有三个 [[runners]] 条目,请确保 concurrent 至少设置为 3。

启用长轮询后,runner:

  1. 启动 concurrent 数量的 Goroutines。
  2. 等待 Goroutines 在长轮询后返回。
  3. 运行另一批请求。

例如,考虑单个 config.toml 配置了:

  • 项目 A 的 3 个 runners。
  • 项目 B 的 1 个 runner。
  • concurrent 设置为 3。

在此示例中,runner 为前三个项目启动 Goroutines。在最坏情况下,runner 等待项目 A 的整个长轮询间隔,然后再继续请求项目 B 的作业。