MicronautのGraalVM Native ImageはDocker使うより普通にビルドする方が良さそう

## 2019-04-17 追記

ブログ書くだけなのもあれだなと思って、こんな感じにしたらどうかな?ってIssueを書いた。

github.com

そしたら「1.1.0でnative image化が十分シンプルになったから、ビルド用のシェルスクリプトを削除したんだ」って教えてもらった。まぁ、確かにそうね。

なので、普通にビルドするってことでいいし、Dockerでやりたい場合でも、僕がこのブログ記事で書いてるようなので良さそう。かな。

それと、マルチステージビルドをしっかりやってるサンプルも教えてもらった。ふむふむー。

https://github.com/micronaut-projects/micronaut-gcp/blob/master/examples/hello-world-cloud-run-graal/Dockerfile

追記はここまでです!

## Native Image

MicronautにはGraalVMのサポートが入っていて、GraalVMを使ってMicronautアプリのNative Imageを生成することができる。

ドキュメントはこの辺: https://docs.micronaut.io/1.1.0/guide/index.html#graal

Micronautのバージョンは1.1.0。

## graal-native-image feature

Micronautのcreate-appをするときに、graal-native-imageをfeatureオプションにつける。

❯ mn create-app hello-graal --features graal-native-image
| Generating Java project...
| Application created at /home/bufferings/workspace/micronaut/hello-graal

と、プロジェクトの生成と同時に、GraalVMでNative Imageをビルドするときのためのファイルがいくつか生成される。

## Dockerでのビルドがいまいち?

その作成されるファイルの中にDockerfileとdocker-build.shがあって「docker-build.shを実行すればDockerのマルチステージビルドで、Native Imageがビルドされるよ!」ってドキュメントには書いてある。

んだけど、いまいちかなー。と思ったのでメモを書いとく。

## docker-build.sh

は、こうなってる

#!/bin/sh
docker build . -t hello-graal
echo
echo
echo "To run the docker container execute:"
echo "    $ docker run --network host hello-graal"

つまり、Dockerfileでビルドしてるよってだけね。じゃあDockerfileを見てみよう。

## Dockerfile

こうなってる。マルチステージビルドを使ってる。

FROM oracle/graalvm-ce:1.0.0-rc15 as graalvm
COPY . /home/app/hello-graal
WORKDIR /home/app/hello-graal
RUN native-image --no-server -cp build/libs/hello-graal-*.jar

FROM frolvlad/alpine-glibc
EXPOSE 8080
COPY --from=graalvm /home/app/hello-graal .
ENTRYPOINT ["./hello-graal"]

### まず前半の4行は

  1. graalvmのイメージを使って
  2. カレントディレクトリーを /home/app/hello-graal にコピーして
  3. そこをWORKDIRにして
  4. native-image コマンドを実行してNative Imageをビルドしてる

んー。これだと、GradleによるJARファイルのビルドを事前に実行しておかないといけない・・・よね。もしDockerだけで完結させたいならこうかな?

RUN ./gradlew assemble \
 && native-image --no-server -cp build/libs/hello-graal-*.jar

ただ、これだとビルドのたびにGradleをダウンロードしてきてビルドするから時間がかかるよね。

### 次に後半の4行は

この部分だけ

COPY --from=graalvm /home/app/hello-graal .

これ、前半でビルドしたイメージから、必要なファイルをコピーしてきてるんだけど、ソースコードまで要らんくない?しかもルートディレクトリーにガッと突っ込んでる。そうじゃなくて、

COPY --from=graalvm /home/app/hello-graal/hello-graal .

Native Imageだけ取ってこれたらいいかなと。あと、これは僕の好みだけなんだけど、ルート直下に置くよりは、なんかディレクトリーいっこ切っておきたいな。

FROM frolvlad/alpine-glibc
EXPOSE 8080
WORKDIR /app
COPY --from=graalvm /home/app/hello-graal/hello-graal .
ENTRYPOINT ["/app/hello-graal"]

### ということで

こうなった

FROM oracle/graalvm-ce:1.0.0-rc15 as graalvm
COPY . /home/app/hello-graal
WORKDIR /home/app/hello-graal
RUN ./gradlew assemble \
 && native-image --no-server -cp build/libs/hello-graal-*.jar

FROM frolvlad/alpine-glibc
EXPOSE 8080
WORKDIR /app
COPY --from=graalvm /home/app/hello-graal/hello-graal .
ENTRYPOINT ["/app/hello-graal"]

### 実行するには

host networkモードを使うんだ!ってドキュメントに書いてある。

$ docker run --network=host hello-world

僕のマシンはUbuntuだからいいんだけど、Docker for Macだと現時点ではサポートされてないよね。。。

https://docs.docker.com/network/host/

素直にpublishオプションを使っていいんじゃないかな。

$ docker run -p 8080:8080 hello-world

## そこまでしてDocker使わなくても・・・

いいかなと思う。さっきも書いたけどDocker使うとgradlewの実行遅いし。だから、素直にホストマシンにsdkmanでGraalVM使ってやるといいのかなと。思うのであった。

❯ sdk use java 1.0.0-rc-15-grl

Using java version 1.0.0-rc-15-grl in this shell.

❯ ./gradlew assemble
Starting a Gradle Daemon, 2 incompatible Daemons could not be reused, use --status for details

BUILD SUCCESSFUL in 11s
10 actionable tasks: 7 executed, 3 up-to-date
<-------------> 0% WAITING
> IDLE

❯ native-image --no-server -cp build/libs/hello-graal-*.jar
[hello-graal:31214]    classlist:   6,843.33 ms
[hello-graal:31214]        (cap):   1,346.60 ms
[hello-graal:31214]        setup:   3,216.68 ms
[hello-graal:31214]   (typeflow):  52,258.57 ms
[hello-graal:31214]    (objects):  38,803.47 ms
[hello-graal:31214]   (features):   2,844.33 ms
[hello-graal:31214]     analysis:  97,553.99 ms
[hello-graal:31214]     universe:   2,264.42 ms
[hello-graal:31214]      (parse):   7,780.03 ms
[hello-graal:31214]     (inline):  10,103.81 ms
[hello-graal:31214]    (compile):  85,401.85 ms
[hello-graal:31214]      compile: 106,246.90 ms
[hello-graal:31214]        image:   5,274.68 ms
[hello-graal:31214]        write:   1,565.77 ms
[hello-graal:31214]      [total]: 223,483.88 ms

❯ ./hello-graal
23:31:52.391 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 169ms. Server Running: http://localhost:8080
^C

❯ ./hello-graal
23:32:00.775 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 64ms. Server Running: http://localhost:8080

お。何も設定してないから速いってのもあるけど200msとか100msとかで起動するのは良いなー。

ということで、もうちょっとMicronautとGraalVMのNative Image周りを勉強してみようと思う。