Java 9から含まれているjlinkを使うと、必要なモジュールだけに絞ってコンパクトなランタイムを作る事ができるので、アプリをコンテナで動かす時は便利そうです。

JEP 282: jlink: The Java Linker
http://openjdk.java.net/jeps/282

Java Day Tokyo 2018でこれに関するセッションがあり、スライドと動画が公開されています。
jlinkで実際どのくらい小さくできるのか例が出されているので、おすすめです。

Java in a World of Containers
https://www.oracle.com/technetwork/jp/ondemand/online2018-javaday-4489556-ja.html


OpenJDK 11でjlinkを試してみたところ、jlinkで生成したランタイムのサイズがめっちゃ大きくなってしまうという問題に遭遇したので、内容をまとめておきます。


使用したバージョン

コンテナイメージを作る時にはまず、ベースイメージの選定から始めると思いますが、
OpenJDKのコンテナイメージないかなーと思って、「openjdk docker」というキーワードでググると以下のものが見つかります。

https://hub.docker.com/_/openjdk/

OpenJDKコミュニティがメンテしているのかなと思ってたんですが、メンテしているのはDockerコミュニティです。(ページ読むとちゃんと書いてありますね。。。)
そして、このコンテナでは、OpenJDKのソースをDebianコミュニティがビルドしたJDKを使っています。

root@c1802c1ad8ea:/# java -version
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment (build 11+28-Debian-3)
OpenJDK 64-Bit Server VM (build 11+28-Debian-3, mixed mode, sharing)

どんな問題が発生したのか?

jlinkで生成したランタイムに含まれる lib/server/libjvm.so が__421M__になってしまいました。

root@c1802c1ad8ea:/# ls -lh myjre/lib/server/libjvm.so
-rw-r--r-- 1 root root 421M Oct 12 19:05 myjre/lib/server/libjvm.so

この問題は既にコンテナイメージのソースrepoで報告されていますが、「自分達はただDebianのパッケージを使ってるだけなので、問題はDebianに報告して(意訳)」との事。
https://github.com/docker-library/openjdk/issues/217

issue #217にもコメントがありますが、Debianには報告済で、理由は「デバッグシンボルを含んでいるから」だそうです。
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=905575


正常なサイズがどのくらいかと言うと、Oracle OpenJDK 11のjlinkでランタイムを作る場合は__23M__です。

root@078e954e39dd:/# ls -lh myjre/lib/server/libjvm.so
-rw-r--r-- 1 root root 23M Oct 12 19:05 myjre/lib/server/libjvm.so

Java 11のリリースからOracle JDKのライセンスが変わり、無償で使いたい人はどのJDKを使ったら良いか?という話題が最近多いですが、
個人的に、Docker Hubのopenjdkイメージは使わない方が良さそうな気がします。

AdoptOpenJDK公式のDockerイメージがあるので、こっちの方が良いです。
https://hub.docker.com/r/adoptopenjdk/openjdk11/

root@6fbc9dafe0e5:/# java -version
Picked up JAVA_TOOL_OPTIONS: -XX:+UseContainerSupport
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment AdoptOpenJDK (build 11+28)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11+28, mixed mode)

余談ですが、-XX:+UseContainerSupportって何だろう?と思って調べたら、Java 10でDockerに関するオプションが追加されていたようです。

Java 10でぼくたちの生活はどう変わるの? from Yuji Kubota

jlinkでどのくらいコンテナイメージを小さくできる?

jlinkでランタイムを生成したい場合は、
「JDKが入っていない小さなベースイメージ + jlinkで生成したランタイム + アプリ」
という構成にすると良いでしょう。

例えば、以下のような組み合わせだと、Dockerイメージサイズは165MBまで小さくできました。

  • debian:9.5-slim(Dockerイメージサイズ: 55.3MB)
  • jlinkで生成したランタイム(ディレクトリサイズ: 56M)
  • とあるSpring Bootアプリ(ファイルサイズ: 50MB)

ちなみに、同じアプリをjlinkを使わずにAdoptOpenJDK公式のDockerイメージそのまま使う場合、Dockerイメージサイズは506MBになります。

  • adoptopenjdk/openjdk11:jdk-11.28(Dockerイメージサイズ: 454MB)
  • とあるSpring Bootアプリ(ファイルサイズ: 50MB)

jlinkいいですね。


まとめ

  • jlinkを使うとコンテナイメージが小さくできる
  • Docker Hubのopenjdkイメージは使わない方が良さそう
  • AdoptOpenJDK公式のDockerイメージがある

あわせて読みたい

Introduction to Java 11: Support and JVM Features #jjug from Yuji Kubota