GitHub ActionsでDockerビルド（Dockerレイヤーによるキャッシュ）を高速化！

# Docker BuildxとGitHub Actionsでビルド時間を短縮する方法

GitHub ActionsでDockerイメージをビルドする際に、ビルド時間を大幅に短縮する方法をご紹介します。特に、Docker Buildxというツールとキャッシュの活用方法に焦点を当てて解説していきます。

## はじめに：Docker Buildxって何？

Docker Buildxは、Dockerイメージを効率的にビルドするための拡張ツールです。複数のプラットフォーム向けのイメージを同時にビルドできたり、ビルドキャッシュを活用してビルド時間を短縮できたりと、とても便利な機能があります。

## キャッシュの重要性

Dockerイメージのビルドでは、毎回全ての手順を実行すると時間がかかります。キャッシュを使うと、変更がない部分は以前のビルド結果を再利用できるので、ビルド時間を大幅に短縮できます。これは特に、CIサーバーでの継続的なビルドを行う際に重要です。

## GitHub Actionsでのキャッシュ活用

それでは、実際のGitHub Actionsの設定を見ていきましょう。

```yaml
# Dockerキャッシュをセットアップ
- name: Cache Docker layers
  uses: actions/cache@v4
  with:
    path: /tmp/.buildx-cache-ecs
    key: ${{ runner.os }}-buildx-ecs-${{ github.sha }}
    restore-keys: |
      ${{ runner.os }}-buildx-ecs-
```

- `uses: actions/cache@v4`: これは、GitHub提供の公式キャッシュアクションを使用することを指定しています
- `path: /tmp/.buildx-cache-ecs`: キャッシュを保存する場所を指定しています。この場合、一時ディレクトリ(`/tmp/`)内の`.buildx-cache-ecs`というフォルダにキャッシュが保存されます
- `key: ${{ runner.os }}-buildx-ecs-${{ github.sha }}`: これはキャッシュを識別するためのユニークなキーです
- `restore-keys: | ${{ runner.os }}-buildx-ecs-`:
   - 完全に一致するキャッシュが見つからない場合に使用する、部分的に一致するキーのリストです
   - この場合、OSとプロジェクト識別子が一致する最新のキャッシュを探します
- `${{ github.sha }}`: コミットID(ハッシュ値)が設定されます
- `注意`: キャッシュ化されるまで少々時間がかかるため、再度コミットした後にキャッシュが効いてないことがある。この場合は時間を空けて、確認してみてください

## Dockerイメージのビルド

次に、実際のDockerイメージのビルド部分を見てみましょう。

```yaml
# バックエンドのDockerイメージをビルド
- name: Build Backend Image
  run: |
    docker buildx create --use
    docker buildx build \
      --platform linux/arm64 \
      -t ${{ secrets.PROJECT }}-private-repository-${{ inputs.env_var }}:${{ github.sha }} \
      -f docker/${{ inputs.env_var }}/Dockerfile \
      --cache-from type=local,src=/tmp/.buildx-cache-ecs \
      --cache-to type=local,dest=/tmp/.buildx-cache-ecs-new,mode=max \
      --load \
      .
```

このステップでは、Docker Buildxを使ってイメージをビルドしています。

- `docker buildx create --use`: Buildxのビルダーインスタンスを作成します
- `--platform linux/arm64`: ARM64アーキテクチャ向けのイメージをビルドすることを指定しています
- `-t ...`: ビルドするイメージの名前とタグを指定します
- `-f ...`: 使用するDockerfileのパスを指定します
- `--cache-from`: 前回のビルドで保存したキャッシュを/tmp/.buildx-cache-ecsから読み込みます
- `--cache-to`: 今回のビルド結果を/tmp/.buildx-cache-ecs-newに新しいキャッシュとして保存します
- `--load`: ビルドしたイメージをDocker engineにロードします

## キャッシュの検索と利用プロセス

`--cache-from type=local,src=/tmp/.buildx-cache-ecs \` で前回のキャッシュを見つけて利用する方法は下記の通りです。

1. キャッシュの検索:
   「actions/cache@v4」より、GitHub Actionsは `key` で完全一致を探し、なければ `restore-keys` で部分一致を探します。キャッシュの検索に成功すると、ログに「Cache restored successfully」となります

    ```yml
    Run actions/cache@v4
    Received 121634816 of 439686615 (27.7%), 116.0 MBs/sec
    Received 348127232 of 439686615 (79.2%), 165.8 MBs/sec
    Cache Size: ~419 MB (439686615 B)
    /usr/bin/tar -xf /home/runner/work/_temp/58061355-ea17-4f88-9e6c-9a1f5be434bc/cache.tzst -P -C /home/runner/work/aws_web --use-compress-program unzstd
    Received 439686615 of 439686615 (100.0%), 139.6 MBs/sec
    Cache restored successfully
    Cache restored from key: Linux-buildx-ecs-104a1876bbdd5336f0aceaecd7bcff8eb05c98e1
    ```

    下記ログの場合、`Linux-buildx-ecs-53199c90c8db91319abc6b4209bdc8dedbd53693`が高速化によるキャッシュ化対象のkey。`Linux-buildx-ecs-7a6fcd3c6c2c9166f3fd9c9398752cb711642f51`は、今回のビルドで発生したキャッシュを保管します。
    ```yml
    Run actions/cache@v4
    ~~ 省略 ~~
    with:
        path: /tmp/.buildx-cache-ecs
        key: Linux-buildx-ecs-53199c90c8db91319abc6b4209bdc8dedbd53693
        restore-keys: Linux-buildx-ecs
    ~~ 省略 ~~
    Cache restored from key: Linux-buildx-ecs-7a6fcd3c6c2c9166f3fd9c9398752cb711642f51
    ```

2. キャッシュの復元:
   マッチするキャッシュが見つかると、`path`（/tmp/.buildx-cache-ecs）にキャッシュデータを復元します

3. Dockerビルドでのキャッシュ利用:
`--cache-from type=local,src=/tmp/.buildx-cache-ecs \` で復元されたキャッシュを使用します。`type=local` はローカルファイルシステム上のキャッシュを使用します。`src=/tmp/.buildx-cache-ecs` はキャッシュの場所を指定します

4. 実際の動作:
初回ビルド: キャッシュなし、全レイヤーを新規ビルド
2回目以降:
    - コード変更なし: 前回キャッシュ使用、ビルド時間短縮
    - 一部変更: 変更なし部分はキャッシュ使用、変更部分のみ再ビルド

この仕組みでビルド時間を短縮し、CI/CDパイプラインの効率を向上させます。

## キャッシュの更新

最後に、次回のビルドのためにキャッシュを更新します。

```yaml
# キャッシュを移動
- name: Move cache
  run: |
    rm -rf /tmp/.buildx-cache-ecs
    mv /tmp/.buildx-cache-ecs-new /tmp/.buildx-cache-ecs
```

このステップでは、古いキャッシュを削除し、新しく生成されたキャッシュを所定の位置に移動しています。これにより、次回のビルドで最新のキャッシュを使用できるようになります。

## まとめ

以上が、GitHub ActionsでDocker Buildxとキャッシュを活用してビルド時間を短縮する方法です。この設定を使うことで、特に大規模なプロジェクトや頻繁にビルドが発生する環境で、大幅な時間短縮が期待できます。

キャッシュの活用は、CI/CDパイプラインの効率化において非常に重要な要素です。ぜひ、皆さんのプロジェクトでも試してみてください！