GitHubActionsで簡単にDockerfileのキャッシュ利用するにはどうすればいいの！

## はじめに
自分で作ったシステム（スプレッドシートの家計簿を更新＆画像で報告）を実行するのにGitHub Actionsを使用しているんですが、これが遅くてちょっとイラッとしていました。

なんとなく原因の見当はついていて、**docker compose使用時のビルド周り**で遅くなってるんだろうなと。
dockerでの環境構築を毎回同じようにさせるのは無駄の極みすぎるので、キャッシュの利用をしようと調べてみました。

今回のDockerfileでは`ARG`を使用しており、この変数に値を渡すのに少し手間取ったのでそこにも触れます。

　
初心者の備忘録なので、間違い等あればぜひご指摘お願いします。


## 結論
GitHub Actionsにて、`docker/setup-buildx-action@v3`と`docker/build-push-action@v6`を利用することでキャッシュ利用できました。

```yaml:run.yml
        # 後のstepで使うため、環境変数に設定
      - name: create UID & GID
        run: |
          echo "UID=$(id -u)" >> $GITHUB_ENV
          echo "GID=$(id -g)" >> $GITHUB_ENV
          echo "USERNAME=user" >> $GITHUB_ENV
          echo "GROUPNAME=user" >> $GITHUB_ENV

        # キャッシュの利用にbuildxが必要なため実行
      - name: activate builx
        uses: docker/setup-buildx-action@v3

        # build-push-actionでGitHubActions上でキャッシュを使えるようにする
      - name: setting build-push-action
        uses: docker/build-push-action@v6
        with:
          load: true
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            UID=${{ env.UID }}
            GID=${{ env.GID }}
            USERNAME=${{ env.USERNAME }}
            GROUPNAME=${{ env.GROUPNAME }}

      - name: Docker-compose build & run
        # docker composeを使用時、キャッシュ利用するため--build-argでもう一度値を渡す
        run: |
          docker compose build --build-arg UID=${{ env.UID }} --build-arg GID=${{ env.GID }} --build-arg USERNAME=${{ env.USERNAME }} --build-arg GROUPNAME=${{ env.GROUPNAME }}
          docker compose up -d
```

```yaml:docker-compose.yml
version: '3'
services:
  web:
    restart: always
    build:
      context: .
    container_name: family-finance
    tty: true
    stdin_open: true
    working_dir: '/workspace/'
    volumes:
      - .bashrc:/home/user/.bashrc:ro
      - .:/workspace
    env_file:
      - .env

```

```docker:Dockerfile
FROM python:3.11


RUN apt-get update && \
    apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG=ja_JP.UTF-8
ENV LANGUAGE=ja_JP:ja
ENV LC_ALL=ja_JP.UTF-8
ENV TZ=JST-9
ENV TERM=xterm

RUN apt-get install -y vim \
    gcc \
    less \
    poppler-utils \
    poppler-data \
    oathtool

WORKDIR /workspace/
COPY requirements.txt .
COPY set-up-chdriver.sh .
RUN pip install --upgrade pip setuptools
RUN pip install -r ./requirements.txt
RUN chmod +x ./set-up-chdriver.sh
RUN sh ./set-up-chdriver.sh

ARG UID
ARG GID
ARG USERNAME
ARG GROUPNAME
RUN groupadd -f -g ${GID} ${GROUPNAME} && \
    useradd -m -s /bin/bash -u ${UID} -g ${GID} ${USERNAME}
USER ${USERNAME}
```

### 実行結果
初期実行時　2m29s
![スクリーンショット 2025-02-12 9.01.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1426862/2c610f08-6cd7-21dc-5a97-e4f5c0046a47.png)

キャッシュ利用時　37s
![スクリーンショット 2025-02-12 9.02.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1426862/fbef894a-a954-edd4-711f-503c2f7908c8.png)


## どうすればキャッシュ利用
[Docker Buildx](https://matsuand.github.io/docs.docker.jp.onthefly/buildx/working-with-buildx/)を使います。Buildxとは、Dockerコマンドを拡張したもので単体のDockerよりも柔軟で高機能なものになります。その機能の中にキャッシュの管理があるので、Buildxからイメージを作成、キャッシュし、docker composeで使用するという流れにしました。
GitHub Actions上で`docker/setup-buildx-action@v3`と`docker/build-push-action@v6`という外部アクションを使用すればOKです。


### docker/build-push-action@v6のオプション
※`List`型は改行区切りの文字列
| オプション名 | 型 | 詳細 |
|:---:|:---:|:---|
| load | bool | buildxでのレイヤーをdockerへ公開するかどうか設定する。 |
| cache-from | List | ビルドに指定した外部キャッシュソースを使用する。`type=gha`としてGitHubリポジトリをソースに設定する。 |
| cache-to | List | ビルドキャッシュを指定した外部キャッシュ先にエクスポートする。`type=gha`として外部キャッシュ先をGitHubリポジトリとする。`mode=max`はビルドイメージの全レイヤーをキャッシュし、`mode=min`の場合は最終ビルドステージにあるレイヤーのみエクスポートする。 |
| build-args | List | ビルドプロセスに値を渡す。Dockerfileに`ARG`を設定しておき渡せるようにしておく。 |


## ちなみに


### なぜDockerfileで`ARG`を使ったのか
rootユーザーを避けて一般ユーザーでの実行をするのに、実行環境でのユーザーIDとグループIDで設定するよう`ARG`を使いました。固定値でも良かったのですが、練習も兼ねて実行環境ごとに受け渡す形としています。


### `ARG`に値を渡すために環境変数を利用する
GitHubActions内での[環境変数の利用](https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-an-environment-variable)は、step内で`$GITHUB_ENV`に流し込むと後続step内で使うことができます。

```yaml
      - name: create UID & GID
        run: |
          echo "UID=$(id -u)" >> $GITHUB_ENV
          echo "GID=$(id -g)" >> $GITHUB_ENV
          echo "USERNAME=user" >> $GITHUB_ENV
          echo "GROUPNAME=user" >> $GITHUB_ENV
```

```yaml
      - name: setting build-push-action
        uses: docker/build-push-action@v6
        with:
          load: true
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            UID=${{ env.UID }}
            GID=${{ env.GID }}
            USERNAME=${{ env.USERNAME }}
            GROUPNAME=${{ env.GROUPNAME }}
```


## 参考
https://blog.mmmcorp.co.jp/2023/08/15/github-actions-cache/

https://ryo-koike.com/ja/blog/github-actions-buildx-cache/

https://github.com/docker/build-push-action?tab=readme-ov-file