NuCon mini '22 Spring in ONLINE
Yuichi Watanabe at Nulab Inc.
me := Profile{
Name: "Yuichi Watanabe",
Org: "Nulab Inc.",
Product: "Backlog",
Lang: "Go",
Twitter: "https://twitter.com/vvvatanabe",
GitHub: "https://github.com/vvatanabe",
Slides: "https://slides.com/vvatanabe",
Lives: "Fukuoka",
}
取り扱うデータの特性
システム設計上の留意事項
アーキテクチャの外観
レプリケーションの仕組み
複製と分散の中核となるリバースプロキシ
まとめ
refs (TagやBranch等の参照)
所謂オブジェクトデータベース
# 取り扱うデータの特性
├── branches
├── config
├── description
├── HEAD
├── hooks
├── index
├── info
├── objects
│ ├── 01
│ │ └── a4798bf8e9872b0ae0301657d9908a0d8bd789
│ ├── cb
│ │ └── 311c3d61afbe9f979770be8f999b66e7550ec8
│ ├── d1
│ │ └── 85f2630d528949f038e055d6b35d5954a8a0d5
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
└── tags
└── v1.0.0
取り扱うデータの特性
システム設計上の留意事項
アーキテクチャの外観
レプリケーションの仕組み
複製と分散の中核となるリバースプロキシ
まとめ
NFS、Amazon EFSのようなファイルストレージ
# システム設計上の留意事項
NFSのようなファイルストレージは複数のサーバーからマウントできる
フルマネージドなAmazon EFSなら耐障害性も高く非常に使いやすい
しかしリポジトリの状態によってCPUやディスクIOが跳ね上がることも多々ある
ブロックストレージと比較すると10倍程度遅くなる場合も
Gitホスティングでは安定したパフォーマンスを保ったまま使用するのは難しい
Amazon S3のような外部のオブジェクトストレージ
s3fs(FUSE)等のファイルシステムとしてマウントさせるツールが必要
Gitオブジェクトや参照は一度に大量に送受信されることも多々ある
読み書きするファイルの量と比例して線形に通信コストも増える
ファイルストレージと同じくパフォーマンス面がボトルネックとなる
# システム設計上の留意事項
Amazon EBSのようなブロックストレージ
安定したパフォーマンスを考慮するとブロックストレージが最適
しかし一般的に複数のホストから同じ領域へ安全に書き込みできない
複数のホストから安全に取り使うために物理的に異なるストレージへ複製が必要
# システム設計上の留意事項
# システム設計上の留意事項
# システム設計上の留意事項
取り扱うデータの特性
システム設計上の留意事項
アーキテクチャの外観
レプリケーションの仕組み
複製と分散の中核となるリバースプロキシ
まとめ
Backlogのシステム全体からGitホスティング機能を中心に切り出したアーキテクチャの外観の図
図を簡素化するために、AZ、VPC、サブネット、ロードバランサーなどは省略
実際は各コンポーネントをMulti AZに配備している
# アーキテクチャの外観
BacklogのGitホスティングのリクエストは大きく4つに分類される
処理を担当するコンポーネントも分割されている
Webブラウザからのリクエスト
Backlog APIからのリクエスト
GitクライアントからのHTTPSリクエスト
GitクライアントからのSSHリクエスト
システムの前段に配備されるのコンポーネントはストレージを持たない(ステートレス)
リクエストの特性に合わせた認証・認可を行う
後段のコンポーネントにRPCで接続してデータの読み書きを行う
# アーキテクチャの外観
# アーキテクチャの外観
システムの後段に配備されるコンポーネントのgit-rpcのみストレージを持つ(ステートフル)
フロントエンドからのRPCを受け取り実際にGitリポジトリへ読み書きを行う
Gitリポジトリの読み書きに特化したデータベースミドルウェアのようなもの
EC2で稼働しておりEBSをマウントしている
Active/ActiveなPrimary/Replica構成
ReplicaはPrimaryを正とした複製
Write系のRPCはPrimaryで処理する
Read系のRPCはPrimaryとReplicaで処理する
# アーキテクチャの外観
中央に配備されているgit-proxyはフロントエンドからの全てのRPCを受け取りバックエンドへ中継する
レプリケーションにおいて中心となるコンポーネント ※ 詳細は後述
図の全てのコンポーネントは全てgRPCで通信する
gRPCは4種類の通信方式をサポートしている
Server Streaming RPC
Client Streaming RPC
Bidirectional Streaming RPC
Unary RPC
これらはGit特有のワークロードを効率化するのに非常に適している
# アーキテクチャの外観
# アーキテクチャの外観
大容量のデータ読み込み
例)git clone、git pull、git fetch、 ファイルのダウンロード
サーバーが一度に送信するデータを抑えたい
複数のレスポンスに分割したい
gRPCのServer Streaming RPCが適している
# アーキテクチャの外観
大容量のデータの書き込み
例)git push
サーバーが一度に受信するデータを抑えたい
複数のリクエストに分割したい
gRPCのClient Streaming RPCが適している
# アーキテクチャの外観
その他小さなデータのやり取り
例)コミット、ブランチ、タグ等の比較的小さなデータの取得
リクエストやレスポンスを分割する必要はない
gRPCのUnary RPCが適している
取り扱うデータの特性
システム設計上の留意事項
アーキテクチャの外観
レプリケーションの仕組み
複製と分散の中核となるリバースプロキシ
まとめ
どのようにリポジトリを複製しているのか?
俗に言う非同期レプリケーション方式
git-proxyは書き込み処理を中継する直前にレプリケーションログ(※1)をAmzon S3へ保存する
ログを保存した直後や変更した直後に別のアプリケーション(※2)からそのデータにアクセスしても常に最新の状態を取得できる必要がある
Amazon S3はStrong Consistency(強い一貫性)をサポートしており、この要件を満たしているので、一連のレプリケーションプロセスを円滑に実施できる
# レプリケーションの仕組み
※1 リポジトリを複製するために必要な情報を記述したログ
※2 git-replication-workerや冗長化した複数のgit-proxyから参照される
書き込み処理が失敗した場合は不要になったAmazon S3上のレプリケーションログを削除する
# レプリケーションの仕組み
レプリケーションログはリポジトリ単位で発行される
ログの種類は書き込みの特性に応じて複数のイベントに分類される
レプリケーションの実行は例のように実行順序を守る必要がある
例)
リポジトリの作成
リポジトリの書き込み
リポジトリのリネーム
リポジトリの削除
# レプリケーションの仕組み
エンキューするメッセージにグループIDを付与することで同一グループIDのメッセージの配信順序が保証される
# レプリケーションの仕組み
git-replication-workerはコンシューマ(ワーカー)としてAmazon SQSをポーリングしている
AWS SDK for Goをベースにして実装しており、goroutineとchannelを使い同時処理数を制御しています。
単独のECSタスクで並列にメッセージを捌く
git-replication-worker自体も冗長化している
取得したメッセージに含まれるレプリケーションログのキーを元にAmazon S3上のレプリケーションログの実態を取得する
# レプリケーションの仕組み
レプリケーションログはリポジトリの情報やレプリケーションの種別を持つ
ログの情報をもとにgit-rpcのレプリカに対してレプリケーション用の適切なRPCを実行する
レプリケーション用のRPCが成功した場合
レプリケーションログを消化
Amazon S3とAmazon SQSからログとメッセージを削除
失敗した場合
リトライ
# レプリケーションの仕組み
git-rpcが提供するレプリケーション用のRPCはレプリケーションの種別毎に提供している
それらは全てリトライされることを想定しており、何度も実行されても問題ないように冪等な作りになっている
例)Gitオブジェクトや参照の複製
レプリカからプライマリにGitのサブコマンドのgit fetchを実行
git fetchのトランザクションにより予期しないエラー発生時もデータの一貫性を保つ
git fetch自体が冪等なので複数回実行しても差分のみ処理できる
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
# レプリケーションの仕組み
取り扱うデータの特性
システム設計上の留意事項
アーキテクチャの外観
レプリケーションの仕組み
複製と分散の中核となるリバースプロキシ
まとめ
git-proxyはL7のリバースプロキシ
gRPCのリクエストの属性やリポジトリの状態にあわせて動的に分散させる
対象リポジトリの複製が完了しているか判定
リクエストの属性の判定
特定のリポジトリグループごとに振り分け先のクラスタ(※1)を指定するカスタムルーティング
書き込みの種類によって適切なレプリケーションログを発行する
Amazon S3へログの送信
Amazon SQSへメッセージを通知
# 複製と分散の中核となるリバースプロキシ
※1 ストレージを持つPrimaryとReplicaの集合
複製と分散の機能が一つのアプリケーションに集約されているためgit-proxyそのものをシステムから着脱可能
ローカルマシンで開発する場合や、エンタープライズ版でスタンドアロンなサーバーにインストールする場合など、冗長化機能が不要なケースを考慮
# 複製と分散の中核となるリバースプロキシ
取り扱うデータの特性
システム設計上の留意事項
アーキテクチャの外観
レプリケーションの仕組み
複製と分散の中核となるリバースプロキシ
まとめ
リポジトリという名のオブジェクトデータベースをブロックストレージに保持している
リクエストの特性に応じて処理するコンポーネントを分割している
ストレージを持つコンポーネントはバックエンドに集約、Active/ActiveなPrimary/Replica構成で冗長化している
全てのコンポーネントはgRPCで繋がり、リクエストの特性に応じて通信方式を選択している
中核となる動的なリバースプロキシが複製と分散の舵を取っている
PrimaryとReplicaをリポジトリ単位で非同期にレプリケーションしている
# まとめ
今後のワークロードの変化によっては、非同期なレプリケーションから同期的なレプリケーションへ手法を変更する可能性もある
所謂3フェーズコミットと呼ばれる分散アルゴリズムを用いて、複数のノードへ同時に書き込みを行う手法などが挙げられる
これからも、プロダクトの成長と共にプロダクトを支える技術も健全に成長させていきたい
# まとめ
NuCon> git commit -m "ご静聴ありがとうございましたʕ◔ϖ◔ʔ"