MandrelでQuarkusのアプリをネイティブイメージ化する
はじめに
Quarkusのネイティブイメージ化したアプリを作ったことなかったのと、Mandrelという名前自体は聞いていたのですがQuarkusに関係するGraalVMぐらいの理解でしか無かったので、ちょっとまとめて動かしてみようかと思います。
基本的にはQuarkusのガイド(BUILDING A NATIVE EXECUTABLE)に従いつつやりますが、自分が気なったところを少しだけ深ぼってまとめるようにしようと思います。
Mandrelってなんぞ?
Oracle GraalVM Community Editionのダウンストリームに当たるGraalVMのディストリビューションの1つです。そのメインの目的としては、Quarkusのためにデザインされたネイティブイメージ化の方法を提供することにあります。基本的にアップストリームからの大きな変更はないようですが、Quakusのアプリに不要なものが取り除かれているようです。MandrelはOracle GraalVM CEと同じネイティブイメージ化の能力を提供しますが、polyglotなどのサポートが取り除かれているようです。Mandrelは現在、Linuxコンテナの環境におけるネイティブイメージのビルドだけの利用をが推奨されており、WindowsやMacOSに対するネイティブイメージを作成する場合は、 Oracle GraalVMを利用することが推奨されるようです。
理解が足りてない部分があるかもですが、おそらくCI上でのビルドやDockerのマルチステージビルドなどでMandrelを使えば諸々のコストの削減になるのでは無いかと思われます。
ネイティブイメージ化してみる
環境
今回の動作環境は以下のとおりです。
$ java --version openjdk 11.0.10 2021-01-19 OpenJDK Runtime Environment 18.9 (build 11.0.10+9) OpenJDK 64-Bit Server VM 18.9 (build 11.0.10+9, mixed mode) $ uname -srvmpio Linux 5.4.0-66-generic #74-Ubuntu SMP Wed Jan 27 22:54:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.2 LTS Release: 20.04 Codename: focal
Mandrelに関してはこのブログ内でインストールします。
Mandrelをインストールする
ダウンロードはGitHubのここのページからおこなうことが可能です。
具体的には以下の手順でセットアップをおこなします。
$ cd ${YOUR_GRAALVM_INSTALL_DIR} $ wget https://github.com/graalvm/mandrel/releases/download/mandrel-21.0.0.0-Final/mandrel-java11-linux-amd64-21.0.0.0-Final.tar.gz $ tar -xf mandrel-java11-linux-amd64-21.0.0.0-Final.tar.gz $ export JAVA_HOME="$( pwd )/mandrel-java11-21.0.0.0-Final" $ export GRAALVM_HOME="${JAVA_HOME}" $ export PATH="${JAVA_HOME}/bin:${PATH}"
QuarkusアプリのネイティブイメージのビルドはQuarkusのMavenラッパーを使って行いますが、その際に自身で指定するGraalVMでのビルドを行いたい場合はPathがとおっているGraalVMに対してnative-image
コマンドがインストールされている必要があります。
通常、native-image
コマンドはgu
等を使ってインストールしないと行けなかった気がしますが、Mandrelの場合最初から内包されているようです。
Nativeイメージ化するプロジェクトの作成
Quarkus - Start coding with code.quarkus.ioを使って、
こんな感じの設定でプロジェクトを作成します。
この際にExample CodeはYes, Please
を選択肢します(自分でコード書いても良いのですが、ちょいめんどくいさいので)。
Exampleのコードを生成を有効にしたので、GreetingResource.java
というハンドラーのコードが生成されているはずです。起動して、/hello-resteasy
のパスにアクセスするとHello RESTEasy
という文字列が返ってきます。
$ ./mvnw compile quarkus:dev # 別ターミナルで $ curl localhost:8080/hello-resteasy Hello RESTEasy
作成されたプロジェクトのPomをちょっと見てみる
作成されたプロジェクトのPomを見てみると以下のようなプロファイルの設定が記述されているのが確認できます。
<profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> <build> <plugins> <plugin> <artifactId>maven-failsafe-plugin</artifactId> <version>${surefire-plugin.version}</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> <configuration> <systemPropertyVariables> <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path> <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> <maven.home>${maven.home}</maven.home> </systemPropertyVariables> </configuration> </execution> </executions> </plugin> </plugins> </build> <properties> <quarkus.package.type>native</quarkus.package.type> </properties> </profile>
Maven Failsafe Pluginを用いたインテグレーションテストの設定が記述されています(詳しくは後述)。
ネイティブイメージをビルドする
Quarkusアプリをネイティブイメージでビルドする場合は以下のコマンドを用いて行います。
$ cd ${YOUR_PROJECT_DIR} $ ./mvnw package -Pnative
この際にPathがとおっているGraalVMにnative-image
コマンドが無かった場合は、以下のようなログを出力し、Dockerイメージをプルしてきてビルドを行ってくれます。
[WARNING] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Cannot find the `native-image` in the GRAALVM_HOME, JAVA_HOME and System PATH. Install it using `gu install native-image` Attempting to fall back to container build.
ビルドが完了するとデフォルトではtarget
配下に実行可能なバイナリが${project.artifactId}-${project.version}-runner
の名前でできています。
今回作成されたバイナリは以下のように実行することができます。
$ ./target/native-image-1.0.0-SNAPSHOT-runner __ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ --\___\_\____/_/ |_/_/|_/_/|_|\____/___/ 2021-03-10 21:12:31,788 INFO [io.quarkus] (main) native-image 1.0.0-SNAPSHOT native (powered by Quarkus 1.12.1.Final) started in 0.030s. Listening on: http://0.0.0.0:8080 2021-03-10 21:12:31,789 INFO [io.quarkus] (main) Profile prod activated. 2021-03-10 21:12:31,789 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
ネイティブイメージを使ったテストを実行する
ネイティブイメージ化すると、jarでビルドする際と比較して予想しない問題が起こることがありえます。そのため、ネイティブイメージで動くアプリのインテグレーションテストを行なって置くことが推奨されます。
今回は、Exampleコードの生成を有効化しているためデフォルトで以下のようなNativeGreetingResourceIT
というクラスが生成されていると思います。
import io.quarkus.test.junit.NativeImageTest; @NativeImageTest public class NativeGreetingResourceIT extends { // Execute the same tests but in native mode. }
@NativeImageTest
を付与されたテストクラスにインテグレーションテストを記述しておくと、前述した、PomのMaven Failsafe Plugin
の設定で指定されるネイティブイメージを利用したテストを実施することが可能です。
ExampleではGreetingResourceTest
を拡張しており、このクラスはGreetingResourceをテストがRest Assuredで行われています。
このテストを実行されるには以下のコマンドを実行します。
$ ./mvnw verify -Pnative
このコマンドを実行すると、ネイティブイメージのビルドが行われ、その後、そのイメージを使ったテストが実施されます。