github actions での docker のビルド時のキャッシュ利用について

HIKKY のバックエンドエンジニアのあったんです。
今回は github actions の docker-compose のビルド時にキャッシュを利用しようとして苦戦した結果うまくいかなかったので、メモを残します。

CircleCI では

CircleCI を利用する際は、DLC(docker layer caching)を利用することで特にキャッシュの仕組みを意識しなくても簡単にキャッシュを導入することが可能です。
/var/lib/docker をキャッシュするというシンプルな構造になっているためです。
また、上記のような仕組みのため docker build、docker-compose build を問わず簡単にキャッシュの恩恵を受けることが可能です。

github actions では

github actions では CircleCI のような機能は提供されておらず(2022年9月時点)、キャッシュ対象のイメージや、キャッシュされる場所を理解して利用しなければなりません。
以下では、docker build の場合と、docker-compose build の場合に分けて、検証した内容を記載します。

docker build でビルドするコンテナイメージのキャッシュ

docker build でビルドしたイメージのキャッシュについては、docker 公式から便利な action が提供されており、それを利用することで可能です。
どこのサーバーに保存するべきかということについては、利用者が理解して利用しなければなりません。

docker/build-push-action

https://github.com/docker/build-push-action
docker 公式で用意されている action です。
ビルドしたイメージを push するための action ですが、buildkit の機能によるキャッシュを簡単に設定できます。

設定例

name: ci

on:
  push:
    branches:
      - 'main'

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v3
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      -
        name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          # キャッシュの恩恵のみを受ける場合は、push の必要なし
          push: false
          tags: user/app:latest
          # github cache を利用してキャッシュの設定をします
          cache-from: type=gha
          cache-to: type=gha,mode=max

ポイントは cache-from cache-to の部分で、github cache を利用してビルドしたレイヤーのキャッシュを行います。

docker-compose build でビルドするコンテナイメージのキャッシュ

docker-compose build でも buildkit によるキャッシュの恩恵は受けられます。
buildkit を有効化する方法はいくつかあり、バージョンによって方法が変わるようです。
今回は以下の方法によって buildkit を有効化して調査を行いました。

  • COMPOSE_DOCKER_CLI_BUILD=1 docker-compose build

  • docker buildx bake -f docker-compose.yml

なぜ docker-compose build でキャッシュを利用したいか

CI で docker-compose を利用することのメリットは、docker-compose run によるコマンドを利用することで、ローカルの開発環境と同じコマンドを利用できることにあります。
docker-compose run を行う際は、ビルドが必要なコンテナがある場合にビルドが走ってしまいます。
依存ライブラリのインストールなど時間がかかるものがある場合には、ビルドする際にキャッシュを利用したいです。

docker-compose run では buildkit でビルドしたイメージを利用できない

docker-compose build を buildkit を利用してビルドした後に、docker-compose run を実行すると、ビルドしたイメージがないと判定されて再度ビルドが走ってしまいました。
これは2022年9月の情報であり、最新の docker では buildkit 周りの環境変数を利用することで解決できるかもしれません。

docker load / image の pull を利用する

docker には docker load というコマンドによるビルド済みイメージの登録機能があります。
ビルドしたイメージを tar にしてキャッシュし、それを docker load するという方法も試しましたが、docker load は時間がかかる処理であり、CircleCI の DLC ほどの恩恵は受けられませんでした。

また、ビルドしたイメージを docker hub や ECR などに登録することで、ビルドすることを避ける方法もあります。
管理コストがかかるため、今回は採用していません。

docker-compose build でキャッシュを利用できない

このため docker-compose run を利用するためのビルドには github actions では現状キャッシュを簡単に活用できないという結論に至りました。

まとめ

CircleCI と活用シーンがほぼ同じの github actions ですが、 CircleCI は先発のサービスとして痒いとこに手が届いています。

CircleCI のほうが便利だなと感じる大きな点として、今回記載した docker のキャッシュと、承認後の実行があります。
CircleCI ではデフォルトの機能として job の step に 承認のワークフロー を入れることが出来、特定の処理のみを承認後に実行できます。
github actions でのデフォルトの承認機能は action の実行に承認を必要とさせる機能 となっており、action を分ける必要があります。

こういった点から CI/CD の記述では、まだ CircleCI のほうが便利そうと感じます。