GitHub Actionsでdocker build
することが多い。このときのキャッシュをどうするかという話題。
基本
GitHub Actionsでdocker build
してAmazon ECRにdocker push
する、典型的な.github/workflow/docker-push-to-ecr.yml
はこういう感じ。
name: Push to Amazon ECR on: push: branches: [ 'main' ] jobs: docker: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: docker/setup-buildx-action@v1 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - uses: docker/metadata-action@v3 id: meta with: images: | ${{ steps.login-ecr.outputs.registry }}/awesome-app tags: | type=sha,prefix= - uses: docker/build-push-action@v2 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max
Docker周りはDocker公式のアクションを組み合わせて使って、BuildKitでやる。
キャッシュはdocker/build-push-action@v2
のcache-from
とcache-to
でやっていて、type=gha
としてGitHub Actionsのキャッシュにのせている。
type=gha
は、BuildKitで使えるキャッシュの中でも手軽で使いやすい。GitHub Actionsのキャッシュはブランチと紐づいていて、現在のブランチとベースブランチのキャッシュが利用できるので、CIと組み合わせたときにわりと都合がいい。
Build Matrixとの組み合わせ
背景
去年、業務で利用しているMacがM1 Proのそれになって、我らがCTO id:motemenが用意しているmod_perl用Dockerイメージ(motemen/docker-mod_perl)にパッチを送り、arm64
アーキテクチャのイメージをビルドしてもらうようにした。
変更は簡単で、docker/setup-qemu-actionでQEMUのセットアップをして、docker/build-push-actionにplatforms
を指定した。ベースイメージの時点でマルチアーキテクチャに対応しているし、Dockerfileでアーキテクチャに依存したことをしていないので、これで十分。
マルチアーキテクチャになったのはよかったけど、GitHub Actionsがすごく遅くなってしまった。もともとキャッシュが切れていても5分30秒くらいで済んでいたのが、1時間以上かかるようになった。とはいえ、QEMUでCPUアーキテクチャをまたいでビルドすると遅いのは、それはそう。それはそうなんだけど、キャッシュの効きが悪いのも気になっていた。
こういうのをみると、Matrixでビルドしているうち「5.30.1」のみ9秒で終わっているが、残りは1時間近くかかっている。
さっきのブランチのベースになっているのはこちらで、「5.30.1」が一番最後に終わっていそうだった。
このことから、Matrixビルドでキャッシュが競合していて、後勝ちになっていることが類推された。
scopeを設定する
ということでBuildKitのghaキャッシュのドキュメントをもう一回見ると、scope
というのがある。これを使って、docker/build-push-action@v2
のcache-from
とcache-to
にscope
を設定してみる。
cache-from: type=gha,scope=perl-${{ matrix.perl_version }} cache-to: type=gha,mode=max,scope=perl-${{ matrix.perl_version }}
これでMatrixごとにキャッシュが分離された。
各バージョンが20秒前後になり、全体で1分弱になった。よかった。
まとめ
GitHub ActionsでBuildKitを使うときにtype=gha
のキャッシュが使える。ただしMatrixビルドなどで並列に複数のジョブが走ると、キャッシュが衝突してしまうが、scope
を設定することで回避できる。