はじめに
openjdk 11のdocker imageは、docker hubのofficial respoitoryで公開されています。
ただ・・・これを使う上でちょっと気になるのが、
- イメージサイズがそもそも大きい。約1GBもあります。
- jlinkを使っても、300MB以上ある
という問題があります。もっと小さくしたいな。という所で、どれくらい小さくできるか試してみました!
前提
- Java Flight Recorderが動くこと
- Spring Boot アプリケーションが動くこと
結果
1GB→85MBと1/10以下に圧縮できました
個人的には大満足です。
docker images
でサイズを確認した結果は下記の通りです。
imageの種類 | jlink使用 | size |
---|---|---|
openjdk:11-jdk | しない | 1GB |
openjdk:11-jdk | する | 468MB |
独自alpinelinux | しない | 336MB |
独自alpinelinux | する | 84.6MB |
jdk11はea版です。そのうちちゃんとopenjdk 11のofficial imageでも小さいイメージが作れるようになると思います。
独自alpinelinuxは、
https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/
です。
問題
OpenJDK 11のofficial imageの問題を詳細に記載します。
イメージサイズが大きい
$ sudo docker images |grep jdk
docker.io/openjdk 11-jdk f684efd78557 2 weeks ago 979 MB
$
jreではちょっとflightrecorderが動かないので、jdkを使いたいのに・・・ちょっと大きすぎます。
参考:Qiita - OpenJDK11-JRE-SLIMじゃFlightRecorderは動きません。
openjdk 11(official)ではjlink使っても小さくならない。
下記issueでも同じ問題を取り上げています。
https://github.com/docker-library/openjdk/issues/217
試しにやると・・・下記のようになぜか400MBほどになり、imageサイズも450MBくらいになってしまいます。
$ docker run -it --rm openjdk:11-jdk /bin/sh
# ls -l /usr/lib/jvm/java-11-openjdk-amd64/lib/server/
total 34944
-rw-r--r-- 1 root root 1322 Jul 27 03:41 Xusage.txt
-r--r--r-- 1 root root 18210816 Jul 27 22:22 classes.jsa
-rw-r--r-- 1 root root 14440 Jul 27 03:41 libjsig.so
-rw-r--r-- 1 root root 17551048 Jul 27 03:41 libjvm.so #まだ小さい
圧縮してみる
# jlink \
--module-path /opt/java/jmods \
--compress=2 \
--add-modules java.base,java.logging,jdk.jfr \
--no-header-files \
--no-man-pages \
--output /opt/jdk-11-mini-runtime
# ls -l /opt/jdk-11-mini-runtime/lib/server/
total 414452
-rw-r--r-- 1 root root 1322 Aug 14 09:41 Xusage.txt
-rw-r--r-- 1 root root 25384 Aug 14 09:41 libjsig.so
-rw-r--r-- 1 root root 424362808 Aug 14 09:41 libjvm.so #なんでや・・・
#
イメージサイズ小さくするぞ!
alpine linuxを使う、そしてjlinkを使って小さくするというアプローチをとってみます。
alpine linuxのベースのbuild用イメージを作成する。
他の人でも約に立つかな?と思って公開してます。
image: https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/
docker file: https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/~/dockerfile/
checksumしてなくてすみません…。
jlinkを使って小さい実行イメージを作る。
multi stageを使ってビルドします。
- 上記で作ったalpine linux + openjdk 11のimageをベースに、jlinkで小さいjava runtime moduleを作成します。
- runtime module、jar moduleをコピーしたalphine linuxのimageを作成します。
FROM hirokimatsumoto/alpine-openjdk-11:latest as jlink-package
# First: generate java runtime module by jlink.
RUN jlink \
--module-path /opt/java/jmods \
--compress=2 \
--add-modules jdk.jfr,jdk.management.agent,java.base,java.logging,java.xml,jdk.unsupported,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
--no-header-files \
--no-man-pages \
--output /opt/jdk-11-mini-runtime
# Second: generate run image.
FROM alpine:3.8
ENV JAVA_HOME=/opt/jdk-11-mini-runtime
ENV PATH="$PATH:$JAVA_HOME/bin"
COPY --from=jlink-package /opt/jdk-11-mini-runtime /opt/jdk-11-mini-runtime
COPY target/k8s-jmc-sample-0.1.0-SNAPSHOT.jar /opt/spring-boot/
EXPOSE 30001 7199
CMD java \
-XX:StartFlightRecording=name=sample,filename=/spring-boot/jfr/sample.jfr,delay=30s,maxage=2h,maxsize=10m,dumponexit=true,settings=/spring-boot/config/profile.jfc \
-Dcom.sun.management.jmxremote.port=7199 \
-Dcom.sun.management.jmxremote.rmi.port=7199 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar /opt/spring-boot/k8s-jmc-sample-0.1.0-SNAPSHOT.jar \
--server.port=${SPRING_SERVER_PORT} \
--spring.config.location=/spring-boot/config/application.yaml
実行アプリケーションは、
https://github.com/h-r-k-matsumoto/k8s-jmc-sample
です。
ハマったこと
Spring Bootアプリケーションをjdepsで依存関係見ても正確ではない
最初jdepsを使い、下記のように必要なmoduleを確認してました。
> jdeps --list-deps .\target\k8s-jmc-sample-0.1.0-SNAPSHOT.jar
java.base
java.logging
>
これでruntime作っても・・・実行時に、下記のようにエラーになります。
Caused by: java.lang.ClassNotFoundException: java.sql.SQLException
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:93)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
うーん、spring boot内部でauto configurationしている箇所は上手く追跡できないのかな・・・。
めんどくさかったので、
https://dev.solita.fi/2018/01/24/Java9-modules-Spring-Boot-2-Docker.html
みて適当に追加しました。
参考
- OpenJDK official image: https://hub.docker.com/_/openjdk/
- 独自のalpine linux image: https://hub.docker.com/r/hirokimatsumoto/alpine-openjdk-11/
- Spring Boot + jlinkの参考情報: https://dev.solita.fi/2018/01/24/Java9-modules-Spring-Boot-2-Docker.html