はじめに
これまで本ブログでは、GitLabをDevSecOpsのための開発プラットフォームとして利用する際に必要となる主要機能(コンテナレジストリやCI/CD)について紹介してきました。今回はその基盤をより安定して運用するために欠かせない冗長化構成について紹介します。
概要
GitLabを構成するコンポーネントとして以下のものがあります:
- GitLab Rails
- UIの提供やAPIの管理を行うGitLabの中核を成すウェブアプリケーションです
- Consul
- GitLabの各コンポーネントのサービスディスカバリーとヘルスチェックを行うコンポーネントです
- Database(PostgreSQL)
- GitLabの主要データ(プロジェクト、ユーザーなど)を保存するデータベースです
- Sidekiq
- GitLabのバックグラウンドジョブを非同期で処理するためのコンポーネントです
- Redis
- GitLabの一時的なデータ(キャッシュ、ジョブキューなど)を保存する高速なインメモリデータストアです
- Gitaly Cluster
- Gitリポジトリの読み書きを行うコンポーネントです
今回は以下のようなGitLabの構成例(例:最大 1000 RPS または 50,000 ユーザー)を参考にして各コンポーネントの冗長化について説明します。

GitLabの構成例(引用:最大 1000 RPS または 50,000 ユーザー)
各コンポーネントの冗長化
GitLab Rails
GitLab RailsはGitLabのアプリケーションサーバーです。想定されるアクセスユーザーに合わせてノードを水平スケールする必要があります。(参考:https://docs.gitlab.com/administration/reference_architectures/)
アプリケーションサーバーは多数のユーザーからのリクエストを処理する必要があります。また、アプリケーションサーバーから内部の各種コンポーネントに多数のリクエストが送られます。そのため、GitLab Railsの冗長化には外部ロードバランサーと内部ロードバランサーが使われます。
外部ロードバランサーはGitLabへアクセスする際の外部からのSSH、HTTPS通信のトラフィックを分散させます。TLS終端をロードバランサーによって行うことが可能です。
内部ロードバランサーはPgBouncerやGitaly Cluster (Praefect)への接続などの内部コンポーネント同士の通信を仲介して、負荷を分散します。
Consul
Consulは各コンポーネントの監視を行い、サービスディスカバリーとヘルスチェックを行うコンポーネントです。GitLabの各コンポーネント同士の接続の手伝いをします。
クォーラム(クラスターが正常に機能する最小台数)を維持するために、3ノード以上の奇数ノードにConsulをデプロイする必要があります。
Database(PostgreSQL)
GitLabでは主要データ(プロジェクト、ユーザーなど)を保存するデータベースとしてPostgreSQLが利用されます。
GitLabのPostgreSQLでは単一障害点を回避するためにプライマリDBとセカンダリDBの2種類が用意されます。プライマリDBは実際に利用されるメインのDBで、セカンダリDBはプライマリの内容をリアルタイムで複製している読み取り専用の予備のDBです。
PgBouncerは各コンポーネントがPostgreSQLのDBに接続を行う際に仲介をして、DB本体への負荷を軽減させるコンポーネントです。PgBouncerがないとGitLabへの接続制限によって、エラーが出たり処理速度が低下します。PgBouncer自身は内部ロードバランサーによって負荷分散されます。
また、PatroniというPostgreSQLのHAクラスタを管理するツールを用いることによってプライマリDBに障害が起きた場合、セカンダリDBをプライマリに昇格する処理が自動的に行われます。
Sidekiq
SidekiqはGitLabの様々なバックグラウンドジョブ(メール送信、CIジョブなど)を処理するコンポーネントです。
Sidekiqのキューにジョブが追加されていくと、あるコンポーネントで処理が低下した場合、連鎖的にすべてのジョブの速度が低下する可能性があります。そのような場合の対策として、以下のようなものがあります。
- 複数インスタンスを立ち上げて処理能力を増やす
- ジョブを小さな単位に分割する
- ジョブのキューを最適化する
上記に示す通りSidekiqの冗長化は単純に水平スケールするだけでなく、ジョブキューの設計を適切に行うことが重要になります。
Sidekiqはログを見ることによってジョブの実行時間や実行回数を視覚的に確認することが可能です。その結果をSidekiqのキューの設計に反映させて、継続的にキューの最適化を行う必要があります。

Sidekiqのログ(引用:GitLabの負荷分散)
Redis
RedisはGitLabで高速処理が求められる様々な一時的なデータを保存するために利用されます。PostgreSQLと同じくプライマリとセカンダリに別れていますが、Redisのセカンダリは読み取りができません。
ジョブキューやユーザーセッションの状態の情報など、失われるとGitLabの機能が停止するような情報はRedis Persisetentに保存され、Cacheやログなどの失われてもGitLabが動作できるような情報はRedis Cacheに保存されます。
Redis Sentinelというコンポーネントによって、Redisのプライマリで障害が起きた場合、セカンダリに自動的に切り替えが行われます。
Redisクラスターはクォーラム(クラスターが正常に機能する最小台数)を維持するために、3ノード以上の奇数ノードにデプロイする必要があります。
Gitaly Cluster
GitalyはGitリポジトリの読み書きを行うコンポーネントです。
Gitaly ClusterはすべてのGitリポジトリがすべてのGitalyノードに保存され、そのうち一つがプライマリとして動作します。
Gitalyへの接続はすべてPraefectというGitaly Clusterのリクエストをルーティングするコンポーネントを経由します。これによってGitalyノードで障害が起きた場合、自動的にフェイルオーバーします。また、PraefectはTLSの接続をサポートしています。
Praefect自身は内部ロードバランサーによって負荷分散されます。また、クォーラム(クラスターが正常に機能する最小台数)を維持するために、3ノード以上の奇数ノードにデプロイする必要があります。
PraefectにはGitaly Clusterのステータスを保存する独自のDB(Praefect PostgreSQL)が必要になります。GitLabのLinuxパッケージで構築する場合、非HA構成のDBになります。高可用性を保ちたい場合は外部の冗長化されたPostgreSQLが必要になります。
終わりに
今回はGitLabの各コンポーネントの冗長化について解説しました。セキュアで継続的な開発環境を実現するための土台として、冗長化の考え方を理解しておくことはDevSecOpsの観点でも重要です。今回示した例は1000RPSまたは50,000ユーザーを想定した大規模な環境です。各々のユースケースに適したGitLabの構成を設計し、その環境規模に応じてどのコンポーネントを冗長化するべきか判断する必要があります。今回の記事がその判断材料の助力になれば幸いです。
参考
- GitLabコンポーネントリスト
- GitLabリファレンスアーキテクチャ
- 例:最大 1000 RPS または 50,000 ユーザー
- マルチノードGitLab向けロードバランサー
- GitLabの負荷分散
- データベース負荷分散