Github Actionsのdocker/build-push-actionのcache-toにECRを指定する

TL;DR

- uses: docker/build-push-action@v3
  with:
    context: .
    tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.MY_REPOSITORY }}:${{ env.MY_TAG }},${{ steps.login-ecr.outputs.registry }}/${{ env.MY_REPOSITORY }}:${{ env.MY_CACHE_TAG }}
    push: true
    cache-from: type=registry,ref=${{ steps.login-ecr.outputs.registry }}/${{ env.MY_REPOSITORY }}:${{ env.MY_CACHE_TAG }}
    cache-to: type=inline,ref=${{ steps.login-ecr.outputs.registry }}/${{ env.MY_REPOSITORY }}:${{ env.MY_CACHE_TAG }}
    outputs: type=registry

説明

まずdocker/build-push-actionではcache-toにtype=ghaを使うのが簡単で一般的だと思う。 しかしこれは内部でGithub Actions Cache APIを使っているのでキャッシュ先はactions/cacheと同様で、actions/cacheのキャッシュ制限事項が適用されると思われる。 そうだとすると、7日間アクセスがなければキャッシュが破棄され、またリポジトリごとの10G制限も発生する。actions/cacheのキャッシュサイズが大きい場合キャッシュが効きづらくなりそうだ。

そこで、cache-toにtype=registryを使いたい。 これならキャッシュ保存先がimageレジストリになるのでキャッシュ制限を考えなくてよくなる。 type=registry はimageとcache manifestを別々にレジストリにpushするが、ECRはcache manifestをサポートしていない(2022/06/11現在)
ただ2022/5/7にサポートを検討するコメントがついているのでしばらくしたらtype=registryが使えるようになりそう。

で、type=registryが使えないのでtype=inlineを使うことになる。 type=inlineはimage内にキャッシュ用メタデータを埋め込む方法。 type=inlineのデメリットとしてはmode=maxが使えないので、マルチステージビルドを使っている場合、中間レイヤーイメージがキャッシュされない。
もし中間レイヤーもキャッシュしたいのであれば「キャッシュのためにDockerビルドで中間イメージをタグ付けしレジストリにPushする - 🤖」を参考にするとよさそう。

また、docker/build-push-actionを使う際はtagsにメインのimageだけじゃなくcache用のimageも指定しないと、cache用のimageがpushされないので注意。