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