GitLab CI/CDでのキャッシング

キャッシュとは、ジョブがダウンロードして保存する一つ以上のファイルのことです。同じキャッシュを使う後続のジョブはファイルを再度ダウンロードする必要がないので、より速く実行できます。

.gitlab-ci.yml ファイルでキャッシュを定義する方法については、cache リファレンス を参照してください。

キャッシュとアーティファクトの違い

インターネットからダウンロードしたパッケージのような依存関係にはキャッシュを使います。キャッシュはGitLab Runnerがインストールされている場所に保存され、ディストリビューションキャッシュが有効な場合はS3にアップロードされます。

ステージ間で中間ビルド結果を渡すためにアーティファクトを使用します。アーティファクトはジョブによって生成され、GitLabに保存され、ダウンロードすることができます。

アーティファクトもキャッシュもプロジェクトディレクトリからの相対パスで定義され、プロジェクトディレクトリ外のファイルにはリンクできません。

キャッシュ

  • cache キーワードを使用して、ジョブごとにキャッシュを定義します。そうでない場合は無効になります。
  • 後続のパイプラインはキャッシュを使用できます。
  • 依存関係が同じであれば、同じパイプライン内の後続のジョブもキャッシュを使用できます。
  • 異なるプロジェクトはキャッシュを共有できません。
  • デフォルトでは、保護されたブランチと保護されていないブランチはキャッシュを共有しません。しかし、この動作を変更することができます。

アーティファクト

  • ジョブごとにアーティファクトを定義します。
  • 同じパイプラインの後のステージのジョブは、アーティファクトを使用できます。
  • 異なるプロジェクトでアーティファクトを共有することはできません。
  • アーティファクトの有効期限は、デフォルトで 30 日です。カスタムで有効期限を定義できます。
  • 最新のアーティファクトを保持]が有効になっている場合、最新のアーティファクトは失効しません。
  • 依存関係を使用して、どのジョブがアーティファクトを取得するかを制御します。

良いキャッシングの実践例

キャッシュの可用性を最大にするために、以下の1つ以上を実行してください:

Runnerがキャッシュを効率的に使用するには、以下のいずれかを行う必要があります:

  • すべてのジョブに単一のRunnerを使用します。
  • キャッシュがS3バケットに保存されるディストリビューションキャッシュを持つ複数のRunnerを使用してください。GitLab.comの共有ランナーはこのように動作します。これらのランナーはオートスケールモードにすることができますが、そうする必要はありません。
  • 同じアーキテクチャの複数のRunnerを使い、これらのRunnerで共通のネットワークマウントされたディレクトリを共有してキャッシュを保存します。このディレクトリはNFSかそれに類するものを使用します。これらのランナーはオートスケールモードでなければなりません。

複数のキャッシュの使用

最大4つのキャッシュを持つことができます:

test-job:
  stage: build
  cache:
    - key:
        files:
          - Gemfile.lock
      paths:
        - vendor/ruby
    - key:
        files:
          - yarn.lock
      paths:
        - .yarn-cache/
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - yarn install --cache-folder .yarn-cache
    - echo Run tests...

複数のキャッシュがフォールバック・キャッシュ・キーと組み合わされている場合、キャッシュが見つからないたびにグローバル・フォールバック・キャッシュがフェッチされます。

フォールバック・キャッシュ・キーの使用

キャッシュごとのフォールバック・キー

GitLab 16.0 で導入されました

各キャッシュエントリはfallback_keys キーワード で5つまでのフォールバックキーをサポートします。ジョブがキャッシュキーを見つけられなかった場合、代わりにフォールバックキャッシュの取得を試みます。フォールバック・キーは、キャッシュが見つかるまで順番に検索されます。キャッシュが見つからない場合、ジョブはキャッシュを使用せずに実行されます。例えば

test-job:
  stage: build
  cache:
    - key: cache-$CI_COMMIT_REF_SLUG
      fallback_keys:
        - cache-$CI_DEFAULT_BRANCH
        - cache-default
      paths:
        - vendor/ruby
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - echo Run tests...

この例では:

  1. ジョブはcache-$CI_COMMIT_REF_SLUG キャッシュを探します。
  2. cache-$CI_COMMIT_REF_SLUG が見つからない場合、ジョブは予備オプションとしてcache-$CI_DEFAULT_BRANCH を探します。
  3. cache-$CI_DEFAULT_BRANCH も見つからない場合、ジョブは2番目の予備オプションとしてcache-default を探します。
  4. 見つからなかった場合、ジョブはキャッシュを使わずにすべてのRubyの依存関係をダウンロードしますが、ジョブが完了したときにcache-$CI_COMMIT_REF_SLUG

フォールバックキーはcache:key と同じ処理ロジックに従います:

グローバル・フォールバック・キー

GitLab Runner 13.4で導入されました

$CI_COMMIT_REF_SLUG定義済み変数を使用して、cache:keyを指定することができます。例えば、$CI_COMMIT_REF_SLUGtest, testである場合、test. でタグ付けされたキャッシュをダウンロードするジョブを設定することが testできます。

このタグを持つキャッシュが見つからない場合、CACHE_FALLBACK_KEY を使用して、存在しない場合に使用するキャッシュを指定できます。

以下の例では、$CI_COMMIT_REF_SLUG が見つからない場合、ジョブはCACHE_FALLBACK_KEY 変数で定義されたキーを使用します:

variables:
  CACHE_FALLBACK_KEY: fallback-key

job1:
  script:
    - echo
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths:
      - binaries/

キャッシュの抽出順序は

  1. 抽出の試行cache:key
  2. 各エントリーの検索試行回数fallback_keys
  3. グローバルフォールバックキーに対する検索試行。CACHE_FALLBACK_KEY

キャッシュ抽出プロセスは、最初に成功したキャッシュが取得された後に停止します。

特定のジョブのキャッシュを無効にします。

キャッシュをグローバルに定義した場合、各ジョブは同じ定義を使用します。ジョブごとにこの動作を上書きすることができます。

ジョブに対してキャッシュを完全に無効にするには、空のリストを使います:

job:
  cache: []

グローバル設定を継承し、ジョブごとに特定の設定を上書きします。

アンカーを使うことでグローバルキャッシュを上書きせずにキャッシュ設定をオーバーライドできます。たとえば、1つのジョブに対してpolicy

default:
  cache: &global_cache
    key: $CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
      - public/
      - vendor/
    policy: pull-push

job:
  cache:
    # inherit all global cache settings
    <<: *global_cache
    # override the policy
    policy: pull

詳細については、cache: policy を参照してください。

キャッシュの一般的な使用例

通常、ジョブを実行するたびに依存関係やライブラリのようなコンテンツをダウンロードするのを避けるためにキャッシュを使用します。Node.jsパッケージ、PHPパッケージ、Ruby gems、Pythonライブラリなどをキャッシュできます。

例については、GitLab CI/CDテンプレートを参照してください。

同じブランチ内のジョブ間でキャッシュを共有

各ブランチのジョブに同じキャッシュを使わせるには、key: $CI_COMMIT_REF_SLUG

cache:
  key: $CI_COMMIT_REF_SLUG

この設定は、誤ってキャッシュを上書きすることを防ぎます。しかし、マージリクエストの最初のパイプラインは遅くなります。次にコミットがブランチにプッシュされると、キャッシュが再利用され、ジョブの実行速度が速くなります。

ジョブ単位およびブランチ単位のキャッシングを有効にしたい場合:

cache:
  key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"

ステージごと、ブランチごとのキャッシュを有効にするには:

cache:
  key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG"

異なるブランチのジョブ間でキャッシュを共有します。

すべてのブランチとすべてのジョブでキャッシュを共有するには、すべてに同じキーを使用してください:

cache:
  key: one-key-to-rule-them-all

ブランチ間でキャッシュを共有し、ジョブごとに固有のキャッシュを持つ場合:

cache:
  key: $CI_JOB_NAME

ジョブのキャッシュポリシーを制御するために変数を使用します。

GitLab 16.1 で導入されました

プルポリシーの違いによるジョブの重複を減らすために、CI/CD変数を使うことができます。

使用例:

conditional-policy:
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      variables:
        POLICY: pull-push
    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
      variables:
        POLICY: pull
  stage: build
  cache:
    key: gems
    policy: $POLICY
    paths:
      - vendor/bundle
  script:
    - echo "This job pulls and pushes the cache depending on the branch"
    - echo "Downloading dependencies..."

この例では、ジョブのキャッシュポリシーは次のとおりです:

  • pull-push です。
  • pull 他のブランチへの変更。

Node.jsの依存関係をキャッシュ

プロジェクトがnpmを使用して Node.js の依存関係をインストールする場合、次の例ではデフォルトのcache を定義して、すべてのジョブがそれを継承するようにしています。デフォルトでは、npm はキャッシュデータをホームフォルダ (~/.npm) に保存します。しかし、プロジェクトディレクトリの外にキャッシュすることはできません。代わりに、./.npm を使うように npm に指示し、ブランチごとにキャッシュします:

default:
  image: node:latest
  cache:  # Cache modules in between jobs
    key: $CI_COMMIT_REF_SLUG
    paths:
      - .npm/
  before_script:
    - npm ci --cache .npm --prefer-offline

test_async:
  script:
    - node ./specs/start.js ./specs/async.spec.js

ロックファイルからキャッシュキーを計算します。

cache:key:files を使用して、package-lock.jsonyarn.lock のようなロック・ファイルからキャッシュ・キーを計算し、多くのジョブで再利用することができます。

default:
  cache:  # Cache modules using lock file
    key:
      files:
        - package-lock.json
    paths:
      - .npm/

Yarnを使用している場合、yarn-offline-mirror を使用して、圧縮されたnode_modules tarballをキャッシュすることができます。圧縮するファイルが少なくなるため、キャッシュの生成がより速くなります:

job:
  script:
    - echo 'yarn-offline-mirror ".yarn-cache/"' >> .yarnrc
    - echo 'yarn-offline-mirror-pruning true' >> .yarnrc
    - yarn install --frozen-lockfile --no-progress
  cache:
    key:
      files:
        - yarn.lock
    paths:
      - .yarn-cache/

PHP の依存関係のキャッシュ

プロジェクトがComposer を使って PHP 依存関係をインストールする場合、以下の例ではデフォルトのcache を定義して、すべてのジョブがそれを継承するようにしています。PHP ライブラリモジュールはvendor/ にインストールされ、ブランチごとにキャッシュされます:

default:
  image: php:7.2
  cache:  # Cache libraries in between jobs
    key: $CI_COMMIT_REF_SLUG
    paths:
      - vendor/
  before_script:
    # Install and run Composer
    - curl --show-error --silent "https://getcomposer.org/installer" | php
    - php composer.phar install

test:
  script:
    - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never

Python 依存のキャッシュ

プロジェクトが Python の依存関係をインストールするためにpipを使っている場合、以下の例ではデフォルトのcache を定義し、すべてのジョブがそれを継承するようにしています。pip のキャッシュは.cache/pip/ で定義され、ブランチごとにキャッシュされます:

default:
  image: python:latest
  cache:                      # Pip's cache doesn't store the python packages
    paths:                    # https://pip.pypa.io/en/stable/topics/caching/
      - .cache/pip
  before_script:
    - python -V               # Print out python version for debugging
    - pip install virtualenv
    - virtualenv venv
    - source venv/bin/activate

variables:  # Change pip's cache directory to be inside the project directory since we can only cache local items.
  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"

test:
  script:
    - python setup.py test
    - pip install ruff
    - ruff --format=gitlab .

Ruby 依存のキャッシュ

プロジェクトがBundlerを使ってgemの依存関係をインストールする場合、次の例ではデフォルトのcache 。gem はvendor/ruby/ にインストールされ、ブランチごとにキャッシュされます:

default:
  image: ruby:2.6
  cache:                                            # Cache gems in between builds
    key: $CI_COMMIT_REF_SLUG
    paths:
      - vendor/ruby
  before_script:
    - ruby -v                                       # Print out ruby version for debugging
    - bundle config set --local path 'vendor/ruby'  # The location to install the specified gems to
    - bundle install -j $(nproc)                    # Install dependencies into ./vendor/ruby

rspec:
  script:
    - rspec spec

異なるgemを必要とするジョブがある場合は、グローバルcache 定義でprefix キーワードを使用します。この設定はジョブごとに異なるキャッシュを生成します。

例えば、テストジョブは本番環境にデプロイするジョブと同じgemsを必要としないかもしれません:

default:
  cache:
    key:
      files:
        - Gemfile.lock
      prefix: $CI_JOB_NAME
    paths:
      - vendor/ruby

test_job:
  stage: test
  before_script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install --without production
  script:
    - bundle exec rspec

deploy_job:
  stage: production
  before_script:
    - bundle config set --local path 'vendor/ruby'   # The location to install the specified gems to
    - bundle install --without test
  script:
    - bundle exec deploy

Goの依存関係をキャッシュ

プロジェクトがGo モジュールを使ってGo の依存関係をインストールする場合、次の例ではgo-cache テンプレートでcache を定義します。Go モジュールは${GOPATH}/pkg/mod/ にインストールされ、go のすべてのプロジェクトにキャッシュされます:

.go-cache:
  variables:
    GOPATH: $CI_PROJECT_DIR/.go
  before_script:
    - mkdir -p .go
  cache:
    paths:
      - .go/pkg/mod/

test:
  image: golang:1.13
  extends: .go-cache
  script:
    - go test ./... -v -short

キャッシュの可用性

キャッシュは最適化ですが、常に機能する保証はありません。キャッシュファイルを必要とするジョブごとにキャッシュファイルを再生成する必要があるかもしれません。

.gitlab-ci.yml](../yaml/index.md#cache)で[キャッシュを定義した後、キャッシュの可用性は以下に依存します:

  • ランナーのエクゼキューター・タイプ。
  • ジョブ間でキャッシュを受け渡すために異なる Runner を使用するかどうか。

キャッシュの格納場所

ジョブに対して定義されたすべてのキャッシュは、単一のcache.zip ファイルにアーカイブされます。ファイルがどこに保存されるかは Runner 設定で定義します。デフォルトでは、キャッシュは GitLab Runner がインストールされているマシンに保存されます。場所はまた、エクゼキューターのタイプによって異なります。

Runner エクゼキューターキャッシュのデフォルトパス
Shell内部では、gitlab-runner ユーザーのホーム・ディレクトリー下:/home/gitlab-runner/cache/<user>/<project>/<cache-key>/cache.zip.
Docker内部では、Dockerボリュームの下:/var/lib/docker/volumes/<volume-id>/_data/<user>/<project>/<cache-key>/cache.zip
Dockerマシン(オートスケールランナー)Docker Executorと同じです。

キャッシュとアーティファクトを使用して同じパスをジョブに保存する場合、キャッシュはアーティファクトよりも先にリストアされるため、キャッシュが上書きされる可能性があります。

キャッシュ・キー名

GitLab 15.0 で導入されました

グローバルフォールバックキャッシュキーを除き、キャッシュキーにサフィックスが追加されます。

例として、cache.key$CI_COMMIT_REF_SLUG に設定され、mainfeature の 2 つのブランチがあるとすると、以下の表は結果のキャッシュ・キーを表しています:

Branch nameキャッシュ・キー
mainmain-protected
featurefeature-non_protected
すべてのブランチで同じキャッシュを使用します。

GitLab 15.0 で導入されました

キャッシュキー名を使いたくない場合は、すべてのブランチ(protectedとunprotected)で同じキャッシュを使うことができます。

キャッシュキー名によるキャッシュの分離はセキュリティ機能であり、Developer ロールを持つすべてのユーザーが高度に信頼されている環境でのみ無効にしてください。

すべてのブランチで同じキャッシュを使用するには:

  1. 左のサイドバーで「検索」または「移動」を選択してあなたのプロジェクトを検索します。
  2. Settings > CI/CDを選択します。
  3. 一般的なパイプラインを拡大する。
  4. 保護ブランチに個別のキャッシュを使用する]チェックボックスをオフにします。
  5. 変更を保存を選択します。

アーカイブと抽出の仕組み

この例では、2つのジョブを2つの連続したステージで示しています:

stages:
  - build
  - test

default:
  cache:
    key: build-cache
    paths:
      - vendor/
  before_script:
    - echo "Hello"

job A:
  stage: build
  script:
    - mkdir vendor/
    - echo "build" > vendor/hello.txt
  after_script:
    - echo "World"

job B:
  stage: test
  script:
    - cat vendor/hello.txt

1台のマシンに1つのRunnerがインストールされている場合、プロジェクトのすべてのジョブは同じホスト上で実行されます:

  1. パイプライン開始。
  2. job A が実行されます。
  3. キャッシュが抽出されます(見つかった場合)。
  4. before_script が実行されます。
  5. script が実行されます。
  6. after_script が実行されます。
  7. cache が実行され、vendor/ ディレクトリがcache.zip にzip圧縮されます。このファイ ル は 、ラ ン ナ ー の 設 定に 基 づ い て デ ィ レ ク ト リ に 保 存 さ れ 、cache: key.
  8. job B が実行されます。
  9. キャッシュが抽出されます(見つかった場合)。
  10. before_script が実行されます。
  11. script が実行されます。
  12. パイプライン完了

1つのマシン上で1つのRunnerを使用することで、job Bjob A とは異なるRunner上で実行されるかもしれないというイシューは発生しません。このセットアップでは、ステージ間でキャッシュを再利用できることが保証されます。これは、同じランナー/マシンでbuild ステージからtest ステージまで実行される場合にのみ機能します。そうでない場合、キャッシュは利用できないかもしれません。

キャッシングのプロセスで、考慮すべき点もいくつかあります。

  • 別のキャッシュ設定を持つ他のジョブが同じzipファイルにキャッシュを保存していた場合、それは上書きされます。S3ベースの共有キャッシュが使用されている場合、ファイルはキャッシュキーに基づいたオブジェクトに追加でS3にアップロードされます。そのため、パスが異なるがキャッシュキーが同じ2つのジョブは、それらのキャッシュを上書きします。
  • cache.zip からキャッシュを展開する場合、zip ファイル内のすべてがジョブの作業ディレクトリ (通常はプルダウンされたリポジトリ) に展開されます。job A のアーカイブがjob B のアーカイブ内のものを上書きしても、ランナーは気にしません。

あるRunnerのために作成されたキャッシュは、別のRunnerで使用されるときには有効でないことが多いため、このように動作します。異なるRunnerは異なるアーキテクチャで実行されるかもしれません(例えば、キャッシュがバイナリファイルを含む場合)。また、異なるステップは異なるマシン上で実行されるランナーによって実行されるかもしれないので、これは安全なデフォルトです。

キャッシュのクリア

Runnerは既存のデータを再利用することでジョブの実行を高速化するためにキャッシュを使用します。これは時に一貫性のない動作につながることがあります。

キャッシュの新しいコピーから始めるには2つの方法があります。

キャッシュをクリアするにはcache:key

.gitlab-ci.yml ファイルのcache: key の値を変更します。次にパイプラインが実行されると、キャッシュは別の場所に保存されます。

手動でキャッシュをクリアします。

GitLab UIでキャッシュをクリアすることができます:

  1. 左のサイドバーで「検索」または「移動」を選択してあなたのプロジェクトを検索します。
  2. 左サイドバーでBuild > Pipelines を選択します。
  3. 右上隅で、Clear runner cachesを選択します。

次のコミットで、CI/CDジョブは新しいキャッシュを使用します。

note
手動でキャッシュをクリアするたびに、内部キャッシュ名が更新されます。名前はcache-<index>という形式を使い、インデックスは1ずつ増加します。古いキャッシュは削除されません。これらのファイルは、Runnerストレージから手動で削除できます。

トラブルシューティング

キャッシュの不一致

キャッシュの不一致がある場合は、以下の手順に従ってトラブルシューティングを行ってください。

キャッシュの不一致の理由修正方法
複数のスタンドアロンランナー(オートスケールモードではない)を、共有キャッシュなしで1つのプロジェクトにアタッチして使用しています。プロジェクトに1つのランナーだけを使用するか、ディストリビューション・キャッシュを有効にして複数のランナーを使用します。
分散キャッシュを有効にせずにオートスケールモードでランナーを使用します。分散キャッシュを使用するようにオートスケールランナーを設定します。
Runnerがインストールされているマシンのディスク容量が少ないか、分散キャッシュを設定している場合、キャッシュが保存されているS3バケットに十分な容量がありません。新しいキャッシュを保存できるように、スペースを確保してください。これを自動的に行う方法はありません。
異なるパスをキャッシュするジョブには同じkeyキャッシュアーカイブが異なる場所に保存され、間違ったキャッシュを上書きしないように、異なるキャッシュキーを使用してください。
ランナーでディストリビューション・ランナー・キャッシュを有効にしていません。 Shared = false を設定し、Runnerを再プロビジョニングしてください。

キャッシュの不一致の例1

プロジェクトに割り当てられたRunnerが1つだけの場合、キャッシュはデフォルトでRunnerのマシンに保存されます。

2つのジョブのキャッシュキーが同じでパスが異なる場合、キャッシュは上書きされます。例えば

stages:
  - build
  - test

job A:
  stage: build
  script: make build
  cache:
    key: same-key
    paths:
      - public/

job B:
  stage: test
  script: make test
  cache:
    key: same-key
    paths:
      - vendor/
  1. job A が実行されます。
  2. public/cache.zip としてキャッシュされます。
  3. job B が実行されます。
  4. 前のキャッシュがあれば、それを解凍します。
  5. vendor/cache.zip としてキャッシュされ、前のものを上書きします。
  6. 次にjob A が実行されると、job B のキャッシュが使用されますが、これは異なるものであるため、効果的ではありません。

このイシューを解決するには、ジョブごとに異なるkeys

キャッシュの不一致の例 2

この例では、プロジェクトに複数のRunnerが割り当てられていて、ディストリビューションキャッシュが有効になっていません。

2回目のパイプラインの実行時に、job Ajob B にキャッシュを再利用させます:

stages:
  - build
  - test

job A:
  stage: build
  script: build
  cache:
    key: keyA
    paths:
      - vendor/

job B:
  stage: test
  script: test
  cache:
    key: keyB
    paths:
      - vendor/

key が異なっていても、その後のパイプラインでジョブが異なる Runner で実行された場合、各ステージの前にキャッシュされたファイルが「クリーニング」される可能性があります。