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

Geo 代理

次节点通过 Workhorse 将几乎所有 HTTP 请求代理到主节点,因此访问次节点的用户可以看到可读写的 UI,并且能够执行在主节点上可以进行的所有操作。

高级组件

GitLab UI 和 API HTTP 请求的代理由 gitlab-workhorse 组件处理。通常发送到 Geo 次节点 Rails 应用的流量会被代理到主 Geo 节点的 内部 URL

Git over HTTP 请求的代理由 gitlab-workhorse 组件处理,但是否代理的决定由 Rails 应用处理,考虑请求是推送还是拉取,以及所需的 Git 数据是否最新。

Git over SSH 流量的代理由 gitlab-workhorse 组件处理,但是否代理的决定由 Rails 应用处理,考虑请求是推送还是拉取,以及所需的 Git 数据是否最新。

请求生命周期

高级视图

代理交互可以通过以下图表在高级层面解释:

sequenceDiagram
actor client
participant secondary
participant primary

client->>secondary: GET /explore
secondary-->>primary: GET /explore (代理)
primary-->>secondary: HTTP/1.1 200 OK [..]
secondary->>client: HTTP/1.1 200 OK [..]

代理检测机制

为了知道是否应该将请求代理到主节点,以及主节点的 URL(存储在数据库中),当 Geo 启用时,Workhorse 会轮询内部 API。当应该启用代理时,内部 API 会返回主节点 URL 和 JWT 签名的数据,这些数据会传递给主节点用于每个请求。

sequenceDiagram
    participant W as Workhorse (次节点)
    participant API as Internal Rails API
    W->API: GET /api/v4/geo/proxy (内部)
    loop 每 10 秒轮询一次
        API-->W: {geo_proxy_primary_url, geo_proxy_extra_data}, 更新配置
    end

与代理相比的本地数据加速的深入请求流程

详细说明实现,次节点(请求)站点上的 Workhorse 决定是否代理数据。如果它可以"加速"数据类型(即可以在本地提供服务以节省往返请求),它会立即返回数据。否则,流量会被发送到主节点的内部 URL,由主节点上的 Workhorse 完全像直接请求一样提供服务。然后响应将通过同一连接中的次节点 Workhorse 代理回用户。

flowchart LR
  A[客户端]--->W1["Workhorse (次节点)"]
  W1 --> W1C[本地提供数据?]
  W1C -- "是" ----> W1
  W1C -- "否 (代理)" ----> W2["Workhorse (主节点)"]
  W2 --> W1 ----> A

登录

需要授权代理到主节点的请求

sequenceDiagram
autoNumber
participant 客户端
participant 次节点
participant 主节点

客户端->>次节点: `/group/project` 请求
次节点->>主节点: 代理 /group/project
opt 主节点未登录
主节点-->>次节点: 302 重定向
次节点-->>客户端: 代理 302 重定向
客户端->>次节点: /users/sign_in
次节点->>主节点: 代理 /users/sign_in
Note right of 主节点: 身份验证发生,POST 到相同 URL 等
主节点-->>次节点: 302 重定向
次节点-->>客户端: 代理 302 重定向
客户端->>次节点: /group/project
次节点->>主节点: 代理 /group/project
end
主节点-->>次节点: /group/project 登录响应(在主节点上创建会话)
次节点-->>客户端: 代理完整响应

Git 拉取

Git 拉取路径在 GitLab 17.10 中从旧名称 push_from_secondary 重命名为 from_secondary 以提高清晰度

Git over HTTP(s)

加速仓库

当仓库存在于次节点且我们检测到与主节点同步时,我们直接提供服务而不是代理。

sequenceDiagram
participant C as Git 客户端
participant Wsec as "Workhorse (次节点)"
participant Rsec as "Rails (次节点)"
participant Gsec as "Gitaly (次节点)"
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>Rsec: <内部 API 检查>
note over Rsec: 决定仓库已同步且最新
Rsec-->>Wsec: 401 未授权
Wsec-->>C: <响应>
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>Rsec: <内部 API 检查>
Rsec-->>Wsec: 渲染 Workhorse OK
Wsec-->>C: 200 OK
C->>Wsec: POST /foo/bar.git/git-upload-pack
Wsec->>Rsec: GitHttpController#git_receive_pack
Rsec-->>Wsec: 渲染 Workhorse OK
Wsec->>Gsec: Workhorse 从 Rails 获取连接详情,连接到 Gitaly: SmartHTTP Service, UploadPack RPC(查看 proto 了解详情)
Gsec-->>Wsec: 返回 Proto 消息流
Wsec-->>C: 将消息管道传输到 Git 客户端

代理仓库

如果请求的仓库未同步,或我们检测到不是最新的,请求将被代理到主节点,以获取最新版本的更改。

sequenceDiagram
participant C as Git 客户端
participant Wsec as "Workhorse (次节点)"
participant Rsec as "Rails (次节点)"
participant W as "Workhorse (主节点)"
participant R as "Rails (主节点)"
participant G as "Gitaly (主节点)"
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>Rsec: <响应>
note over Rsec: 决定仓库已过时
Rsec-->>Wsec: 302 重定向到 /-/from_secondary/2/foo/bar.git/info/refs?service=git-upload-pack
Wsec-->>C: <响应>
C->>Wsec: GET /-/from_secondary/2/foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>W: <代理请求>
W->>R: <数据>
R-->>W: 401 未授权
W-->>Wsec: <代理响应>
Wsec-->>C: <响应>
C->>Wsec: GET /-/from_secondary/2/foo/bar.git/info/refs/?service=git-upload-pack
note over W: 已代理
Wsec->>W: <代理请求>
W->>R: <数据>
R-->>W: 渲染 Workhorse OK
W-->>Wsec: <代理响应>
Wsec-->>C: <响应>
C->>Wsec: POST /-/from_secondary/2/foo/bar.git/git-upload-pack
Wsec->>W: <代理请求>
W->>R: GitHttpController#git_receive_pack
R-->>W: 渲染 Workhorse OK
W->>G: Workhorse 从 Rails 获取连接详情,连接到 Gitaly: SmartHTTP Service, UploadPack RPC(查看 proto 了解详情)
G-->>W: 返回 Proto 消息流
W-->>Wsec: 将消息管道传输到 Git 客户端
Wsec-->>C: 返回来自 Git 的管道消息

Git over SSH

由于 SSH 操作通过 GitLab Shell 而不是 Workhorse 进行,它们不会通过用于 Workhorse 请求的机制代理。对于 SSH 操作,它们被次节点 Rails 内部 API 作为 Git HTTP 请求代理到主站点。

加速仓库

当仓库存在于次节点且我们检测到与主节点同步时,我们直接提供服务而不是代理。

sequenceDiagram
participant C as Git 客户端
participant S as GitLab Shell (次节点)
participant I as Internal API (次节点 Rails)
participant G as Gitaly (次节点)
C->>S: git pull
S->>I: SSH 密钥验证 (api/v4/internal/authorized_keys?key=..)
I-->>S: HTTP/1.1 200 OK
S->>G: InfoRefs:UploadPack RPC
G-->>S: 将 Git 响应流返回
S-->>C: 将 Git 响应流返回
C-->>S: 将 Git 数据流推送
S->>G: UploadPack RPC
G-->>S: 将 Git 响应流返回
S-->>C: 将 Git 响应流返回

代理仓库

如果请求的仓库未同步,或我们检测到不是最新的,请求将被代理到主节点,以获取最新版本的更改。

sequenceDiagram
participant C as Git 客户端
participant S as GitLab Shell (次节点)
participant I as Internal API (次节点 Rails)
participant P as Primary API
C->>S: git pull
S->>I: SSH 密钥验证 (api/v4/internal/authorized_keys?key=..)
I-->>S: HTTP/1.1 300 (自定义操作状态) 包含 {endpoint, msg, primary_repo}
S->>I: POST /api/v4/geo/proxy_git_ssh/info_refs_upload_pack
I->>P: POST $PRIMARY/foo/bar.git/info/refs/?service=git-upload-pack
P-->>I: HTTP/1.1 200 OK
I-->>S: <响应>
S-->>C: 返回来自主节点的 Git 响应
C-->>S: 将 Git 数据流推送
S->>I: POST /api/v4/geo/proxy_git_ssh/upload_pack
I->>P: POST $PRIMARY/foo/bar.git/git-upload-pack
P-->>I: HTTP/1.1 200 OK
I-->>S: <响应>
S-->>C: 返回来自主节点的 Git 响应

Git 推送

Git push over SSH

由于 SSH 操作通过 GitLab Shell 而不是 Workhorse 进行,它们不会通过用于 Workhorse 请求的机制代理。对于 SSH 操作,它们被次节点 Rails 内部 API 作为 Git HTTP 请求代理到主站点。

sequenceDiagram
participant C as Git 客户端
participant S as GitLab Shell (次节点)
participant I as Internal API (次节点 Rails)
participant P as Primary API
C->>S: git push
S->>I: SSH 密钥验证 (api/v4/internal/authorized_keys?key=..)
I-->>S: HTTP/1.1 300 (自定义操作状态) 包含 {endpoint, msg, primary_repo}
S->>I: POST /api/v4/geo/proxy_git_ssh/info_refs_receive_pack
I->>P: POST $PRIMARY/foo/bar.git/info/refs/?service=git-receive-pack
P-->>I: HTTP/1.1 200 OK
I-->>S: <响应>
S-->>C: 返回来自主节点的 Git 响应
C-->>S: 将 Git 数据流推送
S->>I: POST /api/v4/geo/proxy_git_ssh/receive_pack
I->>P: POST $PRIMARY/foo/bar.git/git-receive-pack
P-->>I: HTTP/1.1 200 OK
I-->>S: <响应>
S-->>C: 返回来自主节点的 Git 响应

Git push over HTTP(S)

如果请求的仓库未同步,或我们检测到不是最新的,请求将被代理到主节点,推送会重定向到格式为 /-/from_secondary/$SECONDARY_ID/* 的本地路径。此外,通过此路径的请求会被代理到主节点,主节点将处理推送。

sequenceDiagram
participant C as Git 客户端
participant Wsec as Workhorse (次节点)
participant W as Workhorse (主节点)
participant R as Rails (主节点)
participant G as Gitaly (主节点)
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>C: 302 重定向到 /-/from_secondary/2/foo/bar.git/info/refs?service=git-receive-pack
C->>Wsec: GET /-/from_secondary/2/foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>W: <代理请求>
W->>R: <数据>
R-->>W: 401 未授权
W-->>Wsec: <代理响应>
Wsec-->>C: <响应>
C->>Wsec: GET /-/from_secondary/2/foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>W: <代理请求>
W->>R: <数据>
R-->>W: 渲染 Workhorse OK
W-->>Wsec: <代理响应>
Wsec-->>C: <响应>
C->>Wsec: POST /-/from_secondary/2/foo/bar.git/git-receive-pack
Wsec->>W: <代理请求>
W->>R: GitHttpController:git_receive_pack
R-->>W: 渲染 Workhorse OK
W->>G: 从 Rails 获取连接详情并连接到 SmartHTTP Service, ReceivePack RPC
G-->>W: 返回 Proto 消息流
W-->>Wsec: 将消息管道传输到 Git 客户端
Wsec-->>C: 返回来自 Git 的管道消息