GitLab Architecture #GitLab
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
GitLab と呼んだ場合、一般的には Ruby on Rails で書かれた GitLab アプリケーションだけではなく、GitLab として機能するシステム全体を指します。このシステムとしての GitLab には、リレーショナルデータベースの PostgreSQL や追加の機能を提供する GitLab Pages などの様々なコンポーネントから構成されています。
これらのコンポーネントの概要は公式ドキュメントの GitLab architecture overview で解説されています。
しかし、この図は簡略化されすぎていて少しわかりにくいです。もう少し詳しく解説します。
主要な GitLab のコンポーネントの構成
まずは主要な GitLab の機能を提供するための最小限のコンポーネントから見ていきましょう。
- GitLab Rails
- GitLab の主要機能が実装されている Ruby on Rails のアプリケーションです。GitLab Web UI や Web API を担当する Puma (GitLab Rails) と非同期処理を担当する Sidekiq (GitLab Rails) の二つのインスタンスに別れます。
- GitLab Workhorse
- Puma (GitLab Rails) 専用のスマートなリバースプロキシで、Puma (GitLab Rails) や Gitaly と連携します。
- Gitaly
- Git リポジトリを管理します。
- GitLab Shell
- Git リポジトリへの SSH アクセスを担当します。OpenSSH Server と Gitaly のブリッジ役です。
- ストレージ
- GitLab のプロジェクトのデータやキャッシュなど様々なデータを保存します。ローカルストレージの他に、PostgreSQL や Redis も使われています。
これら以外にも追加の機能を提供するコンポーネントがあります。代表的なものは次のとおりです。
- GitLab Pages
- 静的ウェブサイトなどをホストします
- Container Registry
- プロジェクトに紐付いた Docker Image を保管します。
- GitLab Mattermost
- Slack 風のチャットです。
- Grafana Dashboard / Prometheus
- GitLab 自身の監視用です。
これらのコンポーネントについては後述します。
GitLab の機能のうち、どのコンポーネントが担当しているのかを勘違いしやすいものがあります。次は一例です。
- Git LFS は主に Puma (GitLab Rails) が処理します。Gitaly ではありません。
- Dependency Proxy は Docker Image を扱いますが、Container Registry ではなく Puma (GitLab Rails) の担当です。
Puma (GitLab Rails)
GitLab の主要な機能は GitLab Web UI もしくは Web API でアクセスします。これらを実際に処理をするのが、Puma (GitLab Rails) と呼ばれる Ruby on Rails のアプリケーションで、GitLab の要となります。アプリケーションサーバーとして Puma を採用しています。
ソースコードの規模が大きいだけで、一般的なウェブアプリケーションとかわりありません。
GitLab Workhorse
GitLab Workhorse は GitLab 専用のスマートなリバースプロキシです。GitLab Web UI や Web API 及び Git HTTP アクセスは GitLab Workhorse がエンドポイントを公開しています。Puma (GitLab Rails) や Gitaly は直接は公開されていません。
GitLab Workhorse は Go 言語で書かれています。GitLab Rails とは密接な関係があるためか、ソースコードは GitLab Rails と同じ GitLab プロジェクトで管理されています。
GitLab Workhorse は、図にある通り、受け取った HTTP リクエストを他のコンポーネントに振り分けます。
- GitLab Web UI の表示のための CSS / JavaScript / 画像ファイルをローカルファイルシステムから読み込んでブラウザーに返します。
- HTTP Git アクセスは Gitaly に転送します。詳しくは GItaly の項で解説します。
- それ以外のアクセスは Puma (GitLab Rails) に転送します。
Puma (GitLab Rails) へ転送されるリクエストのうち、イシューの添付ファイルのような大きなファイルのやり取りは GitLab Workhorse が介入します。
Ruby on Rails は大きなファイルのやり取りでメモリを多く消費します。GitLab.com のような大規模な GitLab ではより大きなメモリを持つサーバーが必要で、運用コストの増大に繋がります。GitLab Workhorse はこの大きなファイルのやり取りを Rails より効率よく行えるよう設計されており、Puma (GitLab Rails) の代わりに処理します。
次の図は、イシューの添付画像を表示するときのおおまかな動作です。
- Puma (GitLab Rails) は要求された画像ファイルのデータをレスポンスには含めず、代わりにローカルファイルシステムにある画像ファイルのパスを埋め込みます。
- GitLab Workhorse は (1) で指定された画像ファイルを読み込み、レスポンスボディに埋め込みます。
- ブラウザーには画像ファイルのデータが送信されています。
要求された画像ファイルにアクセスする権限があるかどうかは Puma (GitLab Rails) が処理していることに注意してください。GitLab Workhorse はあくまで Puma (GitLab Rails) が苦手とする処理を肩代わりするだけで、自身が GitLab のプロジェクトの情報にアクセスすることはありません。PostgreSQL データベースにもアクセスしません。
このような大きなファイルの処理は、イシューの添付ファイル以外にもあります。
- イシューの画像や添付ファイル
- CI の成果物
- パッケージレジストリのファイル
- Git LFS
- それ以外にもたくさん
また、これら以外にも GitLab Workhorse の機能はあります。詳しくは公式ドキュメントの Features that rely on Workhorse を参照してください。
Sidekiq (GitLab Rails)
GitLab に限らず、ウェブアプリケーションでは時間がかかる処理や定時実行する処理はバックグラウンドで非同期で実行されます。これらの非同期ジョブの管理のために、GitLab では Sidekiq というジョブ管理ツールを採用しています。
GitLab の非同期ジョブをいくつか例に挙げます。
- イシューの CSV エクスポート
- イシューのエクスポートは CSV ファイルでメールが送信されます。このように時間がかかる処理は非同期ジョブとして実行されることが多いです。
- マージリクエストのマージによるイシューの自動クローズ
- イシューのクローズは時間がかかる処理ではありませんが、このような連鎖的に発生する処理は非同期ジョブとして実行されていることが多いです。
- プロジェクトメンバーの期限での退会
- 定期的に期限切れのメンバーがいないかをチェックし、そのユーザーをメンバーから除外します。この処理はバックグラウンドで非同期に実行されます。他にも、定期クリーンナップ処理や統計情報の作成など、バックグラウンドで定期的に実行される処理はだいたい非同期ジョブです。
図にあるとおり、非同期ジョブのジョブキューは Redis データベースで管理されています。Sidekiq エンジンはジョブキューを常に監視していて、実行可能なジョブが見つかったらそれを取り出して実行します。Puma (GitLab Rails) は処理の中で非同期ジョブをジョブキューに投入します。Sidekiq で実行されるジョブの処理の中で新たな非同期ジョブが投入されることもあります。
Sidekiq ジョブの投入は GitLab Rails のみが行っています。Gitaly や GitLab Workhorse が Sidekiq へジョブを直接投入することはありません。
GitLab の 管理エリア の Monitoring
-> バックグラウンドジョブ
でジョブの一覧が確認できます。この画面は Sidekiq 標準のダッシュボードです。Sidekiq 公式の Getting Started を読めば、Sidekiq がどういうものか理解しやすいと思われます。
Gitaly
Gitaly は Git リポジトリのパフォーマンス改善とスケールアウトのために GitLab が開発したサービスです。
以前は GitLab Shell や GitLab Rails から直接 Git コマンドを呼び出していました。この方法はスケールアウトに向いておらず、GitLab.com のユーザー増に耐えられませんでした。(なお、一時期は NFS を利用していましたが、現在は非推奨です) 詳しい経緯は GitLab 公式ブログの The road to Gitaly v1.0 (aka, why GitLab doesn't require NFS for storing Git data anymore) をご覧ください。
Gitaly のメリットは次のとおりです。
- Git アクセスの結果をキャッシュし、高速化します。
- Gitaly Cluster を組むことでスケールアウトが可能です。
次のことは Gitaly のスコープ外です。
- 認証・認可
- Gitaly はリクエストが誰からのものであるか、Git リポジトリへのアクセスが許可されているかを管理しません。Gitaly にアクセスする前にリポジトリアクセスが許可されているかどうかを確認する必要があります。
- Git LFS ストレージ
- LFS ストレージは Puma (GitLab Rails) の担当で、GitLab Workhorse が連携して処理しています。
Git リポジトリへのアクセスは HTTP と SSH の二種類あります。
HTTP アクセス
図の通り、Git リポジトリへの HTTP アクセスは、GitLab Workhorse が Gitaly に処理を依頼します。その前に、GitLab Workhorse は対象の Git リポジトリへのアクセス可否の確認のために、Puma (GitLab Rails) で認証と認可を確認します。
詳しくは公式ドキュメントの Web request (80/443) をご覧ください。
SSH アクセスと GitLab Shell
Git リポジトリへの SSH アクセスは、OpenSSH Server が SSH アクセス専用の GitLab Shell というコンポーネントを呼び出し、GitLab Shell が Gitaly を呼び出します。アクセス可否のために、GitLab Shell が Puma (GitLab Rails) で認証と認可を確認します。(実際には GitLab Workhorse を経由します)
認証と認可のための API のエンドポイントが異なるくらいで、HTTP アクセスのときと大きな違いはありません。
詳しくは公式ドキュメントの SSH request (22) をご覧ください。
なお、公式ドキュメントでは gitlab-shell-authorized-keys-check
を最初に呼び出していますが、これは SSH 鍵の検索を高速化するためで、利用するには明示的に有効化する必要があります。(Docker 版はデフォルトで有効です) 詳しくは公式ドキュメントの Fast lookup of authorized SSH keys in the database をご覧ください。
gitlab-sshd
gitlab-sshd は OpenSSH Server と GitLab Shell を置き換える新しいコンポーネントです。現時点でデフォルト無効です。(GitLab.com では有効化されているそうです)
- GitLab へのアクセスの IP アドレス制限機能に対応しています。
- コンピューターリソースの利用効率に優れています
GitLab Shell に GitLab で必要とする SSH Server の機能を実装したものと考えるとイメージが付きやすいと思います。
Gitaly から GitLab Internal API の呼び出し
GitLab には内部利用のための利用者には公開されていない Internal API が実装されています。Gitaly は必要に応じてこれを呼び出します。
- ブランチのアクセス権の確認のため
- GitLab では保護されたブランチへのプッシュを禁止する機能があります。プッシュに先立って対象となるブランチへのアクセス権を確認します。
- Git リポジトリへの操作の通知
- Puma (GitLab Rails) は Git リポジトリへのプッシュなどをアクティビティに記録します。また、プッシュ後に GitLab CI/CD パイプラインをキックします。これらのために、Gitaly は Puma (GitLab Rails) にいくつかの Git リポジトリへの操作を通知します。
ストレージ
GitLab に限らず、どのアプリケーションでもデータの永続化のための仕組みが必要です。GitLab のプロジェクトやグループのデータを保存するためには PostgreSQL とローカルストレージが、Sidekiq ジョブキューやキャッシュのために Redis が使われています。
ローカルストレージにはプロジェクトの様々なデータが保管されています。一部抜粋します。
種類 | ファイルシステム内の場所 |
---|---|
Git リポジトリ | /var/opt/gitlab/git-data/repositories |
Git LFS | /var/opt/gitlab/gitlab-rails/shared/lfs-objects |
イシューなどの添付ファイル | /var/opt/gitlab/gitlab-rails/uploads |
Package Registry | /var/opt/gitlab/gitlab-rails/shared/packages |
Container Registry | /var/opt/gitlab/gitlab-rails/shared/registry |
これらのデータはローカルストレージのかわりにクラウドの Amazon S3 やオープンソースの MinIO のようなオブジェクトストレージを使うこともできます。利用できるオブジェクトストレージプロバイダーのリストや GitLab への設定方法は公式ドキュメントの Object storage をご覧ください。
なお、Git リポジトリ (Gitaly) はオブジェクトストレージを利用できません。ローカルストレージのみとなります。
追加の機能のためのコンポーネント
nginx
追加のコンポーネントには GitLab Mattermost や Container Registry などがありますが、HTTP のリクエストをこれらのコンポーネントや GitLab Workhorse に振り分ける役割を担っているリバースプロキシが nginx です。
振り分けルールを正確に知りたい方は、インストールされた GitLab の nginx の設定ファイル (/var/opt/gitlab/nginx/conf
以下) を確認してください。
なお、GitLab / Container Registry / Mattermost はドメイン (もしくは IP アドレスかポート番号) を同じにはできません。
GitLab 自身の監視のためのコンポーネント
Omnibus GitLab には GitLab システム全体を監視するための様々なコンポーネントが含まれています。これらのコンポーネントは GitLab の機能には不要ですが、トラブルシュートや運用監視には欠かせません。
- 各種 exporter
- メトリクス収集のためのコンポーネントです。サーバー自身の CPU・メモリ使用率などのメトリクスを収集する node_exporter や、PostgreSQL のメトリクスを収集する postgres_exporter などがあります。動作している exporter は gitlab-ctl status コマンドで表示できます。
- nginx など
- nginx や Puma (GitLab Rails) などは自身でメトリクスを提供します。
- Prometheus
- 各種 exporter や nginx などからメトリクスを収集し、管理するデータベースです。
- Alertmanager
- Prometheus と協調して、アラートを投げます。標準ではアラートの設定はされていないようです。
- Grafana
- Prometheus のメトリクスをグラフで視覚化するコンポーネントです。このコンポーネントは GitLab 16.0 で非推奨となり GitLab 16.3 で廃止予定です。Grafana は必要に応じて別途インストールする必要があります。
Prometheus は図解した以外のメトリクスも収集しています。何を収集しているかは Prometheus の設定 (/var/opt/gitlab/prometheus/prometheus.yml
) を参照してください。
Container Registry
Docker イメージを管理する Container Registry 機能は container-registry というコンポーネントが担当しています。これは Distribution をベースに GitLab が改良を加えたものです。
Container Registry に似た GitLab の機能に、npm や Maven リポジトリの代わりになる Package Registry や、DockerHub のイメージをキャッシュする Dependency Proxy があります。この二つの機能は Puma (GitLab Rails) が担当しています。
Container Registry は GitLab とはドメイン (もしくは IP アドレスやポート番号) が異なります。ややこしいのですが、docker login registry.gitlab.com は Container Registry へのログインで、docker login gitlab.com は Dependency Proxy へのログインとなります。
container-registry コンポーネントには認証・認可機能がありませんので、Puma (GitLab Rails) と連携してそれを処理しています。
GitLab Pages
GitLab で静的ウェブサイトをホストする GitLab Pages 機能は GitLab Pages というコンポーネントが担当します。
また、Container Registry と同様に GitLab Pages コンポーネントには認証・認可機能がありませんので、Puma (GitLab Rails) と連携してそれを処理しています。
GitLab Pages はドメイン名が必須です。IP アドレスでの運用はできません。
GitLab Mattermost
GitLab Mattermost は Slack 風のチャットサーバーの Mattermost を Omnibus GitLab に同梱したものです。標準では無効化されていますが、簡単に有効化でき、GitLab OAuth での SSO など、GitLab との連携設定も合わせてセットアップされます。
Omnibus GitLab に含まれていますが、開発の主体は Mattermost 社で、GitLab 社はノータッチです。また、GitLab.com では提供されていません。他のコンポーネントと立ち位置が異なるため、注意が必要です。
- gitlab-backup コマンドでのバックアップの対象外です
- GitLab Dashboard での監視の対象外です
GitLab Observability
GitLa Observability は現在開発中で、将来的に Omnibus GitLab に含まれると思われるコンポーネントです。重要なコンポーネントになると思われるので、紹介します。
GitLab のビジョンの一つに DevOps がありますので、GitLab 独自のアプリケーションのエラートラッキングや監視機能も含まれています。GitLab のプロジェクトからシームレスに使える反面、専用の製品と比べると機能が大きく見劣りします。
GitLab はこれらの機能の強化のために、GitLab Observability という新しいコンポーネントを開発しています。
- GitLab Observability Backend
- 各種メトリクスの収集・管理を行います。買収した Opstrace を中心に、Prometheus などを組み合わせて構成されています。
- GitLab Observability UI
- GitLab Observability Backend の情報を視覚化します。Grafana 7.x をベースにフォークして GitLab が開発しています。
詳しくは GitLab 公式の Product Direction - Monitor:Observability をご覧ください。
コンポーネントのビルド
Omnibus GitLab には、nginx や Ruby や openssl などの既成品のコンポーネントを多数含んでいます。これらのコンポーネントは GitLab が独自にビルドし、Omnibus GitLab に同梱しています。オリジナルの配布物ではありません。中にはパッチがあたっているコンポーネントもあります。Linux のディストリビューションに git
や openssl
がインストールされていてもそれを利用しません。
- 例外的に、OpenSSH Server はディストリビューションのものを利用しています。
- 例外的に、GitLab Mattermost はオリジナルの配布物のバイナリをそのまま利用しています。
どのコンポーネントがどのようにビルドされているかについては Omnibus GitLab のビルドスクリプトを読む必要があります。
終わりに
GitLab は内部で様々なコンポーネントが組み合わされて機能を提供しています。日頃は各コンポーネントやその役割と関係を意識することはありませんが、トラブルシュートやパフォーマンス向上のための構成変更に役に立ちます。