以下の内容はhttps://kazuhira-r.hatenablog.com/entry/2025/08/12/003026より取得しました。


GitLab CI/CDのキャッシュの設定方法を確認する

これは、なにをしたくて書いたもの?

GitLab CI/CDのキャッシュについて、雰囲気しか見ていなかったのでちゃんとドキュメントを見ておこうかなと。

GitLab CI/CDのキャッシュ

ドキュメントとして見ておくものはこちらですね。

キャッシュとはなにか?ですが、GitLab CI/CDにおいてジョブがダウンロードして保存する1つ以上のファイルのことを指します。
同じキャッシュを使用する後続のジョブは、ファイルを再度ダウンロードする必要がないため実行速度が向上します。

A cache is one or more files a job downloads and saves. Subsequent jobs that use the same cache don’t have to download the files again, so they execute more quickly.

Caching in GitLab CI/CD | GitLab Docs

ここで、少しキーワードを振り返っておくと

  • ジョブ … タスクを実行するコマンドを定義したもの
  • ステージ … ジョブをグループ化したもの

これらを構成要素としたものがパイプライン、という感じですね。

CI/CD pipelines | GitLab Docs

話をキャッシュに戻します。

  • キャッシュはcacheキーワードを使用してジョブごとに定義する
  • 後続のパイプラインではキャッシュを利用できる
  • 依存関係が同一であれば、同じパイプラインの後続のジョブはキャッシュを使用できる
  • 異なるプロジェクト間でキャッシュを共有することはできない
  • デフォルトでは、ProtectedなブランチとProtectedではないブランチではキャッシュを共有しない
    • この設定は変更可能

Caching in GitLab CI/CD / Cache

このページではこの後、キャッシュのベストプラクティスや使用例に移っていくのですが、設定項目の説明をしないので
このまま読んでいっても???という感じになります。

1度こちらを読みましょう。

なお、ここではキャッシュの特徴として最大4つまでということが書かれています。

設定の意味をまずは押さえておきましょう。気にしておいた方がよさそうなものを挙げてみます。

  • cache:paths
  • cache:key
    • キャッシュを識別するための名前
    • 同じキャッシュキーを使用するジョブは、異なるパイプラインであっても同じキャッシュを使用する
    • 指定しなかった場合のデフォルトのキーはdefault
    • Predefined CI/CD variablesが利用可能
    • CI/CD YAML syntax reference / Job keywords / cache / cache:key
  • cache:key:files
    • 定義されたパスまたはパターンのいずれかに一致するファイルが変更された時に、新しいキーを生成する
    • キーはファイルパスから生成することになる
    • cache:key:filesは一部のキャッシュを再利用し、キャッシュを再構築する頻度を減らすことで実行速度を向上させる
    • pom.xmlpackage.jsonなどが指定される
    • CI/CD YAML syntax reference / Job keywords / cache / cache:key:files
  • cache:key:prefix
  • cache:when
    • ジョブのステータスにもとづいて、キャッシュをいつ保存するかを定義する
    • ジョブが成功した場合のみ保存する(デフォルト)、ジョブが失敗した場合のみ保存する、常に保存する、の3つから選択
    • CI/CD YAML syntax reference / Job keywords / cache / cache:when
  • cache:policy
    • キャッシュのアップロードとダウンロードの動作を定義する
    • デフォルトではジョブの開始時にキャッシュをダウンロードし、終了時にアップロードする(pull-push)
    • ダウンロードのみ(pull)、アップロードのみ(push)、とすることも可能
    • pullは同じキャッシュを使用するジョブを多数並列実行する場合に有効、pushはキャッシュを構築するジョブに利用する
    • CI/CD YAML syntax reference / Job keywords / cache / cache:policy
  • cache:fallback_keys

cache:untrackedとcache:unprotect以外、全部載せてしまったような気がします(笑)。

なんとなくちゃんとリファレンスを読まないとcache:paths、cache:key、cache:key:filesの違いがわかりにくいような
気もするのですが、こうやって見た後にこちらのページの使用例を見ると、だいぶ意味がわかるようになります。

Caching in GitLab CI/CD | GitLab Docs

ここからですね。

Caching in GitLab CI/CD / Common use cases for caches

たとえば、キャッシュのキーはあくまでキャッシュの名前なので、こう書くと同じブランチで同じキャッシュを見るように
なることがわかります。

cache:
  key: $CI_COMMIT_REF_SLUG

Caching in GitLab CI/CD / Common use cases for caches / Share caches between jobs in the same branch

つまり、名前を工夫すればどのブランチ・ジョブの間で共有するかを工夫できます。

Caching in GitLab CI/CD / Common use cases for caches / Share caches across jobs in different branches

その後は言語ごとの設定例が出てくるので参考にするとよいでしょうね。

キャッシュキーを特定のファイルから計算させることもできますが、この場合はキャッシュキーが指定したファイルの
ハッシュ値になります。

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

Caching in GitLab CI/CD / Common use cases for caches / Cache Node.js dependencies / Compute the cache key from the lock file

これだけだとブランチ間で共有されるので、それが嫌な場合はcache:key:prefixを指定してキャッシュキーを制御します。

キャッシュする対象は、プロジェクト内($CI_PROJECT_DIR)からの相対パスのものであるということを意識しておく
必要があります。

An array of paths relative to the project directory ($CI_PROJECT_DIR).

CI/CD YAML syntax reference / Job keywords / cache / cache:paths

あと、グローバルなキャッシュという表現が出てきますが

Caching in GitLab CI/CD / Inherit global configuration, but override specific settings per job

ここで使われているdefaultキーワードはcacheが設定されていないジョブにコピーされるものになるので、結局ジョブごとに
キャッシュの設定を書いていることになると押さえておけばよいと思います。

Each default keyword is copied to every job that doesn’t already have it defined. If the job already has a keyword defined, that default is not used.

CI/CD YAML syntax reference / Global keywords / default

つまり、GitLab CI/CDの「キャッシュがある場所」から指定のキーでルックアップしている感じですね。

キャッシュの保存場所は、デフォルトではRunnerのローカル上に保存され、場所はRunnerのExecutorがShell、Docker、
Docker Machineのいずれで動作しているかで変わるようです。

  • Shell Executor … /home/gitlab-runner/cache/<user>/<project>/<cache-key>/cache.zip
  • Docker Executor、Docker Machine … /var/lib/docker/volumes/<volume-id>/_data/<user>/<project>/<cache-key>/cache.zip

Caching in GitLab CI/CD / Availability of the cache / Where the caches are stored

Docker Executorの場合はキャッシュはvolumeとして表現されるということですね。

キャッシュのクリア方法についてはこちら。

Caching in GitLab CI/CD / Clearing the cache

キーを変更する方法と、GitLabのUIから操作する方法がありますが、キャッシュを「削除する」という意味では後者ですね。
前者は参照するキャッシュキーを変更することで無効化する、ということでしょう。

では、ドキュメントを見るのはこれくらいにして動かしてみましょうか。

環境

今回の環境はこちら。

$ sudo gitlab-rake gitlab:env:info

System information
System:         Ubuntu 24.04
Current User:   git
Using RVM:      no
Ruby Version:   3.2.5
Gem Version:    3.6.9
Bundler Version:2.6.5
Rake Version:   13.0.6
Redis Version:  7.2.9
Sidekiq Version:7.3.9
Go Version:     unknown

GitLab information
Version:        18.2.1
Revision:       baccadafcda
Directory:      /opt/gitlab/embedded/service/gitlab-rails
DB Adapter:     PostgreSQL
DB Version:     16.8
URL:            http://192.168.0.6
HTTP Clone URL: http://192.168.0.6/some-group/some-project.git
SSH Clone URL:  git@192.168.0.6:some-group/some-project.git
Using LDAP:     no
Using Omniauth: yes
Omniauth Providers:

GitLab Shell
Version:        14.43.0
Repository storages:
- default:      unix:/var/opt/gitlab/gitaly/gitaly.socket
GitLab Shell path:              /opt/gitlab/embedded/service/gitlab-shell

Gitaly
- default Address:      unix:/var/opt/gitlab/gitaly/gitaly.socket
- default Version:      18.2.1
- default Git Version:  2.50.1.gl1


$ gitlab-runner --version
Version:      18.2.1
Git revision: cc489270
Git branch:   18-2-stable
GO version:   go1.24.4 X:cacheprog
Built:        2025-07-28T12:43:39Z
OS/Arch:      linux/amd64

GitLabは192.168.0.6で動作しているものとします。

環境はTerraformで作成します。

$ terraform version
Terraform v1.12.2
on linux_amd64

サンプルコード用のJavaまわり。

$ java --version
openjdk 21.0.8 2025-07-15
OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.10 (5f519b97e944483d878815739f519b2eade0a91d)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 21.0.8, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "6.8.0-71-generic", arch: "amd64", family: "unix"

GitLabプロジェクトの作成とGitLab Runnerの登録

最初にGitLabプロジェクトとGitLab Runnerのトークンの作成を行います。

terraform.tf

terraform {
  required_version = "1.12.2"

  required_providers {
    gitlab = {
      source  = "gitlabhq/gitlab"
      version = "18.2.0"
    }
  }
}

main.tf

variable "root_access_token" {
  type      = string
  ephemeral = true
}

provider "gitlab" {
  token    = var.root_access_token
  base_url = "http://192.168.0.6/"
}

resource "gitlab_group" "sample_group" {
  name = "sample group"
  path = "sample-group"
}

resource "gitlab_project" "sample_app" {
  name         = "sample-app"
  namespace_id = gitlab_group.sample_group.id

  default_branch = "main"

  visibility_level = "private"

  auto_devops_enabled = false

  only_allow_merge_if_pipeline_succeeds            = true
  only_allow_merge_if_all_discussions_are_resolved = true
}

resource "gitlab_branch_protection" "main_branch" {
  project = gitlab_project.sample_app.id
  branch  = "main"

  allow_force_push = false

  merge_access_level     = "maintainer"
  push_access_level      = "no one"
  unprotect_access_level = "maintainer"
}

resource "gitlab_group_membership" "sample_user" {
  group_id     = gitlab_group.sample_group.id
  user_id      = gitlab_user.sample_user.id
  access_level = "owner"
}

resource "gitlab_user" "sample_user" {
  name     = "sample-user"
  username = "sample-user"
  password = "P@ssw0rd"
  email    = "sample-user@example.com"
}

resource "gitlab_personal_access_token" "sample_user" {
  user_id    = gitlab_user.sample_user.id
  name       = "access token"
  expires_at = "2025-09-11"

  scopes = ["api"]
}

resource "gitlab_user_runner" "group_runner" {
  runner_type = "group_type"

  group_id = gitlab_group.sample_group.id

  description = "sample group runner"
  untagged    = true
}

output "runner_authentication_token" {
  value     = gitlab_user_runner.group_runner.token
  sensitive = true
}

アクセストークンは環境変数で定義。

$ export TF_VAR_root_access_token=...

リソースを作成。

$ terraform init
$ terraform apply

GitLab Runnerのトークンを確認して

$ terraform output runner_authentication_token
"glrt-xxxxxxxxxx"

GitLab RunenrをGitLabに登録します。

$ RUNNER_TOKEN=...
$ sudo gitlab-runner register \
  --non-interactive \
  --url "http://192.168.0.6/" \
  --token "$RUNNER_TOKEN" \
  --executor "docker" \
  --docker-image ubuntu:24.04 \
  --description "sample group runner"

設定ファイルはこちら。

/etc/gitlab-runner/config.toml

concurrent = 1
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "sample group runner"
  url = "http://192.168.0.6/"
  id = 6
  token = "..."
  token_obtained_at = 2025-08-11T14:11:48Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  [runners.cache]
    MaxUploadedArchiveSize = 0
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "ubuntu:24.04"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    shm_size = 0
    network_mtu = 0

サンプルアプリケーションを作成して、キャッシュを使ったGitLab CI/CDパイプラインを動かす

それでは、サンプルアプリケーションを作成してGitLab CI/CDパイプラインを動かしてみましょう。

ローカルディレクトリーをGitリポジトリーとして初期化して、README.mdだけmainブランチに登録します。

$ git init --initial-branch=main
$ git config --local user.name "sample-user"
$ git config --local user.email "sample-user@example.com"
$ git remote add origin http://192.168.0.6/sample-group/sample-app.git
$ touch README.md
$ git add README.md
$ git commit -m "Initial commit"
$ git push --set-upstream origin main

サンプルコードを作成。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>sample-app</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.13.4</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

src/main/java/org/littlewings/gitlab/CalcService.java

package org.littlewings.gitlab;

public class CalcService {
    public int plus(int a, int b) {
        return a + b;
    }
}

src/test/java/org/littlewings/gitlab/CalcServiceTest.java

package org.littlewings.gitlab;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class CalcServiceTest {
    @Test
    void plus() {
        CalcService sut = new CalcService();

        Assertions.assertEquals(5, sut.plus(2, 3));
    }
}

GitLab CI/CDの設定も書いておきます。

.gitlab-ci.yml

stages:
  - build
  - test
  - deploy

default:
  image: maven:3.9.10-eclipse-temurin-21

workflow:
  rules:
    # Merge Requestを対象
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    # デフォルトブランチを対象
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    # Protectedブランチを対象
    - if: $CI_COMMIT_REF_PROTECTED == "true"
    # 手動実行
    - if: $CI_PIPELINE_SOURCE == "web"
    # それ以外は実行しない
    - when: never

compile:
  stage: build
  script:
    - mvn compile

test:
  stage: test
  script:
    - mvn test

packaging:
  stage: deploy
  script:
    - mvn package -DskipTests

こちらを新しいブランチにして登録。

$ git switch -c add-code
$ git add src pom.xml .gitlab-ci.yml
$ git commit -m 'add, sources'
$ git push origin HEAD

ワークフローを設定しているので、このブランチに対してMerge Requestを作成します。

workflow:
  rules:
    # Merge Requestを対象
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    # デフォルトブランチを対象
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    # Protectedブランチを対象
    - if: $CI_COMMIT_REF_PROTECTED == "true"
    # 手動実行
    - if: $CI_PIPELINE_SOURCE == "web"
    # それ以外は実行しない
    - when: never

するとパイプラインが動き出しますが、Mavenプラグインや依存ライブラリーは毎回ダウンロードされます。

mvn compileの時。

mvn testの時。

では、GitLab CI/CDのパイプライン設定をこう変更します。

.gitlab-ci.yml

stages:
  - build
  - test
  - deploy

variables:
  MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end"
  ## ダウンロード表示を出したくない場合は、--no-transfer-progressを入れる
  # MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end --no-transfer-progress"

default:
  image: maven:3.9.10-eclipse-temurin-21
  cache:
    key:
      files:
        - pom.xml
    paths:
      - .m2/repository

workflow:
  rules:
    # Merge Requestを対象
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    # デフォルトブランチを対象
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    # Protectedブランチを対象
    - if: $CI_COMMIT_REF_PROTECTED == "true"
    # 手動実行
    - if: $CI_PIPELINE_SOURCE == "web"
    # それ以外は実行しない
    - when: never

compile:
  stage: build
  script:
    - mvn ${MAVEN_CLI_OPTS} compile

test:
  stage: test
  script:
    - mvn ${MAVEN_CLI_OPTS} test

packaging:
  stage: deploy
  script:
    - mvn ${MAVEN_CLI_OPTS} package -DskipTests

今回はすべてのジョブに同じ設定を適用しました。

default:
  image: maven:3.9.10-eclipse-temurin-21
  cache:
    key:
      files:
        - pom.xml
    paths:
      - .m2/repository

pom.xmlの変更有無からキーを作成するようにしていますね。

キャッシュ対象は.m2/repositoryで、これをMavenのローカルリポジトリーとするようにしています。

variables:
  MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end"
  ## ダウンロード表示を出したくない場合は、--no-transfer-progressを入れる
  # MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end --no-transfer-progress"


...


compile:
  stage: build
  script:
    - mvn ${MAVEN_CLI_OPTS} compile

test:
  stage: test
  script:
    - mvn ${MAVEN_CLI_OPTS} test

packaging:
  stage: deploy
  script:
    - mvn ${MAVEN_CLI_OPTS} package -DskipTests

こうするのは、キャッシュ対象にできるのはプロジェクト内のディレクトリーだけだからですね。

ちなみに、Apache Maven--no-transfer-progressというオプションを指定すると、ダウンロードログを表示しなくなります。

  MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end"
  ## ダウンロード表示を出したくない場合は、--no-transfer-progressを入れる
  # MAVEN_CLI_OPTS: "-Dmaven.repo.local=.m2/repository --batch-mode --show-version --errors --fail-at-end --no-transfer-progress"

更新したファイルをコミットしてpush。

$ git add .gitlab-ci.yml
$ git commit -m 'add, cache'
$ git push origin HEAD

最初のジョブであるmvn compile時は変化がありませんが、

その後のmvn test時はその続きからの依存ライブラリー等のダウンロードになります。

2回目のパイプライン実行では、完全にダウンロードされなくなります。

この後、pom.xmlを変更すると新しくキャッシュが生成されることが確認できました。また、pom.xmlが変わらないと
ブランチが変わってもキャッシュが再利用されますね。これが嫌な場合はcache:key:prefixを使うのでしょう。

ひとまず、どういうものか感覚はわかったかなと思います。

おわりに

GitLab CI/CDのキャッシュの設定方法を確認してみました。

ドキュメントを斜め読みしているだけだと、設定で出てくるキーワードがよくわからなかったりしたのですが、.gitlab-ci.yml
リファレンスを落ち着いて読むとだいぶ理解が進みましたね。

これで扱えるようにはなったかなと思います。




以上の内容はhttps://kazuhira-r.hatenablog.com/entry/2025/08/12/003026より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14