巷で噂のMicronautをWSL2環境で動かしてみます。
はじめに
Micronaut(マイクロノート)とは、Grailsチームが新たに開発しているマイクロサービス向けのフルスタックなフレームワークです。
DIやAuto-Config、ServiceDiscovery対応といった機能に加え、起動の速さやフットプリントの軽減も狙いにしています。
Micronautの機能の1つにGraalVMを使ったネイティブビルドがあります。
まだ実験的な段階とドキュメントにありますが、今回は実際にJavaのWeb APIをネイティブビルドして動かすところまで確認します。
開発環境
WLS2をセットアップし、WLS2にUbuntu 18.04がインストールされている状態を前提にします。
バージョン | |
---|---|
Windows | Windows 10 Pro バージョン 1903 (OSビルド 19025.1) ※Insider Preview |
WSL | WSL2 |
Ubuntu on WSL | 18.04 |
WLS2の導入がまだの人は、以下の記事が参考になるかと思います。
※「UbuntuがWLS1からWSL2にならない!」という人は、末尾の参考情報をチェックしてみてください。
SDKMAN、Micronautのインストール
MicronautをインストールするためにSDK管理ツールの「SDKMAN」を使います。
まず、SDKMANのインストールに必要となるzipとunzipをインストールします。
パッケージ一覧の更新(apt update)を行ってからapt installしましょう。
$ sudo apt update && sudo apt install -y zip unzip
SDKMANをインストールします。
インストール直後にSDKMANを使うため、.bashrcを再読み込みしておきます。
$ curl -s "https://get.sdkman.io" | bash
$ . ~/.bashrc
SDKMANを使ってJDKとMicronautをインストールします。
今回インストールするMicronautには、2019/11/29現在の最新バージョンである1.2.6を指定します。
$ sdk install java 8.0.232.hs-adpt
$ . ~/.bashrc
$ java -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_232-b09)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.232-b09, mixed mode)
$ sdk install micronaut 1.2.6
$ mn --version
| Micronaut Version: 1.2.6
| JVM Version: 1.8.0_232
Micronautアプリの作成
アプリとコントローラーのスケルトンを作ります。
$ mn create-app hello --features graal-native-image
| Generating Java project...
| Application created at /home/my-user/hello
$ cd hello
$ mn create-controller hello
| Rendered template Controller.java to destination src/main/java/hello/HelloController.java
| Rendered template ControllerTest.java to destination src/test/java/hello/HelloControllerTest.java$ cd hello
作成されたHelloController.javaはlocalhost:8080/helloにアクセスするとステータスコード:200を返すだけなので、少し味付けしましょう。
以下のように書き換えます。
package hello;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.QueryValue;
import javax.annotation.Nullable;
@Controller("/hello")
public class HelloController {
@Get(produces = MediaType.TEXT_PLAIN)
public String index(@Nullable @QueryValue("name") String name) {
if (name == null) {
name = "World";
}
return "Hello " + name + "!!\n";
}
}
JARの作成と実行
ビルドしてFat Jarを作成します。
$ ./gradlew assemble
<ビルド実行の出力>
$ ls build/libs
hello-0.1-all.jar hello-0.1.jar
ネイティブビルドする前にJavaコマンドでJARを起動し、アプリが動くことを確認してみましょう。
$ java -jar build/libs/hello-0.1-all.jar
11:57:15.406 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 1032ms. Server Running: http://localhost:8080
ブラウザで http://localhost:8080/hello にアクセスして「Hello World!!」が表示されればOKです。
確認できたらCtrl-Cでアプリを止めましょう。
ネイティブビルドの方法
Micronautアプリをネイティブビルドして実行するには、以下の2つの方法があります。
- [A] Dockerを使ってネイティブビルドする・・・dockerのコンテナ内でネイティブビルドし、dockerコンテナ内で実行する。
- [B] Ubuntu上でネイティブビルドする・・・ Ubuntu上でネイティブビルドし、Ubuntu上で実行する。
まずは[A]を試してみましょう。
[A] Dockerを使ってネイティブビルドする
既にビルド用のスクリプトやDockerfileが生成されていますので、docker環境があればコマンド一発でネイティブビルドできます。
WLS2のUbuntu上にDockerをインストールしてもいいのですが、Docker Desktop for WindowsのEdge版には、「WSL 2 Backend」という便利なものができたようなので、今回はそれを使います。
以下のサイトからDocker Desktop for Windowsのインストーラーをダウンロードして実行します。
ログインしてEdge用インストーラーを取得します。
※画面右上のDownloadボタンではなく、Edgeチャネルのインストーラーを選びましょう。
表内にある「Get Docker Desktop for Windows(Edge)」ボタンです。
Docker Desktopを起動し、タスクバーのクジラアイコンを右クリックし、Settingを選択します。
以下の2箇所設定します。
- General の「Enable the experimental WSL 2 based engine」にチェックを入れる
- Resources > WSL INTEGRATION を選択し、使用しているWLS2上のUbuntuをONにする。
設定に困った場合は、こちらを記事を参考にどうぞ。
WSL2 で Docker Desktop for Windows (Edge) を利用する
さて、Ubuntuに戻り、dockerが使えるか確認しましょう。
こんな形でかえってくればOKです。
$ docker version
Client: Docker Engine - Community
Version: 19.03.5
API version: 1.40
Go version: go1.12.12
Git commit: 633a0ea838
Built: Wed Nov 13 07:29:52 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.5
API version: 1.40 (minimum version 1.12)
Go version: go1.12.12
Git commit: 633a0ea
Built: Wed Nov 13 07:29:19 2019
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.2.10
GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339
runc:
Version: 1.0.0-rc8+dev
GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
docker-init:
Version: 0.18.0
GitCommit: fec3683
では、ネイティブビルドを実行しましょう。
dockerイメージがダウンロードされた後、ネイティブビルドが実行されます。
私の環境ではネイティブビルドに4分10秒ほどかかりました。
以下のように返ってくればOKです。
$ ./docker-build.sh
<中略>
Step 5/9 : RUN native-image --no-server -cp build/libs/hello-*-all.jar
---> Running in c11ae367c6fc
[hello:27] classlist: 4,908.74 ms
[hello:27] (cap): 1,074.08 ms
[hello:27] setup: 2,537.79 ms
[hello:27] (typeflow): 26,959.79 ms
[hello:27] (objects): 31,552.87 ms
[hello:27] (features): 3,008.96 ms
[hello:27] analysis: 63,966.00 ms
[hello:27] (clinit): 946.54 ms
[hello:27] universe: 2,507.42 ms
[hello:27] (parse): 8,784.57 ms
[hello:27] (inline): 54,129.86 ms
[hello:27] (compile): 65,871.62 ms
[hello:27] compile: 131,680.54 ms
[hello:27] image: 5,230.87 ms
[hello:27] write: 38,152.82 ms
[hello:27] [total]: 249,286.14 ms
Removing intermediate container c11ae367c6fc
---> a7f2d020f676
Step 6/9 : FROM frolvlad/alpine-glibc
latest: Pulling from frolvlad/alpine-glibc
<中略>
Successfully built 86ea9098050c
Successfully tagged hello:latest
To run the docker container execute:
$ docker run -p 8080:8080 hello
最後に書かれているとおりに実行すれば、dockerコンテナ経由でアプリが起動します。
$ docker run -p 8080:8080 hello
03:02:56.348 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 61ms. Server Running: http://ee9cf971ac1f:8080
61ms?? 速っ!!
http://localhost:8080/hello にアクセスしてみてください。
「Hello World!!」が表示されますね?
確認ができたら止めましょう。
docker-build.shがCtl-Cで終了できないようなので、dockerコマンドで終了させましょう。
Ubuntuのターミナルをもう1つ起動して以下のようにします。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bd8d7d61c2e5 hello "./hello" 13 seconds ago Up 12 seconds 0.0.0.0:8080->8080/tcp gifted_swartz
$ docker kill bd8d
bd8d
※「bd8d」の部分はdocker psで表示されるCONTAINER IDの値に読み替えてください。
docker killすると、docker-build.sh側のコンソールのプロンプトが戻ってきたと思います。
[B] Ubuntu上でネイティブビルドする
せっかくなのでDockerを使わずにUbuntu上でネイティブビルドしてみましょう。
まず、Micronautのネイティブビルドに必要なパッケージを入れます。
$ sudo apt install -y build-essential zlib1g-dev
SDKMANを使ってGraalVMをインストールします。
※バージョンは要注意です。v19.0.2のGraalVMだとMicronault v1.2.6のネイティブビルドが失敗します。
$ yes y | sdk install java 19.3.0.r8-grl
$ java -version
penjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-20191008104205.buildslave.jdk8u-src-tar--b07)
OpenJDK 64-Bit GraalVM CE 19.3.0 (build 25.232-b07-jvmci-19.3-b05, mixed mode)
※デフォルトのJDKにするか聞かれるので、yesコマンドを使ってyを入力しています。
続いてGraalVMのnative-imageツールをインストールします。
$ gu install native-image
念のため、JARをビルドしなおしておきましょう。
$ ./gradlew clean assemble
準備完了!! いざ、ネイティブビルド実行!!
$ native-image --no-server -cp build/libs/hello-0.1-all.jar
[hello:7781] classlist: 8,141.21 ms
[hello:7781] (cap): 1,692.15 ms
[hello:7781] setup: 3,379.58 ms
[hello:7781] (typeflow): 27,399.41 ms
[hello:7781] (objects): 22,304.20 ms
[hello:7781] (features): 3,106.65 ms
[hello:7781] analysis: 55,127.37 ms
[hello:7781] (clinit): 1,235.15 ms
[hello:7781] universe: 2,834.11 ms
[hello:7781] (parse): 4,189.54 ms
[hello:7781] (inline): 9,390.51 ms
[hello:7781] (compile): 53,099.58 ms
[hello:7781] compile: 69,783.42 ms
[hello:7781] image: 4,836.02 ms
[hello:7781] write: 840.42 ms
[hello:7781] [total]: 145,407.37 ms
2分半ほどかかりました。
起動直下のディレクトリに「hello」というファイルが出来ているはずです。
これが生成された実行用バイナリファイルです。
あとはこの生成されたバイナリを実行するとアプリが起動します。
$ ./hello
12:28:34.717 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 86ms. Server Running: http://localhost:8080
爆速で起動できたことが確認できました。
ブラウザで http://localhost:8080/hello にアクセスすると「Hello World!!」が表示されますね?
さいごに
ということで、ユーザーガイドを少しだけアレンジしてネイティブビルドを試してみました。
GraalVMを利用したネイティブビルド自体が、まだexperimentalな段階ですが、発想は面白いですよね。
では、今回はこの辺で。
【参考】UbuntuがWSL1からWSL2にならない場合
wsl --set-version Ubuntu-18.04 2
を実行しても、VERSIONが2になってくれない場合、
もしくは、
wsl --set-default-version 2
を実行した状態で、Ubuntuのインストール後の初回起動が
WslRegisterDistribution failed with error: 0x80370102
のエラーになる場合は、Hyper-Vの自動起動がOFFになっているかもしれません。
以下の記事を参考にHyper-Vの自動起動のステータスを確認してみてください。
(bcdeditでhhypervisorlaunchtypeがoffになっていたら、autoにしてWindowsを再起動し、再実行してみましょう)
※おそらくVirtualBoxをインストールしたときの副作用だと思いますが、かなりハマりました。。。
参考になれば幸いです。