🔥

Docker Buildkit InlineCacheを試す

2023/09/28に公開

記事の内容

AWS LambdaをDockerコンテナでデプロイしようとするとローカルPCではビルドのキャッシュが効くのですが、
CI/CD環境ではキャッシュが効かず1からビルドしてしまうため、ビルド時間が毎回掛かってしまう状況に悩んでいました。
これはCI/CD環境は基本的に一度使用したら使い捨てでありキャッシュが残らないためです。キャッシュを活用してビルドを迅速に行うにはキャッシュ戦略を立てる必要があります。
DockerのBuildKitにはイメージレイヤーをイメージにキャッシュする(Inline cache)ことができます。これを使ってキャッシュ戦略を立てるために試して見たいと思います。

https://docs.docker.com/build/cache/backends/inline/

関係する技術・ツール

  • Docker Desktop 4.23.0
  • AWS ECR

作業の流れ

  1. Dockerfile作成
  2. イメージのビルド&プッシュ
  3. 再ビルド(Dockerfile変更なし)
  4. 再ビルド(Dockerfile変更あり)

1. Dockerfile作成

まずは下記でDockerfileを作成します。

Dockerfile
FROM node:18.17.0-slim as builder

RUN apt update && \
    apt install -y \
      curl \
      git \
      vim

RUN mkdir ~/build
RUN echo "build complete >> ~/build/message.txt"

FROM node:18.17.0-slim

COPY --from=builder /root/ ./

2. ビルド&プッシュ

次にビルド&プッシュします。
今回はAWS ECRのプライベートリポジトリへプッシュします。
下記でレポジトリにログインしておきます。リポジトリの場所は東京リージョンです。

zsh
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com

ログインできたらビルド&プッシュを実行します。

zsh
docker build --cache-from type=registry,ref=AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1 --cache-to type=inline --push -t AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1 .

ポイントとしては
--cache-from type=registry,ref=リポジトリ名/イメージ名:タグでキャッシュのエントリを指定します。
--cache-to type=inlineでキャッシュの方法を指定します。今回はインラインキャッシュを指定します。

初回のビルド時は下記のように赤字で出力されると思います。
これは初回時はイメージにキャッシュを含んでいないため、キャッシュヒットしないためです。
ビルド自体は問題なく完了してイメージをプッシュします。

zsh
 => ERROR importing cache manifest from AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1

3.再ビルド(Dockerfile変更なし)

これで初回のイメージ構築は完了してキャッシュも作成されました。
それではDockerfileは変更せず、再度2と同じコマンドでビルド&プッシュします。期待通りであれば、2回目以降はイメージに含んだキャッシュが効いてビルドされるはずです。
ちゃんとインラインキャッシュが効くことを確認するために、事前にローカルPCのビルドキャッシュは削除しておきます。

zsh
docker builder prune

これでローカルPCにはキャッシュは残っていないため端末のキャッシュを使うこともありません。
それではビルド&プッシュを実行しましょう。

zsh
docker build --cache-from type=registry,ref=AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1 --cache-to type=inline --push -t AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1 .

すると今度は下記のように出力されると思います。
先ほどは赤字でエラーとなりキャッシュはヒットしませんでしたが、今度はキャッシュヒットしたためです。
RUNやCOPYもCACHEDとなっていることが確認できます。ビルドも迅速に終わると思います。

zsh
 => importing cache manifest from AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1
 => [builder 1/4] FROM docker.io/library/node:18.17.0-slim@sha256:a0cca98f2896135d4c0386922211c1f90f98f27a58b8f2c07850d0fbe1c2104e                            0.0s
 => CACHED [builder 2/4] RUN apt update &&     apt install -y       curl       git       vim                                                                  0.0s
 => CACHED [builder 3/4] RUN mkdir ~/build                                                                                                                    0.0s
 => CACHED [builder 4/4] RUN echo "build complete >> ~/build/message.txt"                                                                                     0.0s
 => CACHED [stage-1 2/2] COPY --from=builder /root/ ./   

4.再ビルド(Dockerfile変更あり)

3でキャッシュが効いていることが確認できました。
今度はDockerfileを変更して、キャッシュが効くことも確認しておきましょう。
下記のようにDockerfileを変更します。

Dockerfile
FROM node:18.17.0-slim as builder

RUN apt update && \
    apt install -y \
      curl \
      git \
      vim

RUN npm i -g typescript 

RUN mkdir ~/build
RUN echo "build complete >> ~/build/message.txt"

FROM node:18.17.0-slim

COPY --from=builder /root/ ./

2回目のRUNに下記を追加しています。

Dockerfile
+ RUN npm i -g typescript 

再度2と同じコマンドでビルド&プッシュします。
期待通りであれば、イメージレイヤーに差分ができるため変更行より前のコマンドはキャッシュされますが、変更行以降はキャッシュが効かなくなるはずです。

zsh
docker build --cache-from type=registry,ref=AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1 --cache-to type=inline --push -t AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1 .

キャッシュヒットしていますね。
詳細まで追うと、1行目のRUNはキャッシュが効いています。
しかし、今回追加した2行目のRUN以降ではキャッシュが効いていないようです。
ビルドは変更行以降はキャッシュは使わず再構築することが基本の挙動です。
ですので期待通りのキャッシュ動作になっています。これで差分でキャッシュが効いていることも確認できるかと思います。

zsh
 => importing cache manifest from AWSアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/my-test:image1                                                            0.0s
 => CACHED [stage-1 1/2] FROM docker.io/library/node:18.17.0-slim@sha256:a0cca98f2896135d4c0386922211c1f90f98f27a58b8f2c07850d0fbe1c2104e                     0.0s
 => CACHED [builder 2/5] RUN apt update &&     apt install -y       curl       git       vim                                                                  0.0s
 => [builder 3/5] RUN npm i -g typescript                                                                                                                     2.8s
 => [builder 4/5] RUN mkdir ~/build                                                                                                                           0.3s
 => [builder 5/5] RUN echo "build complete >> ~/build/message.txt"                                                                                            0.3s
 => [stage-1 2/2] COPY --from=builder /root/ ./             

まとめ

  • インラインキャッシュでイメージにキャッシュを含めることができる
  • docker buildの--cache-from--cache-toでキャッシュを指定する
  • イメージ編集時も差分でキャッシュを利用できる

以上になります。
迅速なデプロイにはキャッシュ戦略は重要だと思います。ぜひ利用していきましょう。

NCDCエンジニアブログ

Discussion