Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CircleCIでLayer Cachingを使わずにdocker buildを高速化する

CircleCIでLayer Cachingを使わずにdocker buildを高速化する

【東京】CircleCI ユーザーコミュニティ ミートアップ #9
https://circleci.connpass.com/event/242577/

URLリンクも有効なGoogle Slide版は
https://docs.google.com/presentation/d/1UpRyHsdx4bpBWE9P0VWznVCcupmjVWfmEXRoMbcAeIQ/edit?usp=sharing

9bcd328daf60fe29dc9970f6cfc26730?s=128

Kenta Kase

April 21, 2022
Tweet

More Decks by Kenta Kase

Other Decks in Programming

Transcript

  1. CircleCIでLayer Caching を使わずにdocker buildを 高速化する 加瀬健太 @Kesin11 【東京】CircleCI ユーザーコミュニティ ミートアップ

    #9 2022/04/21
  2. 自己紹介 所属:DeNAのSWETグループ(SoftWare Engineer in Test) Twitter: @Kesin11, GitHub: Kesin11 CI/CDサービスのマニアなので有名なサービスは大体チョットワカル

    主な業務 モバイルゲーム開発のJenkins構築、運用、パイプライン構築 社内CircleCI Serverの運用チーム
  3. docker build 毎日しますよね?

  4. ビルドは早いほうが嬉しい CircleCIではどうしますか?

  5. https://circleci.com/docs/ja/2.0/docker-layer-caching/

  6. 完全に正しいが・・・ 実際どうしてますか? DeNAではCircleCI Server(オンプレ)なので実質無料 docker buildする場合にはバンバン使ってもらっている

  7. Layer Cachingのお値段 ドキュメントによると1ジョブごとに300クレジット Freeプランの無料枠は30,000クレジットなので100ビルド相当 Docker/Linux Medium(2CPU, 4GB)が10クレジット/分 Layer Cachingを有効にするだけで30分相当のクレジット キャッシュによってビルド時間自体を減らせる

    高いと見るか安いと見るか・・・
  8. Layer Cachingに頼らずにビルドを 早くする方法はないだろうか?🤔

  9. 発表内容 docker buildの各種キャッシュ方式の解説 CircleCI上でのビルド実験 実験の結果の考察 おまけCircleCIでdocker buildする際の重要ポイント

  10. まずbuildkitの有効化 DOCKER_BUILDKIT: 1 マルチステージビルドが並列ビルドされるので早くなる もう基本中の基本なので語ること無し とはいえ最新のcimg/base:stableを使っていても明示的に環境変数でONに する必要があるので忘れないように

  11. Inline cache docker build --build-arg BUILDKIT_INLINE_CACHE=1 pushするイメージにキャッシュ用のメタデータを書き込む 次回ビルドでキャッシュとしてpush済みのイメージを参照する マルチステージビルドにおいては普通に使ってもキャッシュが有効になる場面 はほぼ無い

    忘れてOK
  12. 補足:buildx この後の各種キャッシュ機能はbuildkitの機能を使います dockerからbuildkitのフル機能を使うにはdocker buildxが必要 とりあえずdocker buildx buildを使うのが最新のビルド方法という認識で大 丈夫です

  13. RUN –mount=type=cache (正式な名称はよくわからない。便宜上Mount cacheと呼びます) DockerfileのRUNに追加する RUN --mount=type=cache,target=/root/.cache/go-build go build …

    RUN中にディレクトリ指定でマウントしたボリュームを次のビルドで使い回す パッケージやビルドキャッシュを使い回せると次のビルドが早くなる
  14. Registry cache docker buildx build -f Dockerfile \ --cache-to=type=registry,mode=max,ref=$CACHE_IMAGE:cache \

    --cache-from=type=registry,ref=$CACHE_IMAGE:cache \ -t $IMAGE:latest . ビルドするイメージとは別にキャッシュ用レイヤーを別イメージとしてpushする mode=maxにするとマルチステージの中間も含めて全レイヤーがpushされる マルチステージビルドでもキャッシュを有効に使える
  15. Registry cache docker buildx build -f Dockerfile \ --cache-to=type=registry,mode=max,ref=$CACHE_IMAGE:cache \

    --cache-from=type=registry,ref=$CACHE_IMAGE:cache \ -t $IMAGE:latest . キャッシュとしてのイメージはdocker run可能なものではないので混ざらない ようにイメージ名を別にした方が安全 CircleCIはコンテナレジストリの機能を提供していないので GitHub/AWS/GCPあたりのレジストリを自分で用意する
  16. Local cache docker buildx build -f Dockerfile \ --cache-to=type=local,mode=max,dest=/tmp/docker_cache \

    --cache-from=type=local,src=/tmp/docker_cache \ -t $IMAGE:latest . キャッシュ用レイヤーをローカルに書き出す キャッシュとしての効果はRegistryと同じ mode=maxの効果も同じ
  17. Local cache docker buildx build -f Dockerfile \ --cache-to=type=local,mode=max,dest=/tmp/docker_cache \

    --cache-from=type=local,src=/tmp/docker_cache \ -t $IMAGE:latest . CircleCIのキャッシュ機能と併用して次回のビルドで使い回せる 注意:書き出されるディレクトリの中身は上書きではなく太り続ける キャッシュ戦略をミスると古いファイルも残り続けてしまい、無駄なネット ワーク転送が増え続けて本末転倒になってしまう
  18. 番外:dockerでビルドしない方法 何を言っているのか??

  19. 番外:dockerでビルドしない方法 CircleCIのremote dockerはスペックが固定されている(2core, 8GB) docker build時にコンパイルなどCPUをぶん回す場合は頭打ちになる (別解としてハイスペックmachine executorを使う方法もある) 最大で20CPU, 40GBまでスペックを上げられるCircleCIなのにもったいない

  20. 番外:dockerでビルドしない方法 ハイスペックなコンテナ内でビルドしてCOPYだけでイメージを焼く 例:go buildでバイナリを生成、DockerfileはCOPYだけ Dockerのキャッシュの代わりに普段どおりCircleCIのキャッシュを使う Dockerが担保していた再現性のあるビルドは担保されなくなるので注意 とはいえCircleCIはコンテナでビルドしているので元々再現性は高い

  21. 実験 CircleCI-Public/circleci-cliをforkしてdocker build中でバイナリをビルド するようなDockerfileを作成 中身はほぼ go mod, go build これをCircleCI上でビルドさせる

    https://github.com/Kesin11/my-circleci-docker-build-sandbox 詳細が気になる人はDockerfileや.circleci/config.ymlを参照
  22. 実験 キャッシュの有無で差が生じやすいようにsmall(1core, 2GB) 以下を比較 キャッシュを使わない通常のdocker build(DOCKER_BUILDKIT=1) Mount cache Registry cache

    Local cache dockerでビルドしない
  23. 実験 キャッシュが有効なケースと無効なケースを意図的に作り出す 有効なケース CircleCIで再ビルド。何も変更しないのでキャッシュがフルに効く 無効なケース go.modに適当なコメント行を追加し、不正なキャッシュを生成 わざと不正なキャッシュを参照させることで意図的に無効化させる

  24. 結果 それぞれのケースで5回計測 横軸はジョブ全体の時間(秒) remote dockerの立ち上げに0-90秒 程度の振れ幅があったため、最小値、中 央値、最大値を表示 評価は中央値で行う(ソートも) プロジェクト構成によって結果はかなり変 わるのであくまで参考程度

  25. 考察 Mount cache 同一マシンにしかキャッシュが保持され ない仕組み CircleCIは毎回マシンが変わるので キャッシュの意味がないことは予想通り デメリットもなさそうなのでDockerfileに 書いておくとローカルマシンでのビルドが 早くなるメリット

  26. 考察 Registry cache キャッシュが有効な場合はLocalとほぼ 同等の早さ ネットワーク的にLocalの方が有利と予 想していたので意外だった Localはrestore_cache, save_cache のキーとパス設計が複雑になりがちなの

    でRegistryの方が簡単
  27. 考察 Local cache キャッシュ無効の場合に特に遅い ログを見るとキャッシュの書き出しに時間 がかかっていた CircleCIはremote dockerなのでネット ワーク越しのIOであり実際にはローカル ではないので時間がかかる?

    書き出されたサイズは約1.5GBでこれを CircleCIのキャッシュに送るのも時間が かかっていた
  28. 考察 dockerでビルドしない キャッシュが無効なら早い、有効なら RegistryやLocalに負ける レイヤーキャッシュがフルに効く場合は dockerはビルドをほぼスキップしている ため 良く言えばキャッシュの有無に依らずに 安定している

  29. 自分なりの結論 buildxを有効にしてRegistry cache + Mount cacheが無難でシンプル ただしキャッシュ用イメージの分だけコンテナレジストリに費用が発生するのを お忘れなく キャッシュ用イメージのタグ戦略 featureブランチでは--cache-fromをmain、--cache-toは無し

    キャッシュ書き出しの時間を節約 mainブランチでは--cache-fromをmain、--cache-toもmain CIでよく使うキャッシュ戦略と同一
  30. おまけ:CircleCI上のbuildxのセットアップ クライアントとリモートのdockerのバージョンがある程度新しい必要がある クライアント cimg系のコンテナであればよほど古くない限り多分大丈夫 リモート(デフォルトが17.09.0-ceなのはそろそろ更新をお願いしたい・・・) setup_remote_docker: version: 20.10.11 CircleCI Serverの場合

    remote docker用のVMのdockerでバージョンが固定されているため、古い場合は管 理者にAMIのアップデートをしてもらう必要がある
  31. おまけ:CircleCI上のbuildxのセットアップ 実はRegistry cacheを使うには以下のコマンドでbuildxの設定が必要 docker buildx create --use ローカルでは問題ないがCircleCIだと以下のようなエラーになった error: could

    not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>` ワークアラウンドとして以下のようにセットアップすれば動く docker context create circleci docker buildx create --use circleci 参考 https://support.circleci.com/hc/en-us/articles/360058095471-How-To-Use-Docker-Buildx-in-Remote-Docker- http://www.er.crichardson.com/post/microservices/2022/01/18/build-multi-arch-docker-images-circleci.html
  32. おまけ:.dockerignoreが超重要 何気ない `COPY . .` がキャッシュを無効化する docker build時にディレクトリの中身がcontextとしてビルダーに送られる contextも完全に一致しないとレイヤーキャッシュが効かない docker

    buildするのに本来は不要なファイルが含まれていないか? .git/の中身はコミットされるごとに変わる .circleci/config.ymlは当然CircleCIの設定を変更すると変わる .gitは気をつけていたが、.circleciを忘れていて実験を何度かやり直すハメになり ました・・・キャッシュの振る舞いが変だと思ったら.dockerignoreをチェックしよう
  33. 参考 RUN —mount=type=cacheの公式ドキュメント(buildkit) buildkitのREADME(Cacheの項目) docker buildx buildの公式ドキュメント How To Use

    Docker Buildx in Remote Docker?(CircleCI Support Center) BuildKitでイメージをビルドする(buildkitとbuildxの解説) Dockerイメージのビルドで使うキャッシュの種類 - レイヤーキャッシュ、BuildKit の--mount=type=cache