DockerイメージビルドのキャッシュをBuildKitを交えつつActionsでやってみた

Actions上でのDockerイメージビルド時間がやや超過気味と感じ、利用枠上限を考慮して時短化できないかと中間データのキャッシュを試みました。
2020.09.02

はじめに

Dockerイメージのビルドは構成次第で長い時間が掛かり、GitHub Actions上で行う場合に頻度が高くなると利用可能枠の圧迫につながります。Organization全体の限度枠をどのくらい占めているのか、及び残り利用枠が分かりにくいこともあり、先んじてキャッシュして備えることにしました。

効率のよいキャッシュを検討する

キャッシュを目的としたActionはいくつかありましたが、今回は使いません。BuildKitプロジェクト成果物のソースコード内での利用形跡が見つからなかったためです。

BuildKitについて

以下のスライドが参考になります。Dockerfile内で効率よくキャッシュするコツにも触れているので、読まれたことがない方には特におすすめします。

BuildKitを併用してキャッシュした場合の時間については、以下のブログ記事にまとまっています。

やってみる

同様の試みをまとめた記事があったため、それをベースにしてみます。

BuildKitを有効にした状態でビルドし、結果をECRのリポジトリへプッシュします。事前にECRへのログインが必要な点については注意してください。手軽に実行して確認するために workflow_dispatch を利用します。

on: [workflow_dispatch]

name: Build Image

env:
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_DEV }} 
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_DEV }} 
  AWS_DEFAULT_REGION: ap-northeast-1

jobs:
  name: BuildAndPush
  runs-on: ubuntu-latest

steps:
  - name: Checkout
    uses: actions/checkout@v2

  - name: Configure AWS credentials
    uses: aws-actions/configure-aws-credentials@v1
    with:
      aws-region: ap-northeast-1

  - name: Login to Amazon ECR
    id: login-ecr
    uses: aws-actions/amazon-ecr-login@v1

  - name: Build, tag, and push image to Amazon ECR
    id: build-image
    env:
      DOCKER_BUILDKIT: 1
      ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
      ECR_REPOSITORY: repository-name # TODO:実際のリポジトリ名に合わせる
      IMAGE_TAG: ${{ github.sha }}
    run: |
      docker build \
          --cache-from=$ECR_REGISTRY/$ECR_REPOSITORY:latest --build-arg BUILDKIT_INLINE_CACHE=1 \
          -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
      docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
      docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
      echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"

元のテンプレートから変更した箇所は

  • ECR_REGISTRYの指定をaws-actions/amazon-ecr-loginのoutput参照に切り替えた
  • Credential 用の環境変数を共通のものに変更した
  • on:pushでは動作しなくなった

の3点です。必要に応じて変更してください。

キャッシュを効かせていないケースとの違いは、

  • 環境変数にDOCKER_BUILDKIT: 1を指定
  • docker buuldに--cache-from=$ECR_REGISTRY/$ECR_REPOSITORY:latest --build-arg BUILDKIT_INLINE_CACHE=1を指定

の2点。手軽ですね。

キャッシュ前後の結果

事前にlatestタグのついたイメージがなかったため、初回は通常ビルド、直後に再実行してキャッシュが効いたビルドにしました。

cache前 cache後
12m 8s 5m 15s

Dockerfileにもよりますが、今回ははっきりと違いのわかる結果となりました。

Cache前のログ一部

#4 importing cache manifest from ***.dkr.ecr.ap-northeast-1.amazon...
#4 ERROR: ***.dkr.ecr.ap-northeast-1.amazonaws.com/aws-cdk/assets:latest not found

#11 [internal] load build context
#11 transferring context: 238.06kB 0.0s done
#11 DONE 0.0s

#5 [1/14] FROM docker.io/library/amazonlinux:2@sha256:2c99363fc74d3a39f0236...
..
#5 DONE 3.7s

#6 [2/14] RUN yum -y install gcc python3-devel postgresql-devel libcurl-dev...
..
#6 DONE 39.1s

#7 [3/14] RUN pip3 install pipenv
..
#7 DONE 9.2s

#8 [4/14] RUN pip3 install --upgrade setuptools
#8 1.582 Collecting setuptools
#8 1.652   Downloading setuptools-50.0.3-py3-none-any.whl (784 kB)
#8 1.865 Installing collected packages: setuptools
#8 1.865   Attempting uninstall: setuptools
#8 1.865     Found existing installation: setuptools 38.4.0
#8 1.888     Uninstalling setuptools-38.4.0:
#8 2.055       Successfully uninstalled setuptools-38.4.0
#8 2.417 Successfully installed setuptools-50.0.3
#8 DONE 2.5s

Cache後のログ一部

#4 importing cache manifest from ***.dkr.ecr.ap-northeast-1.amazon...
#4 DONE 1.9s

#10 [internal] load build context
#10 transferring context: 238.06kB done
#10 DONE 0.0s

#7 [4/14] RUN pip3 install --upgrade setuptools
#7 CACHED

..

あとがき

Dockerfileの中間データキャッシュは、繰り返してビルドを行う回数が多いほど時間短縮の点で効果がでてきます。ビルド時間長期化で悩まれている場合は手をつけてみることをおすすめします。

参考リンク