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 の場合は以下です。
- java.base
- java.sql
- javafx.swing
- 何故か sqlline の sqlline.ClassNameCompleter で javax.swing.JFrame を参照している
- jdk.unsupported
- INT シグナルの trap で sun.misc パッケージを参照している
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>
Create Binary Files with jlink
まずはビルドします。その後、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/sqlline の 1⁄4 程度の大きさになります。
Conclusion
JPMS に対応していないプロジェクトでも、jlink を使ってファイルを生成することにより、その fatjar と実行環境をコンパクトなファイルサイズで配布することができます。