この記事は、ニフティグループ Advent Calendar 2025 19日目の記事です。
はじめに
おはようございます。IWS です
2025年アドベントカレンダーも19日目の記事です。
19日目のこの記事では、 GitHub Actions を使った CI について書こうかなと思います。
GitHub Actions での CI
私のチームでは以下のような構成のコンテナ環境を使用しています。

apl コンテナを中心に複数のコンテナを使う構成で開発しています。テスト実行には各コンテナが必要なため、これまで GitHub Actions で CI を組んでもビルドを含めて10分以上かかるような状態でした。
何かあって CI を回すたびに10分待たなければいけないというのはかなりのストレスなため少しでも早くできるように試していきたいと思います。
イメージの準備
テスト実行にコンテナのDB等が必要な都合、GitHub Actions 上でもビルドが必要になります。CI にかかる時間の半分はビルドにかかった時間です。
今回は必要なコンテナが4つあり、すべてをビルドするのはかなりの時間がかかるため少しでも CI 実行時間を抑えられるようにしていきます。
GHCR にあらかじめイメージを保存しておく
もしほとんど変更がなく都度ビルドする必要がないような場合は GHCR (GitHub Container Registry) にイメージをあらかじめ保存しておくことで CI 時のビルドを省くことができます。GHCR は GitHub が提供している コンテナレジストリです。
以下のコマンドでイメージを push することができます。
|
1 2 3 4 |
$ docker build --no-cache -f stub.Dockerfile -t stub . $ echo "<PAT>" | docker login ghcr.io -u <GitHubユーザ名> --password-stdin $ docker tag stub:latest ghcr.io/<org>/stub:latest $ docker push ghcr.io/<org>/stub:latest |
GHCR ではストレージや pull, push などのデータ転送の無料枠を超えた分の利用については課金が必要になるのですが、GitHub Actions からの利用に関しては Free としてカウントされるため課金を気にせず使用ができます。(データ転送のみ、詳しくは https://docs.github.com/ja/billing/concepts/product-billing/github-packages)
Push ができると GitHub の Packages からイメージの一覧を見ることができます。

WF からは↓のようにすればイメージを pull できます。ログインの action を呼びだし docker pull コマンドをするだけです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
jobs: ci: step: - name: ghcr login uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: stub image pull from ghcr run: | docker pull ghcr.io/${{ github.repository_owner }}/stub:latest docker tag ghcr.io/${{ github.repository_owner }}/stub:latest stub |
複数ビルドするときは matrix で並列にビルド
WF 上で複数イメージのビルドが必要な場合は matrix を使い並列にビルドすることで WF の実行時間を抑えることができます。
GitHub Actions では job ごとに別のランナーで処理されるため、通常 job A でビルドしたイメージを job B で使用することはできません。そのため GHCR などに一度イメージを保存して job B で pull するといった方法が必要になる……
と思っていたのですが「同じ key, 同じ path のキャッシュを使うことでファイルの共有をする」という方法があるそうでこちらの記事を参考にやってみました。
【裏技】別ファイルに切り出した Job 間で Docker イメージを共有し,高速に GitHub Actions をぶん回す
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
jobs: # イメージの並列ビルド build: runs-on: ubuntu-latest timeout-minutes: 20 strategy: fail-fast: false matrix: include: - name: apl tag: apl context: . dockerfile: ./apl.Dockerfile.dev - name: db tag: db context: . dockerfile: db.Dockerfile steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 - name: Build Docker image uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: ${{ matrix.context }} file: ${{ matrix.dockerfile }} push: false load: true tags: ${{ matrix.tag }} cache-from: type=gha,scope=${{ matrix.name }} cache-to: type=gha,mode=max,scope=${{ matrix.name }} # ビルドしたイメージを tar としてキャッシュに保存する - name: Save the built image as a tar file run: docker save -o ${{ matrix.name }}.tar ${{ matrix.tag }} - name: Save the tar file to the cache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ matrix.name }}.tar key: image-cache-${{ matrix.name }}-${{ github.sha }} # イメージの並列ビルド run: runs-on: ubuntu-latest needs: build steps: # キャッシュから tar を復元 - name: Restore apl image cache uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: apl.tar key: image-cache-apl-${{ github.sha }} # Save the tar file to the cache の key に合わせる fail-on-cache-miss: true # db でも同じことをする # tar から Docker イメージを復元 - name: Load images from tar files run: | docker load -i apl.tar docker load -i db.tar docker image ls |
のようにすることで
- 並列にイメージをビルド
- tar としてイメージをキャッシュに保存
- 後続の job で tar からイメージを復元
し、コンテナ起動時に作成されたイメージを使うことができました。


キャッシュも保存されるため2回目移行はさらに早くなるのも good ポイントですね。
テスト実行
おまけのようなものですが、WF 上でどうテストを実行しているかも書いておきます。
コンテナ起動〜テストコマンド実行には @devcontainers/cli を使用しました。devcontainer up で立ち上げ、 devcontainer exec でコマンドの実行が行えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- name: Install Dev Container CLI run: npm install -g @devcontainers/cli # Dev Container 起動 - name: Build and run Dev Container run: devcontainer up --workspace-folder . --config .devcontainer/ci/devcontainer.json # lint, test - name: Run lint id: lint continue-on-error: true run: devcontainer exec --container-id $(docker ps -aq --filter "name=<コンテナ名>") --workspace-folder . <lint コマンド> - name: Run test run: devcontainer exec --container-id $(docker ps -aq --filter "name=<コンテナ名>") --workspace-folder . <teset コマンド> # continue-on-error では job が成功扱いになってしまうため明示的に失敗させる - name: if lint failed if: ${{ steps.lint.outcome == 'failure' }} run: exit 1 |
lint に失敗した際も test の実行をしてもらいたいため lint の step に continue-on-error: true を追加しています。エラーを無視して後続 step を実行する設定です。ただし、このままだと lint 失敗時も job が成功となってしまうため if lint failed step で落とすようにしています。
まとめ
ビルドしつつもかかる時間を抑えて CI を実装する話を書いてみました。
ちなみに現在は CI に約5分程度かかっています。最初の頃の10分↑と比べれば半分以下にはなったのですがやはりまだ長いなとは感じるので他にも短縮要素がないか試してみようと思います!
明日は namiki_ さんのアドベントカレンダー20日目です!おたのしみに!


