Spring Boot Thin Launcherで依存ライブラリーをDockerイメージのレイヤーに閉じ込めてみる

昨日、Spring Boot Thin Launcherの話を書いたけど。

bufferings.hatenablog.com

最後に、でもFat JARと比べて何が嬉しいのかな?って考えて

Dockerのレイヤーにライブラリーを閉じ込めてしまえば、依存関係が変わらない限り再利用できるなぁ、って思った。それはまた次の機会に書こうと思う。

ということで、とりあえず閉じ込めてみた話。でも、これが役に立つかどうかは正直知らん。

## コードはこれ

https://github.com/bufferings/spring-boot-thin-sandbox

demo-thin-dockerディレクトリ。

こんな感じで依存ライブラリを含まないThin JARを作って

cd demo-thin-docker
❯ ./mvnw clean package

Dockerイメージをビルド

❯ docker build -t demo-thin-docker .

ビルド中に依存ライブラリーのダウンロードをするので、しばらく時間がかかる。

で、実行すると

❯ docker run -p 8080:8080 demo-thin-docker - thin.debug=true - thin.offline=true20180930 13:42:32.978 INFO 1 - - [ main] com.example.demo.DemoApplication : Started DemoApplication in 3.437 seconds (JVM running for 5.804)

という感じ。依存ライブラリーは既にダウンロード済みなので、普通の速さで起動する。

一度ビルドすると、依存ライブラリーを変更しないかぎり次からはそのレイヤーが再利用されるからビルドに時間はかからない。

## Spring Boot Thin Launcherのオプション

Dockerfileの説明をする前にSpring Boot Thin Launcherのオプションについて触れておく。ここに書いてる

https://github.com/dsyer/spring-boot-thin-launcher#command-line-options

色々あって面白いんだけど、その中で今回使ってるやつをピックアップ

thin.dryrun

アプリケーションを起動せずに、依存関係のダウンロードだけをする。

thin.offline

オフラインモードになる。ローカルのリポジトリーにあるやつだけを使う。ローカルになければ例外になる。

thin.archive

普通に起動すると、その起動したJARを実行しようとするんだけど、それとは別のJARを対象にしたい場合に使う。これを使った理由は後で説明する。

thin.debug

デバッグ情報がでる。

thin.trace

めっちゃ色々でる。あ、これは確認するときに使ったってだけで、今日紹介する手順の中では使ってなかった。

## Dockerfile

はこうなってる。マルチステージビルドを使ってる。

FROM openjdk:11-jdk-slim
COPY docker/spring-boot-thin-wrapper-1.0.15.RELEASE.jar /thin/wrapper.jar
COPY pom.xml /thin/pom.xml
WORKDIR /thin
RUN jar cvf pom.jar pom.xml
RUN java -jar wrapper.jar \
     --thin.archive=/thin/pom.jar \
     --thin.dryrun=true \
     --thin.debug=true

FROM openjdk:11-jre-slim
COPY --from=0 /root/.m2 /root/.m2
COPY target/demo-thin-docker-0.0.1-SNAPSHOT.jar /app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

いっこずつ説明していく。

## FROM

FROM openjdk:11-jdk-slim

特に理由はないけど11を使ってみた。

## COPY wrapper.jar

COPY docker/spring-boot-thin-wrapper-1.0.15.RELEASE.jar /thin/wrapper.jar

対象のJARがまだない状態で、pom.xmlを元に依存ライブラリーだけダウンロードしてきたいなーと思ったので、その機能(ThinJarWrapper)を持ってるwrapper.jarをダウンロードしておいた。

## COPY pom.xml

COPY pom.xml /thin/pom.xml
WORKDIR /thin
RUN jar cvf pom.jar pom.xml

プロジェクトルートからpom.xmlをコピーしてJARに固める。wrapper.jarpom.xmlを直接読めたらいいなーって思ったんだけど、ソースを見てみたら、どうもJARじゃないとだめみたいなので、pom.xmlだけを持ったpom.jarを作ることにした。

## dry-run

RUN java -jar wrapper.jar \
     --thin.archive=/thin/pom.jar \
     --thin.dryrun=true \
     --thin.debug=true

dry-runモードでwrapper.jarを実行する。でも対象はwrapper.jarじゃなくてpom.jarの中のpom.xmlにしたいので、thin.archiveで指定。

thin.rootオプションを指定していないので、デフォルトの.m2ディレクトリに依存ライブラリーがダウンロードされる。

## COPY .m2

FROM openjdk:11-jre-slim
COPY --from=0 /root/.m2 /root/.m2

依存ライブラリーのダウンロードが終わったら、その.m2ディレクトリーだけがあればいい。wrapper.jarpom.jarは要らないので、別のイメージをFROMにして.m2だけを持ってくる。

## COPY app.jar and run

COPY target/demo-thin-docker-0.0.1-SNAPSHOT.jar /app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

で、あとはいつも通りアプリのJARをコピーしてきて、エントリーポイントを書いておく。

## Offline Mode

実行するときにダウンロードしてきてないことを確認するためにオフラインモードで起動してみた

❯ docker run -p 8080:8080 demo-thin-docker - thin.debug=true - thin.offline=true

実際にはここで何度かエラーが出て、試行錯誤したのであった(thin.propertiesを使う方法があって、最初はそっちでやってたんだけど、それだとランチャーがダウンロードされてなくて、ここで「ランチャーがないから無理!」って怒られたのであった。ごにょっとしたらいけたんだけど、それより素直にpomを使うことにした。)

## という感じ!

今のところThin Launcherは使うつもりはないけど、何かで「依存ライブラリーを切り離したいな」って思ったときには、この記事を読みなおしてやってみるかもなぁというくらい。

いい感じの用途を思いついたら教えてください!これでThin Launcher遊びはおしまい!