🐳

Github Actionsでのdocker buildでキャッシュを有効にする

5 min read

以下を実現するGithub Actionsを作る方法を紹介する。

  • Dockerイメージのビルドには BuildKit を使う
  • Github Actionsのもつキャッシュの仕組みを有効にする
  • DockerイメージのタグにはYYYYMM-(コミットハッシュの頭7桁)のように、コミットごとにユニークになる値を使う
  • mainブランチをビルドする際には、latestタグも付与する

このBuildKitとGithub Actionsのキャッシュを組み合わせる方法は、Dockerの公式ドキュメントに、Optimizing the workflow として掲載されている。

https://docs.docker.com/ci-cd/github-actions/#optimizing-the-workflow

これに自分のアレンジを加えた方法として紹介する。

まず、BuildKitを有効にするには、以下のdocker/setup-buildx-action@v1を先に組み込む。

      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1

次にactions/cache@v2をBuildKitのキャッシュとして使うファイルを引数に組み込む。${{ github.ref }}にはブランチ名が入るため、これをresore-keysにいれておくと、ブランチ内でキャッシュが効くようになる。さらに、mainブランチも指定しておくことで、mainブランチ以外のfeature/xxxブランチであったとしても、mainブランチのキャッシュを使えるようになる。

      - name: Cache Docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ github.ref }}-${{ github.sha }}
          restore-keys: |
            ${{ github.ref }}-${{ github.sha }}
            ${{ github.ref }}
            refs/head/main

タグは別途シェルスクリプトのステップで作成する。ステップ間の値渡しには環境変数を使う。この例では、YYYYMM-HASH先頭7文字をタグにし、さらにmainブランチの場合にはカンマ区切りでlatestタグも追加している。

env:
  IMAGE: 74th/githjub-actions-docker-build-with-cache
jobs:
  build:
    ...
    steps:
      ...

      - name: create tag
        run: |
          SHA=${{ github.sha }}
          TAG=${IMAGE}:$(TZ=UTC-9 date '+%Y%m')-${SHA:0:7}
          if [ "${{ github.ref }}" == "refs/heads/main" ]; then
            LATEST=${IMAGE}:latest
            echo "TAGS=$TAG,$LATEST" >> $GITHUB_ENV
          else
            echo "TAGS=$TAG" >> $GITHUB_ENV
          fi
          echo TAG $TAG

docker/build-push-action@v2 を組み込んでdocker buildを行う。ここで、先のステップのキャッシュのパスと、作成したタグを組み込む。tagsにはカンマ区切りで複数与えることができる。

      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: ./
          file: ./Dockerfile
          builder: ${{ steps.buildx.outputs.name }}
          push: true
          tags: ${{ env.TAGS }}
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache

以上を組み合わせると、以下のYAMLができあがる。

https://github.com/74th/github-actions-docker-build-with-cache/blob/main/.github/workflows/docker-build.yaml
name: build-docker
on: push

env:
  IMAGE: 74th/githjub-actions-docker-build-with-cache

jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
      - name: checkout
        uses: actions/checkout@v1

      - name: Cache Docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ github.ref }}-${{ github.sha }}
          restore-keys: |
            ${{ github.ref }}-${{ github.sha }}
            ${{ github.ref }}
            refs/head/main

      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1

      - name: create tag
        run: |
          SHA=${{ github.sha }}
          TAG=${IMAGE}:$(TZ=UTC-9 date '+%Y%m')-${SHA:0:7}
          if [ "${{ github.ref }}" == "refs/heads/main" ]; then
            LATEST=${IMAGE}:latest
            echo "TAGS=$TAG,$LATEST" >> $GITHUB_ENV
          else
            echo "TAGS=$TAG" >> $GITHUB_ENV
          fi
          echo TAG $TAG

      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: ./
          file: ./Dockerfile
          builder: ${{ steps.buildx.outputs.name }}
          push: false
          tags: ${{ env.TAGS }}
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache

キャッシュが効いている場合には、以下のようにログにCACHEDと書かれて、そのビルドステップは省略され、高速にビルドすることができる。

...
#12 [1/6] FROM docker.io/library/python:3.9@sha256:168fd55b03929f88cd3e1e05b9ebe8f9cc1c095af8b53a8c0cd50da04a8c3a40
#12 sha256:d23acac7e68ba122c4a36d2f678784fa44485e2b1b3ce45b3f4e162de16f7e33
#12 resolve docker.io/library/python:3.9@sha256:168fd55b03929f88cd3e1e05b9ebe8f9cc1c095af8b53a8c0cd50da04a8c3a40 done
#12 DONE 0.0s

#7 [internal] load build context
#7 sha256:d8baf4b681cfd956bd41e7dcd88e0885036e445ad5d58210350906952e0d5393
#7 transferring context: 8.29kB done
#7 DONE 0.0s

#5 [2/6] WORKDIR /app
#5 sha256:cc5bf3907fccf389eeddea6b0481756956d5d7f28d724aaa3a40ea7ee3832ba5
#5 CACHED

#6 [3/6] RUN pip install poetry
#6 sha256:28fee1bc4b7c051e0d5cabc28ba60d91893046d47339c2202ac83d7454322d16
#6 CACHED

#9 [5/6] RUN poetry install
#9 sha256:5eee4e56fd80670a93603c955cf19a8015609f7c776fd9fdbe4e029b3bb23d60
#9 CACHED

#8 [4/6] COPY pyproject.toml poetry.lock ./
#8 sha256:7ecf0a9ab931cf67405e0a6a1ff5f6fd64bb29608c49e544267d2f416ccc15f6
#8 CACHED
...