Omnibus GitLabアーキテクチャとコンポーネント

Omnibus GitLabはChefのOmnibusプロジェクトをカスタマイズしてフォークしたもので、CookbookやレシピのようなChefのコンポーネントを使い、ユーザーのコンピュータ上でGitLabを設定するタスクを実行します。GitLab.comのOmnibus GitLabリポジトリは、Omnibus GitLabに必要なすべてのコンポーネントをホストしています。これらには、設定やプロジェクトメタデータのようなパッケージのビルドに必要なOmnibusの部分と、インストール後にユーザーのコンピュータで使用されるChef関連のコンポーネントが含まれます。

Omnibus-GitLab Components

これらのコンポーネントの詳細なウォークスルー動画がYouTubeにあります。

ソフトウェアの定義

GitLabプロジェクト定義ファイル

オムニバスアーキテクチャの主要なコンポーネントは、プロジェクトの詳細と外部ソフトウェアやライブラリとの依存関係をリストしたプロジェクト定義ファイルです。

このプロジェクト定義ファイルの主な構成要素は次のとおりです:

  • プロジェクトのメタデータ:プロジェクトのメタデータ:プロジェクトの名前や説明などの属性を含みます。
  • プロジェクトのライセンス詳細。
  • 依存関係リスト:GitLabのビルドや実行に必要な外部ツールやソフトウェアのリスト。
  • GitLabのインストールに使用されるグローバル設定変数:インストールディレクトリ、システムユーザー、システムグループを含みます。

個々のソフトウェアの定義

Omnibus GitLabはバッテリーインクルードスタイルのディストリビューションに従っています。GitLabインスタンスが適切に機能するために必要なソフトウェア、ライブラリ、バイナリはすべて、パッケージの一部として、組み込み形式で提供されます。

オムニバスアーキテクチャのもう一つの主要な構成要素は、ソフトウェアの定義と設定です。典型的なソフトウェア設定は、以下の部分で構成されます:

  • 必要なソフトウェアのバージョン。
  • ソフトウェアのライセンス
  • ビルド/実行するソフトウェアの依存関係。
  • ソフトウェアをビルドし、パッケージに組み込むために必要なコマンド。

GitLabで使うためにソフトウェアのソースコードにパッチを当てなければならないことがあります。これはセキュリティの脆弱性を修正したり、GitLabに必要な機能を追加したり、GitLabの他のコンポーネントと連携させたりするためです。この目的のために、Omnibus GitLabはパッチディレクトリで構成されており、様々なソフトウェアのパッチが保存されています。

より広範な変更については、ミラー上のブランチで必要な変更を追跡する方が便利かもしれません。この場合、アップストリームのタグやshaからブランチを作成し、ブランチ名でブランチポイントを参照するのが一般的です。例として、omnibusコードベースからgitlab-omnibus-v5.6.10 、アップストリームプロジェクトのv5.6.10 タグに基づいています。これにより、https://gitlab.com/gitlab-org/omnibus/compare/v5.6.10...gitlab-omnibus-v5.6.10 のような比較リンクを生成し、どのような内部変更があるかを特定することができます。

GitLabのグローバル設定テンプレート

Omnibus GitLabは、ユーザーのコンピュータにインストールされるGitLabインスタンスのすべての部分を設定するために使用できる単一の設定ファイルを同梱しています。この設定ファイルは、GitLabインスタンスに適用されるすべての設定の標準的なソースとして機能します。GitLabインスタンスの一般的な設定と、様々なコンポーネントの様々なオプションが記載されています。このファイルの一般的な構造は、<component>['<setting>'] = <value> というフォーマットで指定された設定で構成されています。利用可能なオプションはすべて設定テンプレートにリストされていますが、GitLabの基本的な動作に必要なもの以外はデフォルトでコメントアウトされています。ユーザーは必要に応じてコメントを外し、対応する値を指定することができます。

GitLabクックブック

前に説明したように、Omnibus GitLabはクックブック、アトリビュート、リソースといったChefコンポーネントの多くを使います。GitLab EEは、GitLab CEが使っているものを拡張してEE専用のコンポーネントを追加した別のCookbookを使っています。Omnibus GitLabのChef関連部分の主な担い手は以下の通りです:

デフォルトの属性

デフォルト属性は、その名前が示すように、設定ファイルで提供されるさまざまな設定のデフォルト値を指定します。これらの値はフェイルセーフとして機能し、ユーザーが設定に値を与えなかった場合に使用されます。

レシピ

レシピはオムニバスパッケージを使ってGitLabをインストールする際に、GitLabエコシステムの各コンポーネントをユーザーのコンピュータにセットアップする役割を果たすため、ほとんどの重労働を行います。必要なファイル、ディレクトリ、リンクを対応する場所に作成し、権限とオーナーを設定し、必要なサービスを設定、起動、停止し、ファイルが変更されるとこれらのサービスに通知します。default というマスターレシピがエントリーポイントとして機能し、様々なコンポーネントやサービスのために必要な他のすべてのレシピを呼び出します。

カスタムリソース

カスタムリソースは、レシピ間で利用可能なグローバルレベルのマクロと考えることができます。カスタムリソースの一般的な用途としては、共通のサービスに使用されるポートの定義や、異なるレシピで使用される可能性のある重要なディレクトリのリストなどがあります。異なるレシピで再利用されるリソースを定義します。

コンポーネントの設定用テンプレート

前述したように、Omnibus GitLabはGitLabインスタンスのすべてのコンポーネントを調整するための単一の設定ファイルを提供します。しかし、異なるコンポーネントのアーキテクチャ設計では、特定の場所に個別の設定ファイルを置く必要があるかもしれません。これらの設定ファイルは、ユーザーが一般設定ファイルで指定した値か、指定されたデフォルト値から生成する必要があります。そのため、Omnibus GitLabには、デフォルト値やユーザーからの値で埋められるプレースホルダを持つ設定ファイルのテンプレートが同梱されています。レシピは、テンプレートを埋めて必要な場所に配置することで、テンプレートを完成させるジョブです。

一般的なライブラリメソッド

Omnibus GitLab には、主にコードの再利用を目的としたライブラリメソッドもいくつか同梱されています。これには、サービスが稼働しているかどうかをチェックするメソッド、ファイルが存在するかどうかをチェックするメソッド、異なるコンポーネントとやりとりするためのヘルパーメソッドなどが含まれます。これらはChefレシピでよく使われます。

Omnibus GitLabで使われるすべてのライブラリのうち、特別なものがあります:主要なGitLabモジュールと、それが呼び出すすべてのコンポーネント固有のライブラリです。コンポーネント固有のライブラリには、対応するコンポーネントの設定を設定ファイルを解析するジョブが含まれています。プライマリGitLabモジュールは、これを調整するメソッドを含んでいます。デフォルト値の確認、コンポーネント固有のライブラリの呼び出し、デフォルト値とユーザー指定の値のマージ、バリデーション、初期値に基づく追加設定の生成などを行います。OmnibusのGitLabパッケージから出荷されるトップレベルのコンポーネントはすべてこのモジュールに追加され、設定ファイルやデフォルト属性に記載され、正しく解析されるようになります。

実行

GitLabはサービスの管理と監督にrunitレシピを使います。runitレシピはOSが使うinitシステムを特定し、GitLabに必要なサービスファイルの作成、サービスの有効化、サービスのリロードなどの基本的なサービス管理タスクを実行するジョブです。runitは他のレシピがサービスとやりとりするために使えるrunit_service 定義を提供します。詳しくは/files/gitlab-cookbooks/runit

サービス

サービスは、runitプロセスのinit/supervisorを使って実行するソフトウェアプロセスです。gitlab-ctl コマンドを使って、ステータスの確認、起動、停止、再起動を行うことができます。レシピでは、プロセスグループやGitLabのインスタンスに設定された設定/ロールに基づいて、これらのサービスを無効にしたり有効にしたりすることもできます。サービスの一覧とそれに関連するサービスグループはfiles/gitlab-cookbooks/package/libraries/config/services.rbにあります。

その他のgitlab-ctl コマンド

Omnibusはデフォルトで、GitLabインスタンスを管理するためにgitlab-ctl reconfiguregitlab-ctl restart のようなラッパーコマンドを提供しています。OmnibusのGitLabリポジトリで定義された特定のユースケースをターゲットにした追加のラッパーコマンドがいくつかあります。これらのコマンドは、一般的なgitlab-ctl コマンドと一緒に使うことで、データベースのマイグレーションや休止アカウントの削除など、あまり一般的ではないアクションを実行することができます。

テスト

Omnibus GitLabリポジトリはChefSpecを使って出荷するCookbookやレシピをテストします。通常の戦略は、レシピが2つ(またはそれ以上)の条件で正しく動作するかをチェックすることです: ユーザーが対応する設定を何も指定しない場合(すなわちデフォルトが使用される場合)と、ユーザーが指定した設定が使用される場合です。テストには、ファイルが正しい場所に生成されているかどうか、サービスが開始/停止/通知されているかどうか、正しいバイナリが起動されているかどうか、メソッドの呼び出しに正しいパラメータが渡されているかどうかなどをチェックするものがあります。レシピやライブラリのメソッドには、テストが関連付けられています。Omnibus GitLab では、テストプロセスを支援するためにいくつかのサポートメソッドやマクロも使用しています。テストは、テストスイート全体の実行に必要な時間を短縮するために、可能な限り Parallels に対応するように定義されています。

上記のコンポーネントのうち、いくつか(ソフトウェア定義、プロジェクトメタデータ、テストなど)はパッケージのビルド中、ビルド環境で使われ、いくつか(Chefクックブックやレシピ、GitLab設定ファイル、runit、gitlab-ctl コマンドなど)はユーザーのインストールしたインスタンスの設定に使われます。

Omnibus GitLabのワークライフサイクル

パッケージ構築中の作業

ビルドされるパッケージの種類は、ビルドプロセスが実行される OS によって異なります。Debian 環境でビルドされた場合、.deb パッケージが作成されます。パッケージのビルド中に起こることは、以下のステップにまとめることができます。

  1. 依存ソフトウェアのソースを取得します:
    1. ソフトウェアの定義を解析して、対応するバージョンを見つけます。
    2. リモコンやキャッシュからのソースコード取得
  2. 個々のソフトウェアコンポーネントのビルド
    1. 必要な環境変数とフラグの設定。
    2. パッチの適用(該当する場合
    3. コンポーネントのビルドとインストールを行い、適切な場所(/opt/gitlab 内部)にインストールします。
  3. 外部ソフトウェア、Ruby gems、JSモジュールなど、バンドルされているすべてのコンポーネントのライセンス情報を生成します。これには、コンポーネントによって提供される追加のライセンス文書(GitLab Railsによって提供されるlicenses.csv ファイルのような)と同様に、各依存関係の定義を分析することが含まれます。
  4. コンポーネントのライセンスをチェックして、互換性のないライセンスでコンポーネントを出荷していないことを確認します。
  5. パッケージのヘルスチェックを実行し、バイナリが利用可能なライブラリに対してリンクされていることを確認します。バンドルされているライブラリの場合、バイナリはグローバルに利用可能なライブラリではなく、そのライブラリに対してリンクする必要があります。
  6. /opt/gitlab の内容でパッケージをビルドします。 これはgitlab.rb ファイル内部で指定されたメタデータを利用します。これには、パッケージ名、バージョン、メンテナー、ホームページ、他のパッケージとの競合に関する情報が含まれます。

キャッシュ

Omnibus は、ビルドプロセスを最適化するために、ソフトウェアアーティファクト (依存するソフトウェアのソース) を保存するキャッシュと、各ソフトウェアコンポーネントがビルドされた後のプロジェクトツリーを保存するキャッシュの2種類を使用します。

ソフトウェアアーティファクトキャッシュ (GitLab Inc ビルド用)

ソフトウェアアーティファクトキャッシュは、依存するソフトウェアのソースを保存するために Amazon S3 バケットを使用します。私たちのビルドプロセスでは、このキャッシュはコマンドbin/omnibus cache populate を使って投入されます。これにより、Amazonバケットから必要なソフトウェアソースがすべて取り込まれ、必要な場所に保存されます。ソフトウェアのバージョン要件に変更があると、omnibusは元のアップストリームからそれをプルし、アーティファクトキャッシュに追加します。このプロセスはomnibus内部で行われ、リポジトリのルートで利用可能なomnibus.rbファイルでAmazonバケットを使用するように設定します。このキャッシュは、元のアップストリームリモートがダウンしても、依存するソフトウェアの可用性を保証します。

ビルドキャッシュ

ビルドプロセスで重要な役割を果たす2つ目のタイプのキャッシュは、ビルドキャッシュです。ビルドキャッシュは、依存するソフトウェアがビルドされた後のプロジェクトツリー (プロジェクトがビルドされる場所 -/opt/gitlab) のスナップショットとして説明できます。A、B、C、D、E の 5 つの依存するソフトウェアがあるプロジェクトを考えてみましょう。ビルドキャッシュは Git タグを使ってスナップショットを作成します。各ソフトウェアがビルドされると Git タグが計算され、コミットされます。ここで、ソフトウェア D の定義を変更したとしましょう。もう一度ビルドしようとすると、omnibus は前回のビルドで D がビルドされる前に作成されたスナップショットを再利用できます。したがって、Cのビルド後に作成されたスナップショットをチェックアウトするだけでよいので、A、B、Cのビルドにかかる時間を節約できます。Omnibusは、キャッシュを「汚した」ソフトウェア(汚しには、ソフトウェア定義の変更、以前のコンポーネントの名前/バージョンの変更、現在のコンポーネントのバージョンの変更のいずれかがあります)がビルドされる直前のスナップショットを使用します。同様に、ビルド中にソフトウェアAの定義に変更があった場合、キャッシュが汚れ、Aとそれに続くすべての依存関係がゼロからビルドされます。Cがキャッシュを汚した場合、AとBは再利用され、C、D、Eはゼロから再度ビルドされます。

このキャッシュが意味を持つのは、ビルドをまたいで保持される場合だけです。そのために、GitLab CI のキャッシュ機構を使います。専用のRunnerがあり、その内部キャッシュをAmazonバケットに保存する設定になっています。各ビルドの前に、このキャッシュを取り込み(Makefileのrestore_cache_bundle target)、適切な場所に移動してビルドを開始します。キャッシュはダーティ化するまで Omnibus によって使用されます。ビルドが終わったら、新しいキャッシュを梱包し、CIにAmazonバケットにバックアップするよう指示します(Makefileのpack_cache_bundle )。

どちらのキャッシュも、GitLabの全体的なビルド時間を短縮し、外部要因への依存を減らします。

キャッシュの仕組みをまとめると以下のようになります:

  1. ソフトウェアの依存関係ごとに
    1. バージョンとSHA256を理解するために定義を解析します。
    2. Amazon バケットのアーティファクトキャッシュにあるソースファイルの tarball がバージョンと SHA256 に一致する場合、それを使用します。
    3. そうでない場合は、アップストリームリモートから正しい tarball をダウンロードします。
  2. CIキャッシュからキャッシュを取得します。
  3. ソフトウェアの依存関係ごとに
    1. キャッシュが汚れている場合は、ループを解除します。
    2. そうでなければ、スナップショットをチェックアウトします。
  4. 依存関係が残っている場合
    1. 残りの依存関係それぞれについて
      1. 依存関係をビルドします。
      2. スナップショットを作成し、コミットします。
  5. 新しいビルドキャッシュをCIキャッシュにプッシュバックします。

ビルド中に起こることgitlab-ctl reconfigure

GitLabインスタンスの管理中によく使われるコマンドの一つがgitlab-ctl reconfigureです。このコマンドは簡単に言うと、設定ファイルを解析し、そこから与えられた値でレシピを実行します。実行されるレシピは、インストールディレクトリ内部のembedded フォルダにあるdna.json というファイルで定義されます(このファイルは、ソフトウェア定義で定義されているgitlab-cookbooks というソフトウェアの依存関係によって生成されます)。GitLab CE の場合、gitlab という名前のクックブックがマスターレシピとして選択され、runit を含む他のすべての必要なレシピが呼び出されます。要するに、reconfigureは設定テンプレートで提供された値で異なるファイルやサービスを設定するchef-clientの実行です。