Kotlin + Quarkus のローカル開発用Docker環境を構築する
こんにちは、ナカエです。本日はサーバサイドKotlinの記事です。
Quarkus とは
Quarkusは "Kubernates native Java framework" なる売り文句の通り、K8sなどコンテナ上での動作に最適化したJavaのWebフレームワークです。 スポンサーはRed Hatで、Apache License 2.0の元で提供されています。
コンテナを利用する環境において従来のJavaのフレームワークが抱える問題となる起動時間の遅さやメモリ使用量の多さを、Quarkusは解決しようとしています。
JVM上で動かすだけではなく、GraalVMを用いてネイティブアプリとしてコンパイルすることでさらにメモリ使用量や起動時間を削減することもできます。
まだプレビュー段階ですがQuarkusはKotlinにも対応しており、サーバサイドKotlinにおいても有力なFWの選択肢の1つになるでしょう。
今回はKotlin+Quarkusのローカル開発用Docker環境を構築していきます。
検証に利用した環境とソフトウェアのバージョン
- OS: macOS Monterey 12.2.1
- CPU: Apple M1 Max
- Docker Desktop for Mac 4.6.1 ※gRPC-FUSEを利用(vertiofsはオフ)
- Quarkus 2.7.5
開発用のコンテナイメージを作る
Quakusは Quakus CLI というツールを提供していて、簡単にQuarkusのプロジェクトを作成したり、開発サーバを起動したり、ビルドを実行したりできます。 このQuarkus CLIを含むコンテナイメージを作るところから始めます。
適当な作業ディレクトリでDockerfileを作成します。
mkdir quarkus-dev
cd quakus-dev
vim Dockerfile
Dockerfile
FROM gradle:7.3.3-jdk11
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
zip \
unzip
ENV JBANG_DIR /usr/local/jbang
RUN curl -Ls https://sh.jbang.dev | bash -s - app setup
ENV PATH $PATH:/usr/local/jbang/bin
RUN jbang trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/ \
&& jbang app install --fresh --force quarkus@quarkusio
Quarkus CLIは SDKMAN!(Linux)、Homebrew(Mac)、Chocolatey(Windows)などのパッケージマネージャ経由の他、JBang を利用してもインストールできます。 JBangはJavaをスクリプトのように実行するツールです。 上記DockerfileではJBangをセットアップしてパスを通し、Quarkus CLIをインストールしています。
参考:Quarkus - Quarkus コマンドラインインターフェース (CLI) を使用し たQuarkus アプリの構築
また、QuarkusはApache Mavnをデフォルトのビルドツールとしていますが、Gradleにも対応しています。 今回はせっかくなので Kotlin DSLでビルドスクリプトを記述できる Gradleを選択することにしました。
保存したDockerfileを元にイメージをビルドします。
docker build -t quarkuscli ./
プロジェクトの作成
Quarkus - Kotlinの使用 の解説に従ってプロジェクトを作成します。
docker run --rm -v $(pwd):/work -w /work quarkuscli \
quarkus create app org.acme:rest-kotlin-quickstart --extension=kotlin,resteasy-reactive-jackson --gradle-kotlin-dsl
Docker Composeで開発環境を構築
あとはQuarkus CLIを使って開発サーバを起動できれば良いのですが、毎回docker runコマンドを実行するのは手間なので、Docker Composeを利用します。 開発サーバの起動にもQuarkus CLIを利用するため、先ほど作成したDockerfileをそのまま使います。
Dockerfileと同じディレクトリに docker-compose.yml を作成します。
docker-compose.yml
version: '3.8'
services:
app:
build: ./
ports:
- "8080:8080"
working_dir: /app
volumes:
- ./rest-kotlin-quickstart:/app
command: quarkus dev -Dquarkus.http.host=0.0.0.0
特に複雑なことはしておらず、コンテナ外からでもHTTPアクセスが許可されるように開発サーバのホストを0.0.0.0に設定している程度です。
docker-compose.ymlを保存してからコンテナを起動します。
docker-compose up
開発サーバが起動したらブラウザで http://localhost:8080/hello にアクセスします。
プレーンテキストのレスポンスが返ってきていますね。
ソースコードの書き換えとライブリロード
先程の http://localhost:8080/hello の処理を記述している箇所は下記になります。
rest-kotlin-quickstart/src/main/kotlin/org/acme/ReactiveGreetingResource.kt
package org.acme
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType
@Path("/hello")
class ReactiveGreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
fun hello() = "Hello RESTEasy Reactive"
}
こちらを書き換えて、Kotlinでの記述を試しつつ開発サーバのライブリロードが動作することを確認していきます。
rest-kotlin-quickstart/src/main/kotlin/org/acme/ReactiveGreetingResource.kt
package org.acme
import javax.enterprise.context.ApplicationScoped
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType
data class Greeting(val message: String = "")
@ApplicationScoped
class GreetingService {
fun greeting(name: String): Greeting {
return Greeting("Hello, $name")
}
}
@Path("/")
class ReactiveGreetingResource(
private val service: GreetingService
) {
@GET
@Path("/hello/{name}")
fun hello(@PathParam("name") name: String) = service.greeting(name)
}
変更点は
- データクラスを使ったJSONレスポンス
- パスパラメータの活用
- サービスクラスの依存注入
となります。
ソースコードを書き換えてから http://localhost:8080/hello/quakus にアクセスすると、JSONのレスポンスが表示されます。
docker compose up を実行したターミナルのログでライブリロードが機能していることも確認できました。
Live reload total time: 1.539s とのことで、十分に実用レベルですね。
まとめ
ホストマシンにJavaやその他ツールをインストールすることなく、Kotlin + Quakusのローカル開発用Docker環境を構築しました。 Quarkusを利用したソースコードがKotlinで簡潔に記述できること、及びコンテナ内で実行している場合でもライブリロードが動作することが確認できました。
サーバサイドKotlinといえばまだSpring Bootの採用率が高そうですが、Quarkusももっと流行ると良いですね。