Geo プロキシ機能
Geoプロキシにより、セカンダリはWorkhorseを通してプライマリへのウェブリクエストをプロキシするため、セカンダリにナビゲートするユーザーには読み書き可能なUIが表示され、プライマリでできるすべてのオペレーションを行うことができます。
リクエストのライフサイクル
トップレベルのビュー
プロキシの相互作用は、以下の図を通して高レベルで説明することができます:
sequenceDiagram
actor client
participant secondary
participant primary
client->>secondary: GET /explore
secondary-->>primary: GET /explore (proxied)
primary-->>secondary: HTTP/1.1 200 OK [..]
secondary->>client: HTTP/1.1 200 OK [..]
プロキシ検出メカニズム
リクエストをプライマリにプロキシすべきかどうか、プライマリのURL(データベースに保存されている)を知るために、WorkhorseはGeoが有効になっているときに内部APIをポーリングします。プロキシが有効な場合、内部APIはプライマリのURLと、リクエストごとにプライマリに渡されるJWT署名データを応答します。
sequenceDiagram
participant W as Workhorse (secondary)
participant API as Internal Rails API
W->API: GET /api/v4/geo/proxy (internal)
loop Poll every 10 seconds
API-->W: {geo_proxy_primary_url, geo_proxy_extra_data}, update config
end
プロキシと比較した詳細なリクエストフローとローカルデータの高速化
実装を詳しく説明すると、セカンダリ(リクエストされた)サイトのWorkhorseはデータをプロキシするかどうかを決定します。データ型を「高速化」できる場合(つまり、往復のリクエストを節約するためにローカルでサービスを提供できる場合)、データを即座に返します。そうでない場合、トラフィックはプライマリの内部URLに送られ、プライマリのWorkhorseによって直接リクエストと同じように処理されます。レスポンスはセカンダリWorkhorseを通して同じ接続でユーザーにプロキシされます。
flowchart LR
A[Client]--->W1["Workhorse (secondary)"]
W1 --> W1C[Serve data locally?]
W1C -- "Yes" ----> W1
W1C -- "No (proxy)" ----> W2["Workhorse (primary)"]
W2 --> W1 ----> A
サインイン
作成者を必要とするプライマリへのプロキシ要求
sequenceDiagram
autoNumber
participant Client
participant Secondary
participant Primary
Client->>Secondary: `/group/project` request
Secondary->>Primary: proxy /group/project
opt primary not signed in
Primary-->>Secondary: 302 redirect
Secondary-->>Client: proxy 302 redirect
Client->>Secondary: /users/sign_in
Secondary->>Primary: proxy /users/sign_in
Note right of Primary: authentication happens, POST to same URL etc
Primary-->>Secondary: 302 redirect
Secondary-->>Client: proxy 302 redirect
Client->>Secondary: /group/project
Secondary->>Primary: proxy /group/project
end
Primary-->>Secondary: /group/project logged in response (session on primary created)
Secondary-->>Client: proxy full response
Git pull
歴史的な理由から、Git pull の転送にはpush_from_secondary
パスが使われています。混乱を避けるために、このルートの名前を変更することを提案しているイシューがあります。
Git pull over HTTP(s)
リポジトリの高速化
セカンダリにリポジトリが存在し、プライマリで最新であることを検出した場合、プロキシではなく直接提供します。
sequenceDiagram
participant C as Git client
participant Wsec as "Workhorse (secondary)"
participant Rsec as "Rails (secondary)"
participant Gsec as "Gitaly (secondary)"
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>Rsec: <internal API check>
note over Rsec: decide that the repo is synced and up to date
Rsec-->>Wsec: 401 Unauthorized
Wsec-->>C: <response>
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>Rsec: <internal API check>
Rsec-->>Wsec: Render Workhorse OK
Wsec-->>C: 200 OK
C->>Wsec: POST /foo/bar.git/git-upload-pack
Wsec->>Rsec: GitHttpController#git_receive_pack
Rsec-->>Wsec: Render Workhorse OK
Wsec->>Gsec: Workhorse gets the connection details from Rails, connects to Gitaly: SmartHTTP Service, UploadPack RPC (check the proto for details)
Gsec-->>Wsec: Return a stream of Proto messages
Wsec-->>C: Pipe messages to the Git client
プロキシされたリポジトリ
リクエストされたリポジトリが同期されていなかったり、最新でないことが判明した場合、リクエストはプライマリにプロキシされ、最新バージョンの変更を取得します。
sequenceDiagram
participant C as Git client
participant Wsec as "Workhorse (secondary)"
participant Rsec as "Rails (secondary)"
participant W as "Workhorse (primary)"
participant R as "Rails (primary)"
participant G as "Gitaly (primary)"
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>Rsec: <response>
note over Rsec: decide that the repo is out of date
Rsec-->>Wsec: 302 Redirect to /-/push_from_secondary/2/foo/bar.git/info/refs?service=git-upload-pack
Wsec-->>C: <response>
C->>Wsec: GET /-/push_from_secondary/2/foo/bar.git/info/refs/?service=git-upload-pack
Wsec->>W: <proxied request>
W->>R: <data>
R-->>W: 401 Unauthorized
W-->>Wsec: <proxied response>
Wsec-->>C: <response>
C->>Wsec: GET /-/push_from_secondary/2/foo/bar.git/info/refs/?service=git-upload-pack
note over W: proxied
Wsec->>W: <proxied request>
W->>R: <data>
R-->>W: Render Workhorse OK
W-->>Wsec: <proxied response>
Wsec-->>C: <response>
C->>Wsec: POST /-/push_from_secondary/2/foo/bar.git/git-upload-pack
Wsec->>W: <proxied request>
W->>R: GitHttpController#git_receive_pack
R-->>W: Render Workhorse OK
W->>G: Workhorse gets the connection details from Rails, connects to Gitaly: SmartHTTP Service, UploadPack RPC (check the proto for details)
G-->>W: Return a stream of Proto messages
W-->>Wsec: Pipe messages to the Git client
Wsec-->>C: Return piped messages from Git
SSH経由でのGitプル
SSHオペレーションはWorkhorseの代わりにGitLab Shellを経由するので、Workhorseのリクエストに使われるメカニズムでプロキシされることはありません。SSHオペレーションでは、セカンダリのRails内部APIによってプライマリサイトへのGit HTTPリクエストとしてプロキシされます。
リポジトリの高速化
セカンダリにリポジトリが存在し、プライマリで最新であることを検出した場合、プロキシではなく直接提供します。
sequenceDiagram
participant C as Git client
participant S as GitLab Shell (secondary)
participant I as Internal API (secondary Rails)
participant G as Gitaly (secondary)
C->>S: git pull
S->>I: SSH key validation (api/v4/internal/authorized_keys?key=..)
I-->>S: HTTP/1.1 200 OK
S->>G: InfoRefs:UploadPack RPC
G-->>S: stream Git response back
S-->>C: stream Git response back
C-->>S: stream Git data to push
S->>G: UploadPack RPC
G-->>S: stream Git response back
S-->>C: stream Git response back
プロキシされたリポジトリ
リクエストされたリポジトリが同期されていなかったり、最新でないことが判明した場合、リクエストはプライマリにプロキシされ、最新バージョンの変更を取得します。
sequenceDiagram
participant C as Git client
participant S as GitLab Shell (secondary)
participant I as Internal API (secondary Rails)
participant P as Primary API
C->>S: git pull
S->>I: SSH key validation (api/v4/internal/authorized_keys?key=..)
I-->>S: HTTP/1.1 300 (custom action status) with {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: <response>
S-->>C: return Git response from primary
C-->>S: stream Git data to push
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: <response>
S-->>C: return Git response from primary
Git プッシュ
SSH経由でのGitプッシュ
SSHオペレーションはWorkhorseの代わりにGitLab Shellを経由するので、Workhorseのリクエストに使われるメカニズムでプロキシされることはありません。SSHオペレーションでは、セカンダリのRails内部APIによってプライマリサイトへのGit HTTPリクエストとしてプロキシされます。
sequenceDiagram
participant C as Git client
participant S as GitLab Shell (secondary)
participant I as Internal API (secondary Rails)
participant P as Primary API
C->>S: git push
S->>I: SSH key validation (api/v4/internal/authorized_keys?key=..)
I-->>S: HTTP/1.1 300 (custom action status) with {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: <response>
S-->>C: return Git response from primary
C-->>S: stream Git data to push
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: <response>
S-->>C: return Git response from primary
Git push over HTTP(S)
Git push over HTTP(S) 統一URL
統一 URL を使うと、プッシュは/-/push_from_secondary/$SECONDARY_ID/*
のような書式の内部パスにリダイレクトされます。このパスを経由したリクエストはプライマリにプロキシされ、プライマリがプッシュを処理します。
sequenceDiagram
participant C as Git client
participant Wsec as Workhorse (secondary)
participant W as Workhorse (primary)
participant R as Rails (primary)
participant G as Gitaly (primary)
C->>Wsec: GET /foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>C: 302 Redirect to /-/push_from_secondary/2/foo/bar.git/info/refs?service=git-receive-pack
C->>Wsec: GET /-/push_from_secondary/2/foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>W: <proxied request>
W->>R: <data>
R-->>W: 401 Unauthorized
W-->>Wsec: <proxied response>
Wsec-->>C: <response>
C->>Wsec: GET /-/push_from_secondary/2/foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>W: <proxied request>
W->>R: <data>
R-->>W: Render Workhorse OK
W-->>Wsec: <proxied response>
Wsec-->>C: <response>
C->>Wsec: POST /-/push_from_secondary/2/foo/bar.git/git-receive-pack
Wsec->>W: <proxied request>
W->>R: GitHttpController:git_receive_pack
R-->>W: Render Workhorse OK
W->>G: Get connection details from Rails and connects to SmartHTTP Service, ReceivePack RPC
G-->>W: Return a stream of Proto messages
W-->>Wsec: Pipe messages to the Git client
Wsec-->>C: Return piped messages from Git
Git push over HTTP(S) with separate URLs
個別のURLの場合、セカンダリは$PRIMARY/-/push_from_secondary/$SECONDARY_ID/*
のような書式のURLにリダイレクトされます。
sequenceDiagram
participant Wsec as Workhorse (secondary)
participant C as Git client
participant W as Workhorse (primary)
participant R as Rails (primary)
participant G as Gitaly (primary)
C->>Wsec: GET $SECONDARY/foo/bar.git/info/refs/?service=git-receive-pack
Wsec->>C: 302 Redirect to $PRIMARY/-/push_from_secondary/2/foo/bar.git/info/refs?service=git-receive-pack
C->>W: GET $PRIMARY/-/push_from_secondary/2/foo/bar.git/info/refs/?service=git-receive-pack
W->>R: <data>
R-->>W: 401 Unauthorized
W-->>C: <response>
C->>W: GET /-/push_from_secondary/2/foo/bar.git/info/refs/?service=git-receive-pack
W->>R: <data>
R-->>W: Render Workhorse OK
W-->>C: <response>
C->>W: POST /-/push_from_secondary/2/foo/bar.git/git-receive-pack
W->>R: GitHttpController:git_receive_pack
R-->>W: Render Workhorse OK
W->>G: Get connection details from Rails and connects to SmartHTTP Service, ReceivePack RPC
G-->>W: Return a stream of Proto messages
W-->>C: Pipe messages to the Git client