1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actions キャッシュを利用するワークフローを自分で書く

Last updated at Posted at 2025-12-01

はじめに

特に新しい内容はありませんが、今のところの自分の理解を書き残しておきます。

GitHub Actions で、リモートブランチにプッシュされた時にテストを実行するとします。

この時、プッシュのたびにテスト実行のためのビルドが数分かけて行われるのは効率がよくないので、ビルドの時間を短くしたいです。

ここでは、GitHub Actions のキャッシュを使用して、テスト実行の時間短縮を図ります。

GitHub Actions のキャッシュについて

ドキュメントに以下の記載があります。

依存関係キャッシュのリファレンス - Restrictions for accessing a cache

  • Workflow runs can restore caches created in either the current branch or the default branch (usually main).
  • Workflow runs cannot restore caches created for child branches or sibling branches.
Google翻訳
  • ワークフロー実行は、現在のブランチまたはデフォルトブランチ(通常はmain)で作成されたキャッシュを復元できます。
  • 子ブランチまたは兄弟ブランチ用に作成されたキャッシュを復元することはできません。

つまり、同じブランチでプッシュを繰り返すときはよいですが、新しいブランチでコミットしてプッシュする、という操作ではキャッシュは働かない、ということになります。

新しいブランチでプッシュした時にもキャッシュが働いてほしいです。

対策

Rust 言語のプロジェクトで開発しているとします。

ビルドの処理を、外部依存クレートのビルドと、プロジェクト自身のビルドに分けて考えます。

外部依存クレートのビルドだけを行った時点の Docker レイヤーをキャッシュに保存して、次のビルドの時間短縮を図ります。

ここで、外部依存関係が頻繁に変わらないのであれば、デフォルトブランチ (ここでは main) でキャッシュを作成しておくと、他のブランチでも時間短縮が期待できるようになります。

外部依存クレートのビルドを行うだけのワークフローを作成することにします。

テストの実行

フォルダー構成

.
├── .github
│   ├── actions
│   │   └── test
│   │       ├── build
│   │       │   └── backend
│   │       │       └── action.yaml
│   │       └── run
│   │           └── action.yaml
│   └── workflows
│       ├── docker-build-test.yaml
│       └── test.yaml
├── Cargo.lock
├── Cargo.toml
├── apps
│   ├── backend
│   │   ├── .env
│   │   ├── Cargo.lock
│   │   ├── Cargo.toml
│   │   ├── Dockerfile
│   │   └── src
│   │       └── main.rs
│   └── db
│       ├── .env
│       └── Dockerfile
└── compose.yaml
パス 概要
apps/backend/ Rust のプロジェクトです。
.github/workflows/test.yaml プッシュされた時にテストを実行するワークフローです。
.github/workflows/docker-build-test.yaml 外部依存クレートのビルドだけを行うワークフローです。

ビルド処理

Docker イメージとそのキャッシュを作成します。

docker buildx コマンドを使用します。compose.yaml に、cache-fromcache-to を記述しており、type=gha で GitHub Actions キャッシュを使用することを指定しています。

cache-from は 2つ指定しています。

scope${GITHUB_REF_NAME} を含めている方が、現在のブランチ用のキャッシュの読み取りを試みることを表しています。

キャッシュがなければ、次に main ブランチ (デフォルトブランチ) のキャッシュの読み取りが試みられます。

cache-to は現在のブランチ用のキャッシュを作成する、とだけ記述しています。

main ブランチでビルドを行いキャッシュが作成されると、これは前述のとおり他のブランチからでも読み取ることができるので、新しいブランチ用のキャッシュとなります。

.github/actions/test/build/backend/action.yaml
name: Build backend
description: Build backend docker image for test

runs:
  using: composite
  steps:
    - name: Build backend image
      shell: bash
      run: |
        docker buildx bake backend \
            -f ./compose.yaml \
            --load
      env:
        BUILD_TARGET: test
compose.yaml
services:
  backend:
    image: myapp:0.1.0
    build:
      target: ${BUILD_TARGET:-development}
      context: apps/backend
      dockerfile: Dockerfile
      x-bake:
        cache-from:
          - type=gha,scope=myapp-${BUILD_TARGET:-development}-backend-${GITHUB_REF_NAME}
          - type=gha,scope=myapp-${BUILD_TARGET:-development}-backend-main
        cache-to: type=gha,scope=myapp-${BUILD_TARGET:-development}-backend-${GITHUB_REF_NAME},mode=max
    volumes:
      - ./apps/backend:/app
      - cargo_target:/home/debian/target
      - cargo_registry:/home/debian/.cargo/registry
    ports:
      - "3000:3000"
    env_file:
      - ./apps/backend/.env
    command: bacon --job run-long --headless

volumes:
  cargo_target:
  cargo_registry:

Dockerfile はマルチステージビルドの形式にしています。

ステップの設定で環境変数 BUILD_TARGET=test としているので、test ステージのビルドが行われます。

Cargo.toml、Cargo.lock、main.rs の 3つのファイルでビルドを行います。これが、外部依存クレートのみでのビルドを表します。

RUN cargo build の Docker レイヤーのキャッシュが作成されることで、外部依存関係が変わらない間、ビルドが省略されるようになります。

apps/backend/Dockerfile
FROM rust:1.91.1-slim-trixie AS base

RUN apt-get update && \
    apt-get install -y libssl-dev pkg-config && \
    rm -rf /var/lib/apt/lists/*

RUN useradd -m debian



FROM base AS development

RUN cargo install --locked bacon

# (中略)


FROM base AS test

USER debian

RUN mkdir -p /home/debian/target && \
    mkdir -p /home/debian/.cargo/registry

ENV CARGO_TARGET_DIR=/home/debian/target
ENV CARGO_HOME=/home/debian/.cargo

WORKDIR /app

COPY Cargo.toml .
COPY Cargo.lock .

RUN mkdir -p src && \
    echo "fn main() {}" > src/main.rs

RUN cargo build

ワークフロー(テスト実行)

テストを実行します。

Build backend のステップで、上述のアクション ./.github/actions/test/build/backend を使用しています。

キャッシュがあれば読み取ります。なければ改めてビルドを行います。どちらの場合でも、ここで Docker イメージが作成されます。これは、「外部依存クレートのビルドまでは終わっている状態」のイメージです。

Run test のステップでテストを実行します。Build backend で作成した Docker イメージを使用します。

「外部依存クレートのビルドまでは終わっている状態」のイメージをキャッシュから読み取るようにすることで、テストを開始できるようになるまでの時間を短くする、というのが狙いです。

.github/workflows/test.yaml
name: test
run-name: Run test
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container

      - name: Expose GitHub Runtime
        uses: crazy-max/ghaction-github-runtime@v3

      - name: Build backend
        uses: ./.github/actions/test/build/backend

      - name: Run test
        uses: ./.github/actions/test/run
.github/actions/test/run/action.yaml
name: Run tests
description: Run tests using pre-built Docker images

runs:
  using: composite
  steps:
    - name: Run test
      shell: bash
      run: |
        docker compose up -d db
        docker compose run --rm backend cargo test -- --test-threads=1

        docker compose down

ワークフロー(ビルド)

手動でビルド処理を開始できるようにするためのワークフローです。

main ブランチ (デフォルトブランチ) でのビルド処理でキャッシュを作成することにより、それ以降の新しいブランチでも「外部依存クレートのビルドまでは終わっている状態」のイメージを使えるようにします。

.github/workflows/docker-build-test.yaml
name: docker-build-test
run-name: Build docker images for test 
on: [workflow_dispatch]
jobs:
  build-backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container

      - name: Expose GitHub Runtime
        uses: crazy-max/ghaction-github-runtime@v3

      - name: Build backend
        uses: ./.github/actions/test/build/backend

補足

crazy-max/ghaction-github-runtime

ワークフローに以下のステップを追加しています。

      - name: Expose GitHub Runtime
        uses: crazy-max/ghaction-github-runtime@v3

これは、GitHub Actions キャッシュに接続できるようにする (認証を通す) 役割をするものです。

GitHub Actions キャッシュへの認証を行う何らかの仕組みを使用しないと、docker buildx コマンドはキャッシュの読み書きを行いません。

docker/build-push-action

この記事では docker buildx コマンドを使用していますが、もっと単純に GitHub Actions キャッシュを利用したければ、docker/build-push-action を使用するのがよいです。

個人的に、Docker に関連する設定を compose.yaml に集めたかったので、docker buildx コマンドを使用しました。

本番用ビルド

目的

テスト実行のためのビルドは cargo build で行いますが、本番デプロイ用のビルドは cargo build --release になります。

テスト用とは別のキャッシュを作成できるようにします。

※実際にデプロイできるかは確認していないので、参考程度ということでお願いします。

フォルダー構成

.
├── .github
│   ├── actions
│   │   ├── production
│   │   │   └── build
│   │   │       └── backend
│   │   │           └── action.yaml
│   │   :
│   └── workflows
│       ├── docker-build-production.yaml
:       :

ビルド処理

テスト実行用のアクションとほぼ同じです。

違いは、環境変数 BUILD_TARGET=production としている部分です。

.github/actions/production/build/backend/action.yaml
name: Build backend
description: Build backend docker image for production

runs:
  using: composite
  steps:
    - name: Build backend image
      shell: bash
      run: |
        docker buildx bake backend \
            -f ./compose.yaml \
            --load
      env:
        BUILD_TARGET: production

ワークフロー(ビルド)

ワークフローは、使用するアクションを本番用にしています。

main ブランチで手動により開始することで、キャッシュを作成します。

.github/workflows/docker-build-production.yaml
name: docker-build-production
run-name: Build docker images for production
on: [workflow_dispatch]
jobs:
  build-backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container

      - name: Expose GitHub Runtime
        uses: crazy-max/ghaction-github-runtime@v3

      - name: Build backend
        uses: ./.github/actions/production/build/backend

Dockerfile は builderproduction のステージを追加しています。

cargo build --release を 2回行います。

1回目は外部依存クレートのみでのビルドです。

2回目はプロジェクト自身のソースコードを含めたビルドです。

1回目の Docker レイヤーがキャッシュに保存されていれば、この部分が短い時間で終わるので、ソースコードの変更分だけのビルド時間になります。

apps/backend/Dockerfile
    :

FROM base AS builder

USER debian

RUN mkdir -p /home/debian/target && \
    mkdir -p /home/debian/.cargo/registry

ENV CARGO_TARGET_DIR=/home/debian/target
ENV CARGO_HOME=/home/debian/.cargo

WORKDIR /app

COPY Cargo.toml .
COPY Cargo.lock .

RUN mkdir -p src && \
    echo "fn main() {}" > src/main.rs

RUN cargo build --release

COPY . .

RUN cargo build --release



FROM debian:trixie-slim AS production

COPY --from=builder /home/debian/target/release/backend /app/backend

WORKDIR /app

注意

GitHub Actions のキャッシュは、サイズと期間に制限があります。

依存関係キャッシュのリファレンス - Default limits

  • GitHub will remove any cache entries that have not been accessed in over 7 days.
  • the total size of all caches in a repository is limited. By default, the limit is 10 GB per repository
Google翻訳
  • GitHubは、7日間以上アクセスされていないキャッシュエントリを削除します。
  • リポジトリ内のすべてのキャッシュの合計サイズには制限があります。デフォルトでは、リポジトリあたりの制限は10GBです

おわりに

main ブランチで外部依存クレートのみでのビルドを行い、キャッシュを作成することで、新しいブランチでもビルド時間を短縮することができました。

ようやくプロダクトのリポジトリに導入できそうな感じになってきたので、活用していきたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?