大規模リポジトリに対するGitLabの最適化

ワークツリー内の50k以上のファイルで構成される大規模なリポジトリでは、クローンとチェックアウトに時間がかかるため、特別な配慮が必要になることがよくあります。

GitLabとGitLab Runnerはこのシナリオをうまく処理しますが、一連のオペレーションを効率的に実行するために最適化された設定が必要です。

大きなリポジトリを扱うための一般的なガイドラインはシンプルです。 各ガイドラインの詳細は以下のセクションで説明します:

  • 常にインクリメンタルにフェッチしてください。 ワークツリーをすべて再作成するような方法でクローンを作成しないでください。
  • データ転送を減らすために常にシャロークローンを使用します。 この場合、CPUへの影響が大きくなるため、GitLabインスタンスへの負担が増えることに注意してください。
  • フォークベースのワークフローを多用する場合は、クローンディレクトリを制御してください。
  • git clean フラグを最適化することで、ビルドに影響する可能性のあるデータを削除または保持し、ビルドを高速化します。

Shallow cloning

GitLab Runner 8.9 で導入されました。

GitLabとGitLab Runnerはデフォルトで常にフルクローンを行います。 GitLabからのすべての変更を受け取ることになりますが、余分なコミットログを受け取る結果になることがよくあります。

これは、GitLabGIT_DEPTH Runner に浅いクローンを実行するように指示します。 浅いクローンは、 GIT_DEPTH変数でGIT_DEPTH 定義したコミット数まで、指定した GIT_DEPTHブランGIT_DEPTH チの最新の変更のみを Git にリクエスト GIT_DEPTHさせます。

これにより、git リポジトリからの変更点の取得が大幅に高速化されます。特に、リポジトリに大きなファイルが多数あるような長いバックログがある場合は、データ転送量を効果的に減らすことができます。

次の例では、GitLab Runner をシャロークローンにして指定したブランチだけを取得するようにしています。他のブランチやタグは取得しません。

variables:
  GIT_DEPTH: 10

test:
  script:
    - ls -al

Git strategy

GitLab Runner 8.9 で導入されました。

デフォルトでは、GitLabは常にこのGIT_STRATEGY: fetch ストラテジーを GIT_STRATEGY: fetch優先するように設定されていますGIT_STRATEGY: fetch 。 この GIT_STRATEGY: fetchストラテジーは、ディスク上にワークツリーが見つかった場合、既存のワークツリーを再利用します。これはGIT_STRATEGY: clone ストラテジーとは異なり、クローンの場合、ワークツリーが見つかった場合、クローンする前に削除されます。

fetch の使用は、転送するデータ量を削減し、CI からリポジトリに対して行うオペレーションに影響を与えないため、好まれます。

しかし、fetch は前のワークツリーにアクセスする必要があります。shelldocker のexecutorを使用する場合は、ワークツリーを保存し、デフォルトで再利用しようとするため、うまく機能します。

これはkubernetes executorでは動作しませんし、docker+machineを使用する場合にも制限があります。kubernetes executorでは常にエフェメラルディレクトリにクローンされます。

GitLabはGIT_STRATEGY: none 。これはGitLabが実行するfetchcheckout のコマンドを無効にし、自分で実行することを要求します。

git clone パス

GitLab Runner 11.10 で導入されました。

GIT_CLONE_PATH これは、フォークワークフローで大きなリポジトリを多用する場合に意味を持ちます。

GitLab Runnerから見たフォークワークフローは、別々のリポジトリに別々のワークツリーとして保存されます。 つまり、GitLab Runnerはワークツリーの使い方を最適化することができず、GitLab Runnerに指示する必要があるかもしれません。

このような場合、理想的にはGitLab Runnerのexecutorを指定されたプロジェクトのみに使用し、異なるプロジェクト間で共有しないようにして、このプロセスをより効率的にしたいものです。

GIT_CLONE_PATH $CI_BUILDS_DIR現在のところ、ディスクから任意のパスを選ぶことは不可能です。

Git clean flags

GitLab Runner 11.10 で導入されました。

GIT_CLEAN_FLAGS では、各 CI ジョブに対してgit clean コマンドの実行を要求するかどうかを制御できます。デフォルトでは、GitLab は指定された SHA にワークツリーがあり、リポジトリがクリーンであることを保証します。

GIT_CLEAN_FLAGS noneに設定すると無効になります。非常に大きなリポジトリでは、 はディスクI/Oを大量に消費するため、これが望ましいかもしれません。 (例) でこれを制御すると、後続の実行の間にワークツリー内のいくつかのディレクトリの削除を制御して無効にすることができ、インクリメンタルビルドを高速化できます。これは、既存のマシンを再利用し、ビルドに再利用できる既存のワークツリーがある場合に最大の効果を発揮します。git clean GIT_CLEAN_FLAGS: -ffdx -e .build/

GIT_CLEAN_FLAGSが受け付ける正確なパラメーターについては、git cleanのドキュメントを参照してください。利用可能なパラメーターはgitのバージョンに依存します。

Git fetch extra flags

GitLab Runner 13.1 で導入されました

GIT_FETCH_EXTRA_FLAGS を使用すると、追加のフラグを渡してgit fetch の動作を変更できます。

詳しくはGIT_FETCH_EXTRA_FLAGS ドキュメントをご覧ください。

フォークベースのワークフロー

GitLab Runner 11.10 で導入されました。

上記のガイドラインに従って、私たちがしたいことを想像してみましょう:

  • 大きなプロジェクト(ディレクトリに50kファイル以上)に最適化します。
  • 貢献するためにフォークベースのワークフローを使用してください。
  • 既存のワークツリーを再利用します。 リポジトリと事前にクローンされたランナーを設定します。
  • プロジェクトとすべてのフォークにのみ割り当てられたrunner。

shell executorを使った例と、docker executorを使った例を考えてみましょう。

shell executor の例

次のようなconfig.tomlがあるとします。

concurrent = 4

[[runners]]
  url = "GITLAB_URL"
  token = "TOKEN"
  executor = "shell"
  builds_dir = "/builds"
  cache_dir = "/cache"

  [runners.custom_build_dir]
    enabled = true

このconfig.toml

  • shell の executor を使用します、
  • すべてのクローンが保存される/builds ディレクトリを指定します。
  • GIT_CLONE_PATHを指定できるようにします、
  • 一度に実行できるジョブは最大4つです。

docker executor の例

次のようなconfig.tomlがあるとします。

concurrent = 4

[[runners]]
  url = "GITLAB_URL"
  token = "TOKEN"
  executor = "docker"
  builds_dir = "/builds"
  cache_dir = "/cache"

  [runners.docker]
    volumes = ["/builds:/builds", "/cache:/cache"]

このconfig.toml

  • docker の executor を使用します、
  • /builds すべてのクローンが保存されるディスク上の /buildsカスタム/builds ディレクトリを /builds指定します。 このディレクトリを/builds マウント /buildsすることで、次回以降の実行時に再利用できるようにし、クローン作成ストラテジーを上書きできるようにします。
  • デフォルトで有効になっているため、GIT_CLONE_PATH を指定する機能は有効になりません。
  • 一度に実行できるジョブは最大4つです。

私たちの.gitlab-ci.yml

executorを設定したら、.gitlab-ci.ymlを微調整する必要があります。

私たちのパイプラインは、以下の.gitlab-ci.yml

variables:
  GIT_DEPTH: 10
  GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_NAME

build:
  script: ls -al

を設定します:

  • 後続のgit fetch コマンドを高速化するため、10の浅いクローン。
  • 親プロジェクトとすべてのフォーク間でワークツリーを再利用できるようにするためのカスタムクローンパス。

$CI_CONCURRENT_ID$CI_CONCURRENT_ID使用する$CI_CONCURRENT_ID主な理由は、使用されるワークツリーがプロジェクト間で衝突しないことを保証 $CI_CONCURRENT_IDするためです。 は指定された executor 内で一意な識別子を表し、パスを構築するためにそれを使用する限り、このディレクトリが他の同時実行中のジョブと衝突しないことが保証されます。

カスタムクローンオプションをconfig.toml

理想的には、すべてのジョブに関連するコンフィギュレーションは、.gitlab-ci.ymlに保存されるべきです。 しかし、これらのスキームをRunnerコンフィギュレーションの一部にすることが望ましい場合もあります。

上記のForksの例では、このコンフィギュレーションをユーザーが発見できるようにすることが望ましいかもしれませんが、ブランチごとに.gitlab-ci.yml を更新する必要があるため、管理上のオーバーヘッドが発生します。このような場合は、.gitlab-ci.yml クローン経路を不可知論にしておき、Runnerのコンフィギュレーションにすることが望ましいかもしれません。

.gitlab-ci.yml がオーバーライドしない場合、Runner が使用する以下の仕様でconfig.tomlを拡張することができます:

concurrent = 4

[[runners]]
  url = "GITLAB_URL"
  token = "TOKEN"
  executor = "docker"
  builds_dir = "/builds"
  cache_dir = "/cache"

  environment = [
    "GIT_DEPTH=10",
    "GIT_CLONE_PATH=$CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_NAME"
  ]

  [runners.docker]
    volumes = ["/builds:/builds", "/cache:/cache"]

これにより、クローン設定は与えられたRunnerの一部となり、.gitlab-ci.ymlを更新する必要はありません。