概要
docker buildのキャッシュを試してみた際の記事
記事内では「RUN --mount=type=secret」や「RUN --mount=type=ssh」については取り上げていません
検証環境
docker for macを使用しています。
$ uname -a
Darwin mbp01 19.0.0 Darwin Kernel Version 19.0.0: Thu Oct 17 16:17:15 PDT 2019; root:xnu-6153.41.3~29/RELEASE_X86_64 x86_64
$ docker version
Client: Docker Engine - Community
Version: 19.03.4
API version: 1.40
Go version: go1.12.10
Git commit: 9013bf5
Built: Thu Oct 17 23:44:48 2019
OS/Arch: darwin/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.4
API version: 1.40 (minimum version 1.12)
Go version: go1.12.10
Git commit: 9013bf5
Built: Thu Oct 17 23:50:38 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.2.10
GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339
runc:
Version: 1.0.0-rc8+dev
GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
docker-init:
Version: 0.18.0
GitCommit: fec3683
検証
(パッケージマネージャのキャッシュを効かせる)
例えばこんなDockerfileがあるとします。
(dnfを使って適当にパッケージをインストールするだけ)
FROM centos:8
RUN echo "test1"
RUN dnf install -y \
gcc gcr lvm2 clang
これをbuildkitを有効にした状態でビルドしてみます
すると手元の環境で約50秒程度でビルドが完了しました。
docker image build -t test01 .
[+] Building 49.4s (7/7) FINISHED
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 114B 0.0s
=> [internal] load metadata for docker.io/library/centos:8 2.1s
=> [1/3] FROM docker.io/library/centos:8@sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9 0.0s
=> CACHED [2/3] RUN echo "test1" 0.0s
=> [3/3] RUN dnf install -y gcc gcr lvm2 clang 44.1s
=> exporting to image 3.2s
=> => exporting layers 3.2s
=> => writing image sha256:d122ae7f88c40310b925ca575df754f3732a18fb26a488ba11c8de7bf174d1e4 0.0s
=> => naming to docker.io/library/test01 0.0s
>>> elapsed time 50s
その後に下記のようにaptの前に新たにレイヤーが作られるようにdockerfileを修正します。
FROM centos:8
RUN echo "test2" # 修正
RUN dnf install -y \
gcc gcr lvm2 clang
そして同じコマンドでビルドすると同じくらいの時間がかかりました。
(イメージレイヤについては下記をご参照ください)
$ docker image build -t test02 .
[+] Building 52.6s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 114B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/centos:8 3.0s
=> CACHED [1/3] FROM docker.io/library/centos:8@sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9 0.0s
=> [2/3] RUN echo "test2" 0.7s
=> [3/3] RUN dnf install -y gcc gcr lvm2 clang 45.7s
=> exporting to image 3.0s
=> => exporting layers 3.0s
=> => writing image sha256:85b29e834097a17f7311c775bed1b1a0604ddacc6af81a19267edff0ab4ae7cf 0.0s
=> => naming to docker.io/library/test02 0.0s
>>> elapsed time 52s
一番時間がかかるのはおわかりの通りgccのインストールです。
gccのインストールにはたくさんの依存パッケージが存在するのでとても時間がかかります。
dnf(yum)はとても賢いです。いい感じに独自のキャッシュを持っていてそこを有効活用することで高速にビルドが可能です。
そんな時に役立つのが「--mount=type=cache,target」です。
オプション
Option | Description |
---|---|
id | ユニークなIDを指定 |
target (必須) | マウントするパスを指定 |
ro,readonly | 読み取り専用オプション |
sharing | 共有する場合の方式(詳細は下記ページをご参照ください🙇♂️) |
from | キャッシュのベースとして使用するディレクトリ |
これを使うことでこれまでDockerfile内の2行目で行っていたechoのキャッシュを破棄しつつ、
パッケージマネージャのキャッシュを活かしたビルドが可能になります。
Dockerfile例です。
まだ非標準命令のため1行目に「# syntax = docker/dockerfile:experimental」という記述が必要とのことです。
「--mount=type=cache,target」のターゲットにキャッシュしたいディレクトリ(aptなどの場合は適宜読み替えてください)
# syntax = docker/dockerfile:experimental
FROM centos:8
RUN echo "test3"
RUN \
--mount=type=cache,target=/var/cache/dnf \
--mount=type=cache,target=/var/lib/dnf \
dnf install -y \
gcc gcr lvm2 clang
1回目のビルド
1回目はキャッシュの構築です。上記と同じく50秒程度かかることがわかりました。
$ docker image build -t test03 .
[+] Building 52.3s (9/9) FINISHED
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 250B 0.0s
=> resolve image config for docker.io/docker/dockerfile:experimental 2.1s
=> CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:888f21826273409b5ef5ff9ceb90c64a8f8ec7760da30d1ffbe6c3 0.0s
=> [internal] load metadata for docker.io/library/centos:8 0.9s
=> CACHED [stage-0 1/3] FROM docker.io/library/centos:8@sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9 0.0s
=> [stage-0 2/3] RUN echo "test3" 0.7s
=> [stage-0 3/3] RUN --mount=type=cache,target=/var/cache/dnf --mount=type=cache,target=/var/lib/dnf dnf install -y gcc 44.8s
=> exporting to image 3.1s
=> => exporting layers 3.1s
=> => writing image sha256:a4f1634b51036b0ae3107ac44884e980701a698c44315786c9eb42450f65ce51 0.0s
=> => naming to docker.io/library/test03
2回目のビルド
キャッシュを使わない検証同様にechoの部分を書き換えます。
# syntax = docker/dockerfile:experimental
FROM centos:8
RUN echo "test4" #修正
RUN \
--mount=type=cache,target=/var/cache/dnf \
--mount=type=cache,target=/var/lib/dnf \
dnf install -y \
gcc gcr lvm2 clang
上記を使用してビルド。
約20秒程度の短縮という結果となりました。
docker image build -t test04 .
[+] Building 35.7s (9/9) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 250B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> resolve image config for docker.io/docker/dockerfile:experimental 0.9s
=> CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:888f21826273409b5ef5ff9ceb90c64a8f8ec7760da30d1ffbe6c3 0.0s
=> [internal] load metadata for docker.io/library/centos:8 0.9s
=> CACHED [stage-0 1/3] FROM docker.io/library/centos:8@sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9 0.0s
=> [stage-0 2/3] RUN echo "test4" 0.7s
=> [stage-0 3/3] RUN --mount=type=cache,target=/var/cache/dnf --mount=type=cache,target=/var/lib/dnf dnf install -y gcc 29.5s
=> exporting to image 2.9s
=> => exporting layers 2.9s
=> => writing image sha256:602d0e7bcc5b5701276bc0d25c853557d45b6925cb049cd4848b739647992ba0 0.0s
=> => naming to docker.io/library/test04 0.0s
>>> elapsed time 35s
注目すべきはは「[stage-0 3/3] RUN --mount=type=cache,target=/var/cache/dnf」の部分です。
1回目は44秒程度かかったのに対して30秒程度で完了したことがわかります。
まとめ
今回はdnfでやってみましたが、pipとかパッケージ管理をテキストでやるようなパッケージマネージャなどでも有効かなと思いました。
(requirements.txtを頻繁に書き換える場合もある程度はキャッシュが効く?)
パッケージのアップデートやらキャッシュが効く部分(マルチステージビルドの記事が結構多い)での改善は楽しいです。
その辺はこれから試しながら遊んでいきたいと思います。