すきま風

勉強したことのメモとか

Cloud Native Buildpacksのpack CLIでSpring BootのFat Jarを起動するDocker Imageを作成する。 あるいはSpring Boot 2.3.0.M1 のlayeredをエミュレートする

この記事の内容

  • pack CLIでLayer ArchitectのSpring Boot ApplicationのDocker Imageを作成する
  • 上記の目的のために、Spring Boot 2.3.0.M1のlayered()をエミュレートしたshell scriptを書く
  • 実用性皆無のネタ記事です 😑


Spring Boot 2.3.0.M1のBuildpacks サポートについて

Spring Boot 2.3.0.M1から、maven, gradleにデフォルトでDocker Imageを作成する機能が追加されました。

gradlew buildBootImage で簡単にDocker Imageが作成できます。こりゃ便利。

Layered Jarについて

Layer Architect, Clean Architectを採用しているApplicationの場合、 bootJarに layered() をつけてImageを作成します。記事を読む限り、Fat Jarを一旦解凍して再配置しているようです。 とにかく、Spring Boot 2.3 以降はDocker Imageをお手軽に作れるようになりそうです。

Spring Boot 2.2でもCloud Native Buildpacksを使いたいんじゃ!

という場合、pack CLIでimageを作成できます。 しかし、pack CLIにはデフォルトでFat Jarを使ったImageを作成する機能がないっぽいので、Clean ArchitectなApplicationの場合、普通に動きません 😑
(cf CLIならなんとかなるかもしれませんが、cloudFoundry使っていないので試せない)
とはいえ、Spring Boot 2.3.0.M1も結局はbuildpacksを使っているはずなので、頑張ればなんとかなるはず。

Spring Boot 2.3.0.M1のlayerToolsをエミュレートする

Spring Boot 2.3.0.M1で作成するImageと同じ感じにApplicationを配置すれば勝手に動くだろ、 という悪魔的な推測を元に、layerToolsをエミュレートするカジュアルなshを書きました。
Jibの実験で使っていたLayer ArchitectのApplicationをターゲットにしています。

#!/bin/bash

# layers.sh
TARGET_DIR="docker-app-web/build/libs"
WORK_DIR="build/workspace"

# build
./gradlew bootJar

# jar解凍
mkdir -p $WORK_DIR
cd $WORK_DIR
jar xf ../../$TARGET_DIR/docker-app-web-0.0.1-SNAPSHOT.jar
cd ../../

# layers directory
mkdir -p $WORK_DIR/BOOT-INF/layers/application
mkdir -p $WORK_DIR/BOOT-INF/layers/dependencies
mkdir -p $WORK_DIR/BOOT-INF/layers/snapshot-dependencies

# MANIFEST.MFを書き換える
head -n 4 $WORK_DIR/META-INF/MANIFEST.MF | nkf -Lu > $WORK_DIR/META-INF/NEW_MANIFEST.MF
echo "Spring-Boot-Layers-Index: BOOT-INF/layers.idx" >> $WORK_DIR/META-INF/NEW_MANIFEST.MF
echo "Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx" >> $WORK_DIR/META-INF/NEW_MANIFEST.MF

mv -f $WORK_DIR/META-INF/NEW_MANIFEST.MF $WORK_DIR/META-INF/MANIFEST.MF

# 依存jarをmv
find $WORK_DIR/BOOT-INF/lib -name "*SNAPSHOT.jar" | xargs -I% mv % $WORK_DIR/BOOT-INF/layers/snapshot-dependencies/
mv $WORK_DIR/BOOT-INF/lib/* $WORK_DIR/BOOT-INF/layers/dependencies
rmdir $WORK_DIR/BOOT-INF/lib

# main-class copy
mv $WORK_DIR/BOOT-INF/classes $WORK_DIR/BOOT-INF/layers/application/

# classpath.idx作成
find $WORK_DIR/BOOT-INF/layers/snapshot-dependencies -name "*.jar" | sed -e 's/build\/workspace\///' > $WORK_DIR/BOOT-INF/classpath.idx
find $WORK_DIR/BOOT-INF/layers/dependencies -name "*.jar" | sed -e 's/build\/workspace\///' >> $WORK_DIR/BOOT-INF/classpath.idx

# layers.idx作成
echo "snapshot-dependencies" > $WORK_DIR/BOOT-INF/layers.idx
echo "dependencies" >> $WORK_DIR/BOOT-INF/layers.idx
echo "resources" >> $WORK_DIR/BOOT-INF/layers.idx
echo "application" >> $WORK_DIR/BOOT-INF/layers.idx

echo "layered!!"

これで、

pack build docker-app-buildpacks:0.0.1-SNAPSHOT --builder cloudfoundry/cnb:bionic --path build/workspace

でImageを作成して

docker run -p 8080:8080 -w /workspace --entrypoint /layers/org.cloudfoundry.openjdk/openjdk-jre/bin/java --rm docker-app-buildpacks:0.0.1-SNAPSHOT -Dspring.profiles.active=development org.springframework.boot.loader.JarLauncher

で起動できました。やったぜ!(空虚な喜び)
でもentrypointを上書きするとかダサい、ダサくない?ということでpack buildを使わずにDockerfileを使う場合は

FROM adoptopenjdk:11-jre-hotspot
WORKDIR application
COPY build/workspace ./
COPY start.sh ./
ENTRYPOINT ["./start.sh"]
#!/bin/sh
exec java $@ org.springframework.boot.loader.JarLauncher

こんな感じ。というか結局Dockerfile書くならもっと良い方法たくさんありますよね。俺は何をやっているんだろう 😑

まとめ

Spring Boot 2.2でなんとかしてpack CLIを使ってみた、という記事でした。 無理やり感が凄いので、素直にSpring Boot 2.3のGAを待つことにします 🙃

おまけ

どうしてもpack CLIを使いたい場合、多分これが一番早いと思います。

./gradlew distZip
pack build docker-app-buildpacks:0.0.1-SNAPSHOT --builder cloudfoundry/cnb:bionic --path ./build/distributions/docker-app-0.0.1-SNAPSHOT.zip
docker run -p 8080:8080 --rm  --entrypoint /layers/org.cloudfoundry.openjdk/openjdk-jre/bin/java docker-app-buildpacks:0.0.1-SNAPSHOT -jar /workspace/docker-app-0.0.1-SNAPSHOT/docker-app-web-0.0.1-SNAPSHOT.jar

参考

First look at Cloud Native Buildpacks support in Spring Boot 2.3 Milestone 1

Creating Docker images with Spring Boot 2.3.0.M1

An App's Brief Journey from Source to Image · Cloud Native Buildpack Documentation