LoginSignup
22
11

More than 3 years have passed since last update.

Buildkitを使ってMulti-stage BuildでMavenのキャッシュを効かせる

Last updated at Posted at 2018-11-22

背景

Dockerを利用する際、Multi-stage Buildが使われることが一般的になってきました。
しかし、Multi-stage Buildには、Mavenなどのパッケージマネージャのキャッシュを利用できないという欠点がありました。

そんな中、Dockerに搭載されたBuildkitで --mount=type=cache を使えば、Multi-stage Buildでもパッケージマネージャのキャッシュが利用できるようになったとのことです。

そこで、MavenプロジェクトのSpring BootをMulti-stage Build + Buildkitでdocker buildしてみました。

環境

  • Docker for Mac
    • Docker Engine 18.09.0

手順

Spring Bootプロジェクト作成

Spring Initializr でMavenプロジェクトのSpring Bootを初期化しました。

Dockerfileを記述

Multi-stage Buildを使ったDockerfileを作成しました。

# syntax = docker/dockerfile:1.0-experimental

FROM openjdk:8u181-jdk-alpine3.8 AS builder

ADD . /work
WORKDIR /work

RUN --mount=type=cache,target=/root/.m2 \
    ./mvnw clean install


FROM openjdk:8u181-jre-alpine3.8 AS runner

ENV JAR=demo-0.0.1-SNAPSHOT.jar

RUN addgroup -S -g 1000 app \
    && adduser -D -S -G app -u 1000 app \
    && apk add --no-cache su-exec
WORKDIR /home/app

COPY --from=builder --chown=app:app /work/target/$JAR /home/app/

CMD ["sh", "-c", "su-exec app java -jar $JAR"]

# syntax = docker/dockerfile:1.0-experimental の部分で --mount=type=cache という文法を有効化しています。

RUN --mount=type=cache,target=/root/.m2 で.m2のキャッシュが効くようにしています。

Buildkit有効化

$ export DOCKER_BUILDKIT=1

ビルド1回目 1

$ docker build --no-cache -t demo .
[+] Building 151.6s (15/15) FINISHED
 => [internal] load build definition from Dockerfile                               0.0s
 => => transferring dockerfile: 557B                                               0.0s
 => [internal] load .dockerignore                                                  0.0s
 => => transferring context: 2B                                                    0.0s
 => resolve image config for docker.io/docker/dockerfile:1.0-experimental          2.0s
 => CACHED docker-image://docker.io/docker/dockerfile:1.0-experimental@sha256:d2d  0.0s
 => [internal] load metadata for docker.io/library/openjdk:8u181-jdk-alpine3.8     0.6s
 => [internal] load metadata for docker.io/library/openjdk:8u181-jre-alpine3.8     0.8s
 => CACHED [runner 1/3] FROM docker.io/library/openjdk:8u181-jre-alpine3.8@sha256  0.0s
 => [builder 1/3] FROM docker.io/library/openjdk:8u181-jdk-alpine3.8@sha256:b18e4  9.7s
 => => resolve docker.io/library/openjdk:8u181-jdk-alpine3.8@sha256:b18e45570b6f5  0.0s
 => => sha256:ef87ded15917facc562d84e802d02ded848ae802cc0419440 70.61MB / 70.61MB  7.5s
 => => sha256:b18e45570b6f59bf80c15c78d7f0daff1e18e9c19069c323613 2.03kB / 2.03kB  0.0s
 => => sha256:3ef5b11bf99dc3829740e59e17dfaa491aa745816828286c6e0bf53 947B / 947B  0.0s
 => => sha256:97bc1352afdef16cbb71b5fe50c605e3174ba40c721ce9e0da3 3.40kB / 3.40kB  0.0s
 => => extracting sha256:ef87ded15917facc562d84e802d02ded848ae802cc04194400ba4cb8  1.9s
 => [internal] load build context                                                  0.1s
 => => transferring context: 67.98kB                                               0.0s
 => [runner 2/3] RUN addgroup -S -g 1000 app     && adduser -D -S -G app -u 1000   3.3s
 => CACHED [internal] helper image for file operations                             0.0s
 => [builder 2/3] ADD . /work                                                      0.5s
 => [builder 3/3] RUN --mount=type=cache,target=/root/.m2     ./mvnw clean inst  134.3s
 => [runner 3/3] COPY --from=builder --chown=app:app /work/target/demo-0.0.1-SNAP  1.9s
 => exporting to image                                                             0.1s
 => => exporting layers                                                            0.1s
 => => writing image sha256:d1628e99aa58995cf80c0f0e655dc136ea48953a3c75a2028088e  0.0s
 => => naming to docker.io/library/demo                                            0.0s

ビルドには151秒かかりました。

ビルド2回目

$ docker build --no-cache -t demo .
[+] Building 24.4s (15/15) FINISHED
 => [internal] load build definition from Dockerfile                               0.1s
 => => transferring dockerfile: 37B                                                0.0s
 => [internal] load .dockerignore                                                  0.0s
 => => transferring context: 2B                                                    0.0s
 => resolve image config for docker.io/docker/dockerfile:1.0-experimental          1.9s
 => CACHED docker-image://docker.io/docker/dockerfile:1.0-experimental@sha256:d2d  0.0s
 => [internal] load metadata for docker.io/library/openjdk:8u181-jdk-alpine3.8     0.7s
 => [internal] load metadata for docker.io/library/openjdk:8u181-jre-alpine3.8     0.8s
 => [internal] load build context                                                  0.0s
 => => transferring context: 1.67kB                                                0.0s
 => CACHED [internal] helper image for file operations                             0.0s
 => CACHED [runner 1/3] FROM docker.io/library/openjdk:8u181-jre-alpine3.8@sha256  0.0s
 => CACHED [builder 1/3] FROM docker.io/library/openjdk:8u181-jdk-alpine3.8@sha25  0.0s
 => [runner 2/3] RUN addgroup -S -g 1000 app     && adduser -D -S -G app -u 1000   2.6s
 => [builder 2/3] ADD . /work                                                      0.5s
 => [builder 3/3] RUN --mount=type=cache,target=/root/.m2     ./mvnw clean insta  17.6s
 => [runner 3/3] COPY --from=builder --chown=app:app /work/target/demo-0.0.1-SNAP  1.6s
 => exporting to image                                                             0.1s
 => => exporting layers                                                            0.1s
 => => writing image sha256:56669cdad5c5798018d9faa7598b9a63d93927b61f8b97c679c03  0.0s
 => => naming to docker.io/library/demo                                            0.0s

.m2のキャッシュが効いているようで、24秒でビルドできました。

おまけ

キャッシュ削除

キャッシュの削除方法は INTRODUCING DOCKER ENGINE 18.09 に書かれていました。

Build cache pruning and configurable garbage collection: Build cache can be managed separately from images and cleaned up with a new command docker builder prune. You can also set policies around when to clear build caches.

docker builder prune という新しいコマンドで削除できるとのことです。2
実際に削除してみました。

$ docker builder prune
WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
Deleted build cache objects:
uz11u06qximcevxlom5o0oqjr
81lx4qivrfshyovit8f0tjcfk
mgin0p02pfn3o43rvkjo4ivz1
mh2e2399h35v8nzp2u3ifzmc3
ltd1751lktom5ns0zqkgrog8n
bhvsubwkwkz11gysean5ylce1
i3fpig7npayhyh5t7gkfvtooe
abp0xlrlny8prxyl9bs90durc
sha256:0a3e38ed053dfc9a81a4f7cc87c7b1588c436663f726f013937491ab42fc2931

Total reclaimed space: 179.2MB

ビルド3回目

$ docker build --no-cache -t demo .
[+] Building 143.7s (15/15) FINISHED
 => [internal] load build definition from Dockerfile                               0.1s
 => => transferring dockerfile: 557B                                               0.0s
 => [internal] load .dockerignore                                                  0.0s
 => => transferring context: 2B                                                    0.0s
 => resolve image config for docker.io/docker/dockerfile:1.0-experimental          0.8s
 => CACHED docker-image://docker.io/docker/dockerfile:1.0-experimental@sha256:d2d  0.0s
 => [internal] load metadata for docker.io/library/openjdk:8u181-jre-alpine3.8     1.8s
 => [internal] load metadata for docker.io/library/openjdk:8u181-jdk-alpine3.8     1.2s
 => CACHED [runner 1/3] FROM docker.io/library/openjdk:8u181-jre-alpine3.8@sha256  0.0s
 => CACHED [internal] helper image for file operations                             0.0s
 => [internal] load build context                                                  0.1s
 => => transferring context: 67.98kB                                               0.0s
 => [builder 1/3] FROM docker.io/library/openjdk:8u181-jdk-alpine3.8@sha256:b18e  10.1s
 => => resolve docker.io/library/openjdk:8u181-jdk-alpine3.8@sha256:b18e45570b6f5  0.0s
 => => sha256:ef87ded15917facc562d84e802d02ded848ae802cc0419440 70.61MB / 70.61MB  7.6s
 => => sha256:b18e45570b6f59bf80c15c78d7f0daff1e18e9c19069c323613 2.03kB / 2.03kB  0.0s
 => => sha256:3ef5b11bf99dc3829740e59e17dfaa491aa745816828286c6e0bf53 947B / 947B  0.0s
 => => sha256:97bc1352afdef16cbb71b5fe50c605e3174ba40c721ce9e0da3 3.40kB / 3.40kB  0.0s
 => => extracting sha256:ef87ded15917facc562d84e802d02ded848ae802cc04194400ba4cb8  2.2s
 => [runner 2/3] RUN addgroup -S -g 1000 app     && adduser -D -S -G app -u 1000   2.8s
 => [builder 2/3] ADD . /work                                                      0.5s
 => [builder 3/3] RUN --mount=type=cache,target=/root/.m2     ./mvnw clean inst  126.6s
 => [runner 3/3] COPY --from=builder --chown=app:app /work/target/demo-0.0.1-SNAP  1.7s
 => exporting to image                                                             0.2s
 => => exporting layers                                                            0.1s
 => => writing image sha256:d832ff1987e91375bf080b69504233eafe717d2c46043a7179e9e  0.0s
 => => naming to docker.io/library/demo                                            0.0s

キャッシュを削除すると、143秒という1回目と同程度の時間がかかりました。

まとめ

Buildkitで --mount=type=cache を使うことで、Multi-stage buildでパッケージマネージャのキャッシュを利用できました。
同様にして、Maven以外のキャッシュも使えるはずです。

補足

CircleCIなどCIのSaaSを利用する場合、CI側のキャッシュ設定も必要なので注意してください。

参考


  1. 1回目と言いつつ実際には事前に何度か試しているため、ベースイメージなどのキャッシュが効いてしまっている部分があります。 

  2. docker system prune でもキャッシュは削除されました。 

22
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
11