スケーラブルで冗長性のあるGitサーバをどう作るか?

GitBucketでの長年の課題の1つがGitリポジトリのスケールアウトと冗長化でした。Gitリポジトリを格納するストレージに分散ファイルシステムを使うというのは1つの解決策になりますが、分散ファイルシステムの運用はなかなか大変です。もっと手軽に使い始めて徐々にスケールできる方法はないものかと考えていたのですが、1つ思いついたアイデアを試すために実験的にコードを書き始めてみました。

github.com

基本的なアイデアは普通のGitサーバを複数台用意しておき、リポジトリを分散配置するというものです。例によってScalaとJGitを使って実装されています。

f:id:takezoe:20180114032907p:plain

Gitクライアントからのリクエスト(pushやfetch)はフロントコントローラーでルーティングされ、リポジトリの配置されたノードにプロキシされます。リポジトリは全く同じものが複数ノードに存在するので1ノードが落ちても動作を継続することができます。落ちたノードが復旧すると生存しているノードと同期して自ノードのリポジトリを最新化します。

コントローラーは各ノードのディスク使用率を把握しており、新しくリポジトリを作成したり、リポジトリの再配置を行う際はディスク使用率を参照してどのノードに作成するかを決定します。1リポジトリで1ノードのディスクを食い尽くしてしまうような巨大なリポジトリがあると厳しいですが、理論上はディスクが足りなくなってきたら新しいノードを足していくだけでOKということになります。

ちなみに後から知ったのですが、このアプローチはGitHub社のDGit(現在はSpokesに改名されているようです)と似ています。以下のブログに色々と情報が書かれているので参考になります。

githubengineering.com

githubengineering.com

上記のブログからではわからない点として、WebブラウザからのGit操作(ブラウザでファイルを編集したり、プルリクエストをマージしたり等)をどのように実現しているのか?という点があります。

GitBucketではファイルシステム上のbareリポジトリを直接操作しているのですが、この方法ではリモートリポジトリを冗長化するのでそういうわけにはいかなくなります。上記のブログからはSpokesでも同じようにgitクライアントからのリクエストのレベルでプロキシしているように見えるので、Webアプリケーションがローカルリポジトリを持っていてそこをいじってからSpokesにpushしているのかなぁと推測されますが、実際どうなっているのか気になるところです。