はじめに
なぜServer Side Kotlin?
- 言語自体の先進性、生産性の高さ(シンプルな構文、Nullable)
- Java開発環境(特にIntelliJ)があれば、ほとんど障壁なく導入可能
- Javaの資産流用、相互運用が非常に簡単
- Javaで使っていたフレームワークがそのまま使えるなどJavaのスキルスタックを活用可能
- GoogleがAndoroidアプリの公式開発言語とすることを発表(2017年)
- →Android アプリ開発とのスキルセット共通化
参考:
Jibってなに?
Googleが公開しているJavaアプリケーションをcontainerizeするツールです。
特長として
- Dockerfile作成などコンテナ化のための開発が大幅に減る
- イメージのビルドにDockerが不要
- ベストプラクティスに沿ったコンテナイメージ
作成されるイメージのベースは、distolessというGoogleが公開するプロダクション向けのベースイメージが採用されています。
参考:
-
distrolessイメージを使って、ランタイムDockerイメージを作ってみる
昨年私がまとめた内容です。(この時から比べるとjavaとccのイメージが正式にプロダクション向けになりました)
この記事で想定している環境
- Mac OS
- IntelliJ IDEA (2019.1)
- Kotlin plugin (v1.3.31)
- openjdk 1.8.0_201
- Spring Boot 2.1.4
- Jib gradle plugin 1.2.0
- Docker (18.09.2)
※今回の記事ではローカルのDockerイメージとして登録したかったのでDockerインストールしていますが、リモートコンテナレポジトリに登録する場合は不要なはず
手順
[前準備] 1. Kotlin × Spring Bootのプロジェクト作成
Spring Bootを利用したKotlinプロジェクトを作成します。
Spring InitialzrがKotlin対応しているので、極めて簡単に雛形プロジェクトが手に入ります。
https://start.spring.io/
にアクセスして、
- Gradle
- Kotlin
- Spring Boot 2.1.4
を選択します。
Group名、Artifact名は各自の値を入力します。
今回は、以下としました。
- Grouop: com.example
- Artifact: kotlindemo
「Generate Project」を押すと、Zipがダウンロードされるので
解凍して、IntelliJにImportします。
これで、KotlinベースのSpring Boot Applicationのプロジェクトが完成しました。
[前準備] 2. Controllerを追加
この状態では、APIが1つも存在していないので追加します。
今回はサンプルのレスポンスを返却するControllerを追加してみます。
まずは、REST関係のアノテーションを利用したいので、
spring-boot-starter-web
を依存ライブラリに追加しておきます。
--- a/build.gradle
+++ b/build.gradle
@@ -20,6 +20,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
+ implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
依存を追加したので、ビルドしておきます。(Command + F9)
次に、サンプルのレスポンスを返却するControllerを追加します。
SampleController.kt
package com.example.kotlindemo
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.GetMapping
@RestController
class SampleController {
@GetMapping("sample")
fun sample(): HashMap<String, String> {
val sample = HashMap<String, String>()
sample.put("name", "test_name")
sample.put("id", "test")
return sample
}
}
利用するアノテーションはJavaで実装した時と同じですね。
これで /sample というREST APIが完成したはずです。
IntelliJでデバッグして確かめてみます。
私の場合は、kotlindemo
というArtifact名にしたので、
「KotlindemoApplication」という名前で、デバッグ構成が登録されていました。
これを実行して、curlコマンドで/sample APIを確認します。
curl localhost:8080/sample
実行結果:
{"name":"test_name","id":"test"}
サンプルREST APIの完成です。
[本題] 3. Jibの追加、コンテナイメージ作成
build.gradleにJibプラグインを追加します。
ついでに設定も追加します。
--- a/build.gradle
+++ b/build.gradle
@@ -4,6 +4,7 @@ plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.2.71'
id 'org.jetbrains.kotlin.plugin.spring' version '1.2.71'
+ id 'com.google.cloud.tools.jib' version '1.2.0'
}
apply plugin: 'io.spring.dependency-management'
@@ -12,6 +13,9 @@ group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
+// jib configuration
+jib.container.useCurrentTimestamp = true // jibで作成されるイメージの作成時間を現在時刻とする
+
repositories {
mavenCentral()
}
プロジェクトをビルドし、Jibをインストールします。(Autoビルドにしておけばよかった。。)
下記のコマンドを実行して、イメージ作成&ローカルのDockerイメージとして登録します。
./gradlew jibDockerBuild
なお、リモートレポジトリにプッシュしたい場合は、
https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#quickstart
ここら辺をみて設定するようです。
では最後にイメージを起動して確認してみます。
docker run --rm -p 8080:8080 kotlindemo:0.0.1-SNAPSHOT
コンテナのログ:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.4.RELEASE)
2019-05-15 02:26:04.665 INFO 1 --- [ main] c.e.kotlindemo.KotlindemoApplicationKt : Starting KotlindemoApplicationKt on c6df41f5d4a3 with PID 1 (/app/classes started by root in /)
2019-05-15 02:26:04.671 INFO 1 --- [ main] c.e.kotlindemo.KotlindemoApplicationKt : No active profile set, falling back to default profiles: default
2019-05-15 02:26:05.154 WARN 1 --- [kground-preinit] o.s.h.c.j.Jackson2ObjectMapperBuilder : For Jackson Kotlin classes support please add "com.fasterxml.jackson.module:jackson-module-kotlin" to the classpath
2019-05-15 02:26:06.928 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-05-15 02:26:06.998 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-05-15 02:26:06.998 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.17]
2019-05-15 02:26:07.244 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-05-15 02:26:07.244 INFO 1 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2494 ms
2019-05-15 02:26:08.528 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-05-15 02:26:08.965 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-05-15 02:26:08.973 INFO 1 --- [ main] c.e.kotlindemo.KotlindemoApplicationKt : Started KotlindemoApplicationKt in 4.958 seconds (JVM running for 5.682)
2019-05-15 02:26:40.980 INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-05-15 02:26:40.980 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-05-15 02:26:40.993 INFO 1 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 13 ms
起動成功!、curlも通るようになっていました。
終わりに
KotlinプロジェクトにSpring Bootを適用して、REST APIのアプリケーションを作成、
そのアプリケーションのコンテナイメージ化にJibを利用するやり方を試してみました。
国内でもサーバーサイドのKotlin採用事例があるようですが、
導入も非常に簡単で、確かにとうなづける感覚はしました。