はじめに
GitHub Actions 上での Docker イメージのビルド高速化について情報が少なかったので自分なりに調査してみました。 Jenkins のように自分でホストするのではなく、GitHub Actions や CircleCI ではステートレスにジョブが行われます。 そのため、ローカルにキャッシュが残らないため、外部にキャッシュを持つようにします。
試す選択肢
1.普通のdocker build
🐭
キャッシュが効かない普通のdocker build .
です。
GitHub Actionsはステートレスなのでこのままではレイヤーキャッシュは効きません。
2.普通のdocker build
+ BuildKit🐶
🐭と同様ですが、BuildKitを効かせます。
環境変数にDOCKER_BUILDKIT=1
をセットしdocker build .
するだけです。
3.actions/cache + docker save & load
🦊
GitHub Actionsでは、actions/cacheを使うことでキャッシュできます。
また、docker save
とdocker load
により、イメージをtar形式のファイルにセーブしたりロードできます。
4.外部レジストリのキャッシュdocker build --cache-from
🐷
これは外部キャッシュです。
今回はDocker Hubですが、AWSのECRへも同様にキャッシュできます。
これによりレイヤーキャッシュが効きます。
--build-arg BUILDKIT_INLINE_CACHE=1
のオプションも必須です。
試してみた
以下のDockerfileで今回は試してみました。 マルチステージビルドではありません。 もっと、重くビルド時間がかかるようなものでやると違う結果が得られると思います。
# Dockerfile FROM ruby:2.6.3 ENV APP_ROOT /app WORKDIR $APP_ROOT # mysqlのクライアントをインストール RUN apt-get update && apt-get install -y \ default-mysql-client \ --no-install-recommends && \ rm -rf /var/lib/apt/lists/* ADD Gemfile $APP_ROOT ADD Gemfile.lock $APP_ROOT RUN gem install bundler:2.0.2 && \ bundle install && \ rm -rf ~/.gem ADD . $APP_ROOT EXPOSE 4567 CMD ["bundle", "exec", "ruby", "app.rb", "-o", "0.0.0.0"]
1.普通のdocker build
🐭
build_no_cache: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@master - name: Build run: docker build .
53 秒程度でした。 https://github.com/kotaroooo0/githubactionsbuild/runs/846073213?check_suite_focus=true
2.普通のdocker build
+ BuildKit🐶
build_no_cache_buildkit: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@master - name: Build env: DOCKER_BUILDKIT: 1 run: docker build .
45 秒程度でした。 https://github.com/kotaroooo0/githubactionsbuild/runs/846073213?check_suite_focus=true
3.actions/cache + docker save & load
🦊
build_with_docker_save_load: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@master - id: cache-docker uses: actions/cache@v1 with: path: /tmp/docker-save key: docker-save-${{ hashFiles('Dockerfile') }} - run: docker load -i /tmp/docker-save/snapshot.tar || true if: steps.cache-docker.outputs.cache-hit == 'true' - run: docker build . -t thing --cache-from=thing-cache - run: docker tag thing thing-cache && mkdir -p /tmp/docker-save && docker save thing-cache -o /tmp/docker-save/snapshot.tar && ls -lh /tmp/docker-save || true if: always() && steps.cache-docker.outputs.cache-hit != 'true'
キャッシュがヒットしない 1 回目は 53 秒でした。
しかし、別途docker save
に 1 分程度かかりました。
キャッシュがヒットする 2 回目は 19 秒でした。
しかし、別途docker load
や cache の復元に 1 分程度かかりました。
https://github.com/kotaroooo0/githubactionsbuild/runs/846270916?check_suite_focus=true
4.外部レジストリのキャッシュdocker build --cache-from
🐷
cache-from-with-build-arg: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@master - name: Build and Push env: DOCKER_BUILDKIT: 1 DOCKERHUB_USER: ${{ secrets.DOCKER_USERNAME }} DOCKERHUB_PASS: ${{ secrets.DOCKER_PASSWORD }} run: | docker login -u $DOCKERHUB_USER -p $DOCKERHUB_PASS docker build -t adachikun/githubactionsbuild:latest --cache-from=adachikun/githubactionsbuild:latest --build-arg BUILDKIT_INLINE_CACHE=1 . docker push adachikun/githubactionsbuild:latest
キャッシュがヒットしない 1 回目は 50 秒程度でした。
別途docker push
に 10 秒程度かかりました。
https://github.com/kotaroooo0/githubactionsbuild/runs/854242620?check_suite_focus=true
キャッシュがヒットする 2 回目は 30 秒程度でした。 https://github.com/kotaroooo0/githubactionsbuild/runs/854249995?check_suite_focus=true
プラグイン編
GitHubでDocker公式のビルド用プラグインを見つけたので使ってみました。
Buildkit が使えたり、build_args
やcache_froms
のオプションが設定できるため同じことができるのではと思い試してみました。
push
オプションもあり、デフォルトではtrue
になっています。
build-push-action-cache: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@master - name: Build and Push uses: docker/build-push-action@v1 env: DOCKER_BUILDKIT: 1 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: adachikun/githubactionsbuild tags: latest cache_froms: adachikun/githubactionsbuild:latest build_args: BUILDKIT_INLINE_CACHE=1
自分で手書きで書いて外部キャッシュを利用する🐷と同様の結果でした。 1回目は50秒程度でした。https://github.com/kotaroooo0/githubactionsbuild/runs/854302398?check_suite_focus=true 2回目は30秒程度でした。https://github.com/kotaroooo0/githubactionsbuild/runs/854310734?check_suite_focus=true
まとめ
actions/cache + docker save & load
🦊、または外部レジストリのキャッシュdocker build --cache-from
🐷が高速でした。
🦊はビルド単体の速度だけで見れば最速でしたが、docker save
とdocker load
によるオーバーヘッドが大きかったです。
🐷の方が無難に高速そうですが、これは場合によって使い分けるのが適切だと思います。
もしかしたら、普通にBuildKitでビルドするのが高速な場合もあるかもしれませんし、計測するのが大事だと思います。
マルチステージビルド編は次回やろうと思います。
過去に書いたDockerビルド関係の記事
Go製CLIツールを使うDockerイメージをダイエットしてみた - 🤖
DockerイメージのビルドをBuildKitで並列実行し高速化する - 🤖
Dockerイメージのビルドで使うキャッシュの種類(レイヤーキャッシュ, BuildKit, CI) - 🤖
Dockerfileを正しく書けるように指摘してくれる静的解析ツール「hadolint」 - 🤖