sbt-native-imageでバイナリを生成する方法
[AD] Scalaアプリケーションの開発・保守は合同会社ミルクソフトにお任せください
この記事では、sbt-native-image プラグインの使い方を解説します。
sbt-native-image はバイナリ生成用プラグイン
sbt-native-imageプラグインを使用すると、Scalaプログラムをネイティブ環境で実行できるようバイナリを出力することができます。
生成に必要なGraalVMを自動でインストールしてくれるので大変便利です。
sbt-native-imageを使用してネイティブイメージを生成する手順
環境構築をする
まず、現在のバージョンでは、プロジェクトで使用するJDKのバージョンと、sbt-native-imageで使用するJDKのバージョンを一致させる必要があります。
公式は「手動で環境構築する手間がない」を謳っていますが、この点には注意です。
一致していない場合、例えばJava 11でコンパイルしたコードのネイティブイメージを生成すると、実行時にGraalVMが実行できない旨のエラーを出力します。
プロジェクトを作成する
まずはsbt-native-imageプラグインを使用するサンプルプロジェクトを用意します。
テンプレートに scala/scala-seed.g8 を使います。
shellsbt new scala/scala-seed.g8
初回の実行には時間がかかります。関連するいろいろなライブラリ等をダウンロードするためです。
途中でプロジェクト名をつけるよう尋ねられるので、ここではsbt-native-image-sample
と入力します。
sbt shell[info] resolving Giter8 0.13.1... A minimal Scala project. name [Scala Seed Project]: sbt-native-image-sample Template applied in <current directory>/sbt-native-image-sample
sbt-native-imageを設定する
さて、sbt-native-imageを使ってネイティブイメージを出力してみましょう。
project/plugins.sbt を開いて以下の内容を追記し、sbt-native-imageプラグインを有効にします。
plugins.sbtaddSbtPlugin("org.scalameta" % "sbt-native-image" % "0.2.0")
また、build.sbtに以下の内容を追記します。
プロジェクトでNativeImagePlugin
を有効にし、起動時のエントリーポイントを明らかにします。
build.sbtlazy val proj = project ... .enablePlugins(NativeImagePlugin) .settings( ... Compile / mainClass := Some("com.my.MainClass") ... )
(任意)JVMのバージョンを切り替える
必要に応じて以下のようにsbt-native-imageで使用するJavaのバージョンを選びましょう。Java 8またはJava 11を使用することができます。
デフォルトはJava 11です。
nativeImageJvm
にgraalvm-java11
(デフォルト)もしくはgraalvm
を設定するとJVMのバージョンを切り替えることができます。
build.sbt.settings( ... nativeImageJvm := graalvm-java11 ... )
sbt シェルで nativeImageコマンドを実行する
これで準備が整ったので、sbtシェルでnativeImage
コマンドを実行します。
sbt shell> nativeImage ... [info] Native image ready! [info] <project root>/target/native-image/sbt-native-image-sample [success] Total time: 74 s (01:14), completed 2020/09/01 1:11:11
初回の実行には時間がかかります。GraalVMのイメージなどいろいろなものをダウンロードするためです。
生成に成功すると、上のログのようにバイナリのパスが表示されます。
生成したネイティブイメージを実行する
さて、それでは生成したネイティブイメージを実行してみましょう。
実行した結果、"hello"とだけ出力されればOKです。
shell$ /target/native-image/sbt-native-image-sample hello
爆速で実行完了します。
どれくらいなのか時間を計測してみましょう。
shell$time /target/native-image/sbt-native-image-sample hello sbt-native-image-sample/target/native-image/native-image-sampl 0.00s user 0.00s system 72% cpu 0.007 total
実行時にJVMを起動していたらこんなものでは済まないので、間違いなくネイティブで実行できていることがわかります。
サンプルコードはこちらのリポジトリで公開しています。
sbt-native-packager との違いは?
既存の類似のプラグインとしてsbt-native-packagerがあります。
今後変わる可能性もありますが、現段階でsbt-native-imageと比較してみましょう。
sbt-native-image はGraalVMのネイティブイメージを自動的にインストールする
sbt-native-image はデフォルトで GraalVMのネイティブイメージを自動的にインストールします。
sbt を起動する前に docker イメージを設定したり、正しい GraalVM JDK を手動でインストールしたりする必要はありません。
ただし、上述のように使用しているGraalVM JDKのバージョンが適切に設定されているかどうかに注意してください。
sbt-native-image はScala 2.12.12+ あるいは 2.13.3+ で正常に動作する
sbt-native-image は、Scala 2.12.12+ および 2.13.3+ で正常に動作することが確認されています。
これに対して、sbt-native-packagerは正常に動作しないことがあります。
不具合を回避するためには scala/bug#11634 のような設定を追加で行う必要があります。
sbt-native-image では生成の進捗が表示される
sbt-native-image は、バイナリを生成している間にネイティブイメージからの進捗を出力します。
sbt-native-packager は、何らかの理由でプロセスが完了しない限りがネイティブイメージからの出力を表示することはありません。
sbt-native-image は Docker をサポートしない
sbt-native-image に Docker サポートを追加する予定はありません。
sbt-native-packager は Docker をサポートしており、リンク環境をより細かく制御したい場合に便利です。
Play Frameworkには未対応
sbt-native-imageプラグインはPlay Frameworkには対応していません。
まだ若いプラグインなので当然ではありますね。
Scalapediaでは実際にPlay Frameworkのネイティブイメージを生成してみました。
起動してトップページを開くことまではできるものの、画像やJavaScriptが読み込まれませんでした。
まだ期待通りに表示できないレベルですので、これ以上のテストはしませんでした。
とはいえ画像やJavaScriptの無関係なAPIサーバとしての用途ではある程度挙動する可能性はあるので、テストしてみる価値はあるかもしれません。
nativeImage
コマンドを実行する際には、build.sbt
にてアプリケーションのエントリーポイントとしてplay.core.server.DevServerStart
またはplay.core.server.ProdServerStart
を指定すると、ネイティブイメージを生成することができます。