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 的管道消息