web-dev-qa-db-ja.com

Maven Dockerキャッシュの依存関係

ドッカーを使用してMavenビルドを自動化しようとしています。ビルドするプロジェクトは、すべての依存関係をダウンロードするのに20分近くかかります。そのため、これらの依存関係をキャッシュするdockerイメージをビルドしようとしましたが、保存されないようです。私のDockerfileは

FROM maven:Alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ADD pom.xml /usr/src/app
RUN mvn dependency:go-offline

イメージがビルドされ、すべてがダウンロードされます。ただし、結果のイメージはベースmaven:Alpineイメージと同じサイズであるため、イメージの依存関係がキャッシュされていないようです。イメージをmvn compileに使用しようとすると、すべてを再ダウンロードするのに20分かかります。

ビルドを実行するためにイメージを使用するたびにダウンロードする必要がないように、依存関係をキャッシュするMavenイメージをビルドすることは可能ですか?

私は次のコマンドを実行しています:

docker build -t my-maven .

docker run -it --rm --name my-maven-project -v "$PWD":/usr/src/mymaven -w /usr/src/mymaven my-maven mvn compile

私の理解では、Dockerビルドプロセス中にRUNが行うことはすべて、結果のイメージの一部になります。

27
Daniel Watrous

通常、pom.xmlファイルには変更はありませんが、Dockerイメージビルドを開始しようとするときに、他のソースコードがいくつか変更されるだけです。そのような状況では、これを行うことができます:

参考までに:

FROM maven:3-jdk-8

ENV HOME=/home/usr/app

RUN mkdir -p $HOME

WORKDIR $HOME

# 1. add pom.xml only here

ADD pom.xml $HOME

# 2. start downloading dependencies

RUN ["/usr/local/bin/mvn-entrypoint.sh", "mvn", "verify", "clean", "--fail-never"]

# 3. add all source code and start compiling

ADD . $HOME

RUN ["mvn", "package"]

EXPOSE 8005

CMD ["Java", "-jar", "./target/dist.jar"]

キーは次のとおりです。

  1. pom.xmlファイルを追加します。

  2. それからmvn verify --fail-never it、Maven依存関係をダウンロードします。

  3. すべてのソースファイルを追加し、コンパイルを開始します(mvn package)。

pom.xmlファイルに変更がある場合、またはこのスクリプトを初めて実行する場合、dockerは1-> 2-> 3.を実行します。pom.xmlファイルに変更がない場合、Dockerはステップ1、2をスキップして直接3を実行します。

この簡単なトリックは、他の多くのパッケージ管理環境(gradle、yarn、npm、pip)で使用できます。

編集:

他のコメントと回答が示唆するように、それに応じてmvn dependency:resolveまたはmvn dependency:go-offlineの使用も検討する必要があります。

25
Kim

ベースとして使用している画像には、定義する親画像があります

VOLUME "$USER_HOME_DIR/.m2"

参照: https://github.com/carlossg/docker-maven/blob/322d0dff5d0531ccaf47bf49338cb3e294fd66c8/jdk-8/Dockerfile

その結果、ビルド中にすべてのファイルが$USER_HOME_DIR/.m2に書き込まれますが、ボリュームであることが予想されるため、これらのファイルはいずれもコンテナーイメージに永続化されません。

現在、Dockerにはそのボリューム定義を登録解除する方法はないため、公式のMavenイメージを使用するのではなく、別のMavenイメージを作成する必要があります。

7
Daniel Watrous

@Kimは最も近いですが、まだ完全ではありません。 --fail-neverの追加は正しいとは思わない、たとえそれが仕事を成し遂げたとしても。

verifyコマンドにより、多くのプラグインが実行されますが、これは問題です(私にとって)-依存関係をインストールするだけの場合は、実行する必要があるとは思いません!また、マルチモジュールビルドとjavascriptサブビルドがあるため、セットアップがさらに複雑になります。

ただし、次のコマンドでverifyを実行すると、使用するプラグインが増えるため、ダウンロードする依存関係が増えるため、installのみを実行するだけでは不十分です。関連する読み取り: Maven:ビルドライフサイクルの概要

基本的に、各プラグインを無効にするプロパティを見つけて、それらを1つずつ追加する必要があります。これにより、ビルドが壊れないようになります。

WORKDIR /srv

# cache Maven dependencies
ADD cli/pom.xml /srv/cli/
ADD core/pom.xml /srv/core/
ADD parent/pom.xml /srv/parent/
ADD rest-api/pom.xml /srv/rest-api/
ADD web-admin/pom.xml /srv/web-admin/
ADD pom.xml /srv/
RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip

# cache YARN dependencies
ADD ./web-admin/package.json ./web-admin/yarn.lock /srv/web-admin/
RUN yarn --non-interactive --frozen-lockfile --no-progress --cwd /srv/web-admin install

# build the project
ADD . /srv
RUN mvn -B clean install

しかし、いくつかのプラグインはそれほど簡単にスキップされません-私はmavenの専門家ではありません(したがって、cliオプションを無視する理由はわかりません-それはバグかもしれません)が、以下はorg.codehaus.mojo:exec-maven-pluginに対して期待どおりに動作します

<project>
    <properties>
        <maven.exec.skip>false</maven.exec.skip>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.3.2</version>
                <executions>
                    <execution>
                        <id>yarn install</id>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <phase>initialize</phase>
                        <configuration>
                            <executable>yarn</executable>
                            <arguments>
                                <argument>install</argument>
                            </arguments>
                            <skip>${maven.exec.skip}</skip>
                        </configuration>
                    </execution>
                    <execution>
                        <id>yarn run build</id>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <phase>compile</phase>
                        <configuration>
                            <executable>yarn</executable>
                            <arguments>
                                <argument>run</argument>
                                <argument>build</argument>
                            </arguments>
                            <skip>${maven.exec.skip}</skip>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

明示的な<skip>${maven.exec.skip}</skip>に注意してください-他のプラグインはcliパラメーターからこれを選択しますが、このプラグインは選択しません(-Dmaven.exec.skip=true-Dexec.skip=trueも単独では機能しません)

お役に立てれば

4

@Kimの回答と似ていますが、dependency:resolve mvnコマンドを使用します。これが私の完全なDockerfileです。

FROM maven:3.5.0-jdk-8-Alpine

WORKDIR /usr/src/app

# First copy only the pom file. This is the file with less change
COPY ./pom.xml .

# Download the package and make it cached in docker image
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve

# Copy the actual code
COPY ./ .

# Then build the code
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml package

# The rest is same as usual
EXPOSE 8888

CMD ["Java", "-jar", "./target/YOUR-APP.jar"]
2
ikandars

数日苦労した後、私はこの中間キャッシングを使用してこのキャッシュを実行しました。このトピックは非常に有用であり、Google検索のフロントページに頻繁に表示されるため、ここで調査結果を要約します。

  1. キムの答えは特定の条件でのみ機能します:pom.xmlは変更できず、Mavenはデフォルトで毎日定期的に更新を行います
  2. mvndependency:go-offline -B --fail-neverには同様の欠点があるため、レポジトリから新しいコードをプルする必要がある場合、Mavenが毎回フルチェックアウトをトリガーする可能性が高い
  3. イメージのビルド中に依存関係を解決する必要があるため、マウントボリュームも機能しません。
  4. 最後に、私は実行可能な解決策を組み合わせています(他の人には機能しないかもしれません):
    • 最初にすべての依存関係を解決するイメージを構築します(中間イメージではありません)
    • 中間画像を使用して別のDockerfileを作成します。次のようなサンプルdockerfiles:
#docker build -t dependencies .
From ubuntu
COPY pom.xml pom.xml
RUN mvn dependency:go-offline -B --fail-never
From dependencies as intermediate

From Tomcat
RUN git pull repo.git (whatsoever)
RUN mvn package

アイデアは、Mavenがすぐに使用できる別のイメージにすべての依存関係を保持することです

まだ遭遇していない他のシナリオかもしれませんが、このソリューションは、なぜJavaが今日の無駄のない世界でこんなに太ったクジラになったのか想像できないたびに3GBのゴミをダウンロードすることから少し安心します

1
Calvin Zhou

ここでの他の答えが最適だとは思わない。たとえば、 mvn verify answerは次のフェーズを実行し、依存関係を解決するだけではありません。

validate-プロジェクトが正しいこと、および必要なすべての情報が利用可能であることを検証します

コンパイル-プロジェクトのソースコードをコンパイルします

test-適切な単体テストフレームワークを使用して、コンパイルされたソースコードをテストします。これらのテストでは、コードをパッケージ化またはデプロイする必要はありません

パッケージ-コンパイルされたコードを取得し、JARなどの配布可能な形式でパッケージ化します。

検証-統合テストの結果をチェックして、品質基準が満たされていることを確認します

依存関係のみを解決したい場合は、これらのすべてのフェーズとそれに関連する目標を実行する必要はありません。

依存関係のみを解決する場合は、 dependency:go-offline 目標:

FROM maven:3-jdk-12
WORKDIR /tmp/example/

COPY pom.xml .
RUN mvn dependency:go-offline

COPY src/ src/
RUN mvn package

BuildKitを使用する

Docker v18.03以降では、他の回答で言及されたボリュームの代わりに BuildKit を使用できます。ビルド間で保持できるキャッシュをマウントでき、対応する.m2/repositoryのコンテンツを毎回ダウンロードすることを回避できます。

Dockerfileがプロジェクトのルートにあると仮定します。

# syntax = docker/dockerfile:1.0-experimental

FROM maven:3.6.0-jdk-11-slim AS build
COPY . /home/build
RUN mkdir /home/.m2
WORKDIR /home/.m2
USER root
RUN --mount=type=cache,target=/root/.m2 mvn -f /home/build/pom.xml clean compile

target=/root/.m2は、MavenイメージDockerfileの指定された場所にキャッシュをマウントします docs

ビルドするには、次のコマンドを実行できます。

DOCKER_BUILDKIT=1 docker build --rm --no-cache  .   

BuildKitの詳細については、 こちら をご覧ください。

0
Farzad Vertigo

Maven依存関係をキャッシュする方法は2つあります。

  1. ビルドではなく、コンテナ実行の一部として「mvn verify」を実行し、ボリュームから.m2をマウントすることを確認します。

    これは効率的ですが、クラウドビルドおよび複数のビルドスレーブではうまく機能しません

  2. 「依存関係キャッシュコンテナー」を使用し、定期的に更新します。方法は次のとおりです。

    a。 pomをコピーしてオフライン依存関係を構築するDockerfileを作成します。

    FROM maven:3.5.3-jdk-8-Alpine
    WORKDIR /build
    COPY pom.xml .
    RUN mvn dependency:go-offline
    

    b。 「Deps:latest」として定期的に(夜間など)ビルドします

    c。別のDockerfileを作成して、コミットごとにシステムを実際に構築し(できればマルチステージを使用します)-FROM Depsであることを確認します。

このシステムを使用すると、ほとんど十分なキャッシュを備えた、高速で再構築可能なビルドができます。

0
Omri Spector

この問題はほんの少し前にありました。 Web上には多くのソリューションがありますが、私のために働いたのはmavenモジュールディレクトリのボリュームを単にマウントすることです:

mkdir /opt/myvolumes/m2

次に、Dockerfileで:

...
VOLUME /opt/myvolumes/m2:/root/.m2
...

より良いソリューションがありますが、それほど簡単ではありません。

このブログ投稿では、すべてをキャッシュするのに役立ちます。

https://keyholesoftware.com/2015/01/05/caching-for-maven-docker-builds/

0
Bruno9779