JDBC 経由で DB に接続したい時に、sqlline という SQL コマンドラインツールを使っています。このツールは既に fatjar や Docker Image でも提供されているのですが、JRE や Docker をインストールしていない環境でも使いたいことが何度かありました。

Java 9 から jlink というツールが提供されています。このツールは、実行可能なバイナリを含んだ JRE を生成します。生成する JRE には、必要なモジュールのみを含められるようになっている為、ファイルサイズを抑えることができそうです。

当初はこの jlink を使って sqlline の実行可能なバイナリを作成しようとしたのですが、jlink は automatic module (Java 9 以前の module-info.java が無いモジュール) に対応していない為、実行可能なバイナリを作成することができませんでした。sqlline に module-info.java を追加してみたのですが、依存しているモジュールが automatic module だとエラーになってしまいます。

しかし、jlink を使うことで sqlline の fatjar を実行できる JRE を生成することはできます。そこで今回は jlink で生成した JRE + sqlline の fatjar のファイルサイズを確認し、Docker Image とファイルサイズの比較をしてみました。

Environment

sqlline をビルドする環境には adoptopenjdk/openjdk11 を使いました。但し、pom.xml に記載された source / target は 1.8 のままにしています。

Module Dependency

jlink でバイナリを生成するにあたり、fatjar 実行時に必要になるモジュールを調べておきます。これらには、fatjar には含まれないものの実行時には必要なモジュールを指定することになります。sqlline の場合は以下です。

javafx.swing は JDK から外れてしまっているものの、maven リポジトリから取得することができます。jlink で使うので、pom.xml の dependencies に追加しておきます。

$ git diff
diff --git a/pom.xml b/pom.xml
index b8ff859..9abfbc9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -326,6 +326,12 @@
   </build>

   <dependencies>
+    <dependency>
+      <groupId>org.openjfx</groupId>
+      <artifactId>javafx-swing</artifactId>
+      <version>11.0.2</version>
+      <scope>provided</scope>
+    </dependency>
     <dependency>
       <groupId>org.jline</groupId>
       <artifactId>jline-terminal</artifactId>

まずはビルドします。その後、mvn dependency:copy-dependencies を実行して javafx-* を取り出して任意のディレクトリに配置します。そして jlink を実行すると output フォルダに指定したモジュールを含んだ実行環境が生成されます。この output フォルダには fatjar は含まれていないので、自分でコピーします。

$ mvn clean package -DskipTests -Dmaven.javadoc.skip -Dcheckstyle.skip
$ mvn dependency:copy-dependencies
$ mkdir modules
$ cp -ip target/dependency/javafx-* modules/
$ jlink --compress=2 --module-path modules --add-modules java.base,java.sql,javafx.swing,jdk.unsupported --output output
$ cp -ip target/sqlline-1.7.0-SNAPSHOT-jar-with-dependencies.jar output/

output フォルダは以下のようになっています。

$ ls -l output
total 2208
drwxr-xr-x  2 root root    4096 Mar  2 16:01 bin
drwxr-xr-x  3 root root    4096 Mar  2 16:01 conf
drwxr-xr-x  3 root root    4096 Mar  2 16:01 include
drwxr-xr-x 11 root root    4096 Mar  2 16:01 legal
drwxr-xr-x  5 root root    4096 Mar  2 16:01 lib
-rw-r--r--  1 root root     192 Mar  2 16:01 release
-rw-r--r--  1 root root 2234860 Mar  2 15:49 sqlline-1.7.0-SNAPSHOT-jar-with-dependencies.jar

output フォルダのサイズは 62M でした。

$ du -d 1 -h . | grep output
62M     ./output

これを tar.gz で固め、配布可能な1ファイルとします。固めた後のファイルサイズは 43M になりました。

$ tar cvfz sqlline-1.7.0-executable.tar.gz output
$ ls -lh sqlline-1.7.0-executable.tar.gz
-rw-r--r-- 1 root root 43M Mar  2 16:04 sqlline-1.7.0-executable.tar.gz

Execute on non-JRE Environment

生成したバイナリファイルを、JRE がインストールされていないコンテナに持っていって展開したところ、無事動きました。

rails@745e84994342:/usr/local/src/output$ java
bash: java: command not found
rails@745e84994342:/usr/local/src$ tar xf ./sqlline-1.7.0-executable.tar.gz
rails@745e84994342:/usr/local/src$ cd output/
rails@745e84994342:/usr/local/src/output$ bin/java -jar sqlline-1.7.0-SNAPSHOT-jar-with-dependencies.jar
sqlline version 1.7.0-SNAPSHOT
sqlline> !quit

File size comparison

sqlline を Docker で提供しているいくつかの image を pull してみたところ、Size は以下のようになっていました。

$ docker images | grep sqlline
joshelser/sqlline        latest              943cfdc9ccda        23 months ago       277 MB
veltio/sqlline           latest              a839cf56b131        2 years ago         602.1 MB
gleisonsilva/sqlline     latest              d5800d44684a        2 years ago         602.1 MB

jlink で生成した output フォルダのサイズは 62M なので、joshelser/sqlline14 程度の大きさになります。

Conclusion

JPMS に対応していないプロジェクトでも、jlink を使ってファイルを生成することにより、その fatjar と実行環境をコンパクトなファイルサイズで配布することができます。