【QUARKUS】KotlinとGradleでサンプル作ってネイティブ起動するところまで

# はじめに
QUARKUS触ってみたいなーと前々から思っていたので、KotlinとGradleを使ったQUARKUSのサンプルを作ってみました
QUARKUSのドキュメントではmavenのほうが情報が厚そうだったのですが、pom.xml書きたくない、スクリプトで依存性管理したいと思ったのでGradleを選択しました
とりあえず今回はネイティブイメージをビルドするところまでです
また、自分はmacを使っているのでmacユーザー向けに書いていきます

作成したコードのリポジトリはこちらになります
https://github.com/yoshi10321/sample-quarkus-app

# GraalVMのインストール
まずGraalVMをインストールします
https://github.com/oracle/graal/releases
macの場合
`graalvm-ce-darwin-amd64-{バージョン名}.tar.gz`
というファイルをダンロードして解凍します

解凍したディレクトリを
`/Library/Java/JavaVirtualMachines`
に移動しておきます

`/usr/libexec/java_home -V`
コマンドを叩くとインストールしてあるJavaのバージョンが見れます
GraalVMがあればOK

環境変数にセットしてGraalVMを使うようにします

```
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=$JAVA_HOME/bin:$PATH
```

次にネイティブイメージ作成用に`native-image`ツールをインストールしておきます

```
gu install native-image
```

ここまでやったらGraalVMのインストールは完了です

# Gradleプロジェクトの作成
mavenだと便利なQUAKUSプロジェクト作成用のコマンドが用意されているようですが、現状Gradleではまだそういった便利なものは用意されてなかったので自分で作っていきます

まず好きなディレクトリを作成してそこに移動しておきます

```
mkdir sample-quarkus-app
cd sample-quarkus-app
```

下記のコマンドでgradleのkotlinプロジェクトが作成できます

```
gradle init --type=kotlin-application
```

# QUARKUSの設定を追加していく

まずQUARKUSのGradle pluginがまだGradle Plugin Portalに上がってないから`./settings.gradle.kts`に下記のコードを追加します

```kotlin
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "io.quarkus") {
                useModule("io.quarkus:quarkus-gradle-plugin:${requested.version}")
            }
        }
    }
}
```

次に`build.gradle.kts`の中身はこんな感じになりました

```kotlin:build.gradle.kts

import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet

plugins {
    // Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
    id("org.jetbrains.kotlin.jvm").version("1.3.31")
    id("org.jetbrains.kotlin.plugin.allopen") version "1.3.41"
    java
    id("io.quarkus") version "0.19.1"
}

allOpen {
    annotation("javax.ws.rs.Path")
    annotation("javax.enterprise.context.ApplicationScoped")
    annotation("sample.quarkus.app.MyAnnotation")
}

repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation(enforcedPlatform("io.quarkus:quarkus-bom:0.19.1"))
    implementation("io.quarkus:quarkus-resteasy")
    implementation("io.quarkus:quarkus-resteasy-jsonb")
    implementation("io.quarkus:quarkus-kotlin")
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
    testImplementation("io.quarkus:quarkus-junit5")
    testImplementation("io.rest-assured:rest-assured:3.1.0")
}

quarkus {
    setSourceDir(project.projectDir.resolve("src/main/kotlin").absolutePath)
    setOutputDirectory(project.projectDir.resolve("build/classes/kotlin/main").absolutePath)
}

kotlin.sourceSets["test"].kotlin.srcDirs("test")

```

build.gradleの設定で自分が少しハマったところを書いておくと

- `sourceDir`を設定しないと`./gradlew quarkusDev`で起動した時にエラーが発生します
- `implementation("io.quarkus:quarkus-resteasy-jsonb")`  
これを入れないと

```kotlin
@GET
@Produces(MediaType.APPLICATION_JSON)
fun hello() = Greeting("hello")
```
このコードのようなjsonフォーマットでレスポンスを返すときのjsonシリアライズでコケました

- `implementation("io.quarkus:quarkus-kotlin")`  
これを入れないとkotlinで書いたコードのホットリロードがうまく動かなかったです
- kotlinのクラスはデフォルトで`final`クラスになってしまうのでallopenプラグインを使ってopenにします。ただなぜかApplicationScopedアノテーションを付けたクラスでうまく効いてくれなかったので、別途手動でopenをつけたりもしました。（原因調べきれてないです、すいません）


下記のコマンドで起動することで、ホットリロードが有効な状態で起動できます

```
./gradlew quarkusDev
```

ブラウザで`localhost:8080/hello`にアクセスするとjsonのレスポンスが返ってくるのが確認できます

Kotlinのコードはgithubの方を参照していただければと思います
https://github.com/yoshi10321/sample-quarkus-app

# ネイティブイメージのビルド
環境変数に`GRAALVM_HOME`をセットしておく必要があります
macの場合は下記のように設定します

```
export GRAALVM_HOME=$HOME/Development/graalvm/Contents/Home/
```

docker向けにネイティブビルドする場合は下記のコマンドを叩きます

```
./gradlew buildNative --docker-build=true
```
このとき`quarkusBuild`タスクの実行中に失敗するかもしれません
自分は`src/main/resources`の中に設定のpropertiesファイルが無かったのが原因でした
同様の現象が発生した場合はgradleのquarkusタスクにある`generateConfig`を実行すると設定ファイルが作成されます
作成後に再度buildNativeをすればうまくいきました

実際にDockerにデプロイしていきます
Dockerfileを作成して

```docker:Dockerfile
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY build/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
```

下記のコマンドでDocker build&runしていきます

```
docker build -f Dockerfile -t sample/quarkus-app .
docker run -i --rm -p 8080:8080 sample/quarkus-app
```

```:ログ
-> % docker run -i --rm -p 8080:8080 sample/quarkus-app
2019-07-21 16:50:38,190 INFO  [io.quarkus] (main) Quarkus 0.19.1 started in 0.013s. Listening on: http://0.0.0.0:8080
2019-07-21 16:50:38,191 INFO  [io.quarkus] (main) Installed features: [cdi, kotlin, resteasy, resteasy-jsonb]
```
0.013秒で起動したというログが確認できました。
Kotlinで書いたコードがこの速さで起動するのは感動ものです

# 終わりに
少しハマったところもありましたがQUARKUSのアプリケーションをKotlinとGradleを使ってネイティブ起動するところまでいけました
ネイティブコードで実行することでJVMの起動が遅い問題が回避できるため、Kubernetes上で起動させたときのオートスケーリング時のパラメータ設定にそこまで気を使わなくても良くなるんじゃないかなということを期待しています
今後も特にQUARKUS, GraalVM周りの情報には期待していきたいですね。