より速いMavenはDockerでビルドする


先週、私は述べたdifferent techniques Mavenビルドを固定するには今日は、スコープを広げて、Dockerの中のMavenビルドのために同じことをしたいです.
それぞれの実行の間に、1つの空白行を追加してソースコードを変更します各セクション間で、我々は多段ビルドの結果である中間のものを含むすべてのビルドされたイメージを削除します.アイデアは以前に建てられたイメージを再利用することを避けることです.

ベースライン
有用なベースラインを計算するには、サンプルプロジェクトが必要です.私の創造one この目的のために、それは比較的小さいKotlinプロジェクトです.
関連項目Dockerfile :
FROM openjdk:11-slim-buster as build                         #1

COPY .mvn .mvn                                               #2
COPY mvnw .                                                  #2
COPY pom.xml .                                               #2
COPY src src                                                 #2

RUN ./mvnw -B package                                        #3

FROM openjdk:11-jre-slim-buster                              #4

COPY --from=build target/fast-maven-builds-1.0.jar .         #5

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.0.jar"]     #6
  • 包装ステップのためのJDKイメージから始めてください
  • 必要なリソースを追加
  • 瓶を作る
  • イメージ作成ステップのためのJREから始めてください
  • 前のステップからjarファイルをコピーします
  • エントリポイントを設定する
  • ビルドを実行しましょう
    time DOCKER_BUILDKIT=0 docker build -t fast-maven:1.0 .      #1
    
  • 次のセクションで説明するように、今の環境変数を忘れてください
  • ここでは、5つのランの結果です
    * 0.36s user 0.53s system 0% cpu 1:53.06 total
    * 0.36s user 0.56s system 0% cpu 1:52.50 total
    * 0.35s user 0.55s system 0% cpu 1:56.92 total
    * 0.36s user 0.56s system 0% cpu 2:04.55 total
    * 0.38s user 0.61s system 0% cpu 2:04.68 total
    

    勝利のためのビルディングキット
    最後のコマンドラインはDOCKER_BUILDKIT 環境変数.それは、dockerにレガシーエンジンを使うように言う方法です.あなたがしばらくの間Dockerを更新しなかったならば、それはあなたが使っているエンジンです.最近ではBuildKit が設定されていて、新しいデフォルトである.
    Buildkitはいくつかのパフォーマンス向上をもたらします.
  • 自動ガベージコレクション
  • 同時依存依存解決
  • 効率的な命令キャッシュ
  • キャッシュのインポート/エクスポート
  • など
  • 新しいエンジンの前のコマンドを再実行しましょう:
    time docker build -t fast-maven:1.1 .
    
    最初のランのコンソールログの抜粋です.
    ...
     => => transferring context: 4.35kB
     => [build 2/6] COPY .mvn .mvn
     => [build 3/6] COPY mvnw .
     => [build 4/6] COPY pom.xml .
     => [build 5/6] COPY src src
     => [build 6/6] RUN ./mvnw -B package
    ...
    
    0.68s user 1.04s system 1% cpu 2:06.33 total
    
    以下のコマンドの実行には少し異なる出力があります.
    ...
     => => transferring context: 1.82kB
     => CACHED [build 2/6] COPY .mvn .mvn
     => CACHED [build 3/6] COPY mvnw .
     => CACHED [build 4/6] COPY pom.xml .
     => [build 5/6] COPY src src
     => [build 6/6] RUN ./mvnw -B package
    ...
    
    実行中にソースコードを変更することを忘れないでください.変更しないファイル.mvn , mvnw and pom.xml , はbuildkitによってキャッシュされる.しかし、これらのリソースは小さいので、キャッシュは大幅にビルド時間を改善しません.
    * 0.69s user 1.01s system 1% cpu 2:05.08 total
    * 0.65s user 0.95s system 1% cpu 1:58.51 total
    * 0.68s user 0.99s system 1% cpu 1:59.31 total
    * 0.64s user 0.95s system 1% cpu 1:59.82 total
    
    ログを高速に見ると、ビルドの最大ボトルネックは、プラグインを含むすべての依存関係のダウンロードです.ソースコードを変更するたびに発生します.それがbuildkitがパフォーマンスを改善しない理由です.

    層、層、層
    我々は努力を依存関係に集中すべきだ.そのためには、レイヤを活用し、ビルドを2つのステップに分割できます.
  • 最初のステップでは、依存関係をダウンロードします
  • 第二に、我々は適切な包装を行う
  • 各々のステップは層をつくります.
    レイヤリングでは、2番目のレイヤーでソースコードを変更すると、最初のレイヤーが影響を受けず、再利用できます.我々は再び依存関係をダウンロードする必要はありません.新しいDockerfile 以下のようになります.
    FROM openjdk:11-slim-buster as build
    
    COPY .mvn .mvn
    COPY mvnw .
    COPY pom.xml .
    
    RUN ./mvnw -B dependency:go-offline                          #1
    
    COPY src src
    
    RUN ./mvnw -B package                                        #2
    
    FROM openjdk:11-jre-slim-buster
    
    COPY --from=build target/fast-maven-builds-1.2.jar .
    
    EXPOSE 8080
    
    ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.2.jar"]
    
  • The go-offline すべての依存関係とプラグイン
  • この時点で、すべての依存関係が利用可能です
  • 注意go-offline すべてをダウンロードしません.コマンドを実行しようとするとうまくいかない-o オプション(オフライン用).それはa well-known old bug . すべてのケースでは、それは“十分に良い”です.
    ビルドを実行しましょう
    time docker build -t fast-maven:1.2 .
    
    最初のランはベースラインよりかなり時間がかかります.
    0.84s user 1.21s system 1% cpu 2:35.47 total
    
    しかし、以降のビルドは非常に高速です.ソースコードを変更するのは2層目にしか影響しません.
    * 0.23s user 0.36s system 5% cpu 9.913 total
    * 0.21s user 0.33s system 5% cpu 9.923 total
    * 0.22s user 0.38s system 6% cpu 9.990 total
    * 0.21s user 0.34s system 5% cpu 9.814 total
    * 0.22s user 0.37s system 5% cpu 10.454 total
    

    ボリュームマウント
    ビルドの階層化は、ビルド時間を大幅に改善しました.我々は、ソースコードを変更することができますし、それを低く保つ.しかし、1つの残りの問題があります.単一の依存関係を変更すると、レイヤーを無効にするので、再度すべてをダウンロードする必要があります.
    幸いにも、Buildkitはビルド中にボリュームマウントを導入します.マウントのいくつかの種類がご利用いただけますが、我々の興味のあるものはcache mount . これは実験的な機能なので、明示的にオプトインする必要があります.
    # syntax=docker/dockerfile:experimental                      #1
    FROM openjdk:11-slim-buster as build
    
    COPY .mvn .mvn
    COPY mvnw .
    COPY pom.xml .
    COPY src src
    
    RUN --mount=type=cache,target=/root/.m2,rw ./mvnw -B package #2
    
    FROM openjdk:11-jre-slim-buster
    
    COPY --from=build target/fast-maven-builds-1.3.jar .
    
    EXPOSE 8080
    
    ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.3.jar"]
    
  • 実験的特徴へのオプトイン
  • キャッシュを使用してビルド
  • ビルドを実行する時です.
    time docker build -t fast-maven:1.3 .
    
    ビルド時間は通常のビルドよりも高くなりますが、まだビルドされているレイヤーよりも低くなります.
    0.71s user 1.01s system 1% cpu 1:50.50 total
    
    以下のビルドはレイヤと同等です.
    * 0.22s user 0.33s system 5% cpu 9.677 total
    * 0.30s user 0.36s system 6% cpu 10.603 total
    * 0.24s user 0.37s system 5% cpu 10.461 total
    * 0.24s user 0.39s system 6% cpu 10.178 total
    * 0.24s user 0.35s system 5% cpu 10.283 total
    
    しかし、層とは対照的に、更新された依存関係をダウンロードする必要があります.ここからKotlinのバージョンを変えましょう1.5.30 to 1.5.31 :
    <properties>
        <kotlin.version>1.5.31</kotlin.version>
    </properties>
    
    ビルド時間に関する大きな改善です.
    * 0.41s user 0.57s system 2% cpu 44.710 total
    

    Mavenデーモンを考える
    前の投稿でregular Maven builds , 私は、Mavenデーモンに言及しました.したがって、ビルドを変更しましょう.
    FROM openjdk:11-slim-buster as build
    
    ADD https://github.com/mvndaemon/mvnd/releases/download/0.6.0/mvnd-0.6.0-linux-amd64.zip . #1
    
    RUN apt-get update \                                         #2
     && apt-get install unzip \                                  #3
     && mkdir /opt/mvnd \                                        #4
     && unzip mvnd-0.6.0-linux-amd64.zip \                       #5
     && mv mvnd-0.6.0-linux-amd64/* /opt/mvnd                    #6
    
    COPY .mvn .mvn
    COPY mvnw .
    COPY pom.xml .
    COPY src src
    
    RUN /opt/mvnd/bin/mvnd -B package                            #7
    
    FROM openjdk:11-jre-slim-buster
    
    COPY --from=build target/fast-maven-builds-1.4.jar .
    
    EXPOSE 8080
    
    ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.4.jar"]
    
  • Mavenデーモンの最新版をダウンロードしてください
  • パッケージインデックスを更新する
  • インストールunzip
  • 専用フォルダの作成
  • 我々がダウンロードしたアーカイブを抽出します
  • 抽出したアーカイブの内容を以前に作成したフォルダに移動する
  • 用途mvnd Mavenラッパーの代わりに
  • では、ビルドを実行しましょう.
    docker build -t fast-maven:1.4 .
    
    ログは次のように出力します.
    * 0.70s user 1.01s system 1% cpu 1:51.96 total
    * 0.72s user 0.98s system 1% cpu 1:47.93 total
    * 0.66s user 0.93s system 1% cpu 1:46.07 total
    * 0.76s user 1.04s system 1% cpu 1:50.35 total
    * 0.80s user 1.18s system 1% cpu 2:01.45 total
    
    ベースラインに比べて有意な改善はない.
    私は専用を作成しようとしましたmvnd 親画像として使用します.
    # docker build -t mvnd:0.6.0 .
    FROM openjdk:11-slim-buster as build
    
    ADD https://github.com/mvndaemon/mvnd/releases/download/0.6.0/mvnd-0.6.0-linux-amd64.zip .
    
    RUN --mount=type=cache,target=/var/cache/apt,rw apt-get update \
     && apt-get install unzip \
     && mkdir /opt/mvnd \
     && unzip mvnd-0.6.0-linux-amd64.zip \
     && mv mvnd-0.6.0-linux-amd64/* /opt/mvnd
    
    # docker build -t fast-maven:1.5 .
    FROM mvnd:0.6.0 as build
    
    # ...
    
    このアプローチは、出力を任意の重要な方法で変更します.mvnd デーモンがいくつかの実行の間、アップするとき、良いだけです.私はDockerでそれをする方法を見つけませんでした.あなたがそれを達成する方法についてのアイデアがあれば、私に言ってくださいあなたが実装に私を指すことができるならば、余分な点.
    すべての実行時間の概要を以下に示します.
    ベースライン
    ビルドキット
    レイヤー
    ボリュームマウント
    MVND
    1/1
    113.06
    125.08
    155.47
    110.5
    111.96
    - 1/2
    112.5
    118.51
    9.91
    9.68
    107.93
    - 1/3
    116.92
    119.31
    9.92
    10.6
    106.07
    - 1/4
    124.55
    119.82
    9.99
    10.46
    110.35
    5/5
    124.68
    9.81
    10.18
    121.45
    六六六
    10.45
    10.28
    九七七
    44.71
    平均値
    118.34
    120.68
    9.91
    10.24
    111.55
    偏差
    28.55
    6.67
    0.01
    0.10
    111.47
    ベースラインから得る
    0
    - 2.34
    108.43
    108.10
    6.79
    %ゲイン
    0.00 %
    - 1.98 %
    91.63 %
    91.35 %
    5.74 %

    結論
    Dockerの中でMavenのビルドを高速化するのは、通常のビルドとはかなり異なります.Dockerでは、制限された要因は依存関係のダウンロード速度です.
    BuildKitを使用すると、レイヤーが無効になった場合、すべての依存関係をダウンロードするのを避けるために新しいキャッシュマウント機能を使用することをお勧めします.
    この記事の完全なソースコードは、Maven形式でGithubにあります.

    ajavageek / fast-maven-builds
    さらに進む
  • Faster Maven builds
  • Introducing BuildKit
  • Docker Layers Explained
  • Build Mounts
  • 当初公開A Java Geek 2021年10月10日