Help us understand the problem. What is going on with this article?

【サーバーサイドKotlin】 KotlinにSpring Bootを適用してJibを使ってコンテナイメージ作成

はじめに

なぜServer Side Kotlin?

  • 言語自体の先進性、生産性の高さ(シンプルな構文、Nullable)
  • Java開発環境(特にIntelliJ)があれば、ほとんど障壁なく導入可能
  • Javaの資産流用、相互運用が非常に簡単
    • Javaで使っていたフレームワークがそのまま使えるなどJavaのスキルスタックを活用可能
  • GoogleがAndoroidアプリの公式開発言語とすることを発表(2017年)
    • →Android アプリ開発とのスキルセット共通化

参考:

Jibってなに?

Googleが公開しているJavaアプリケーションをcontainerizeするツールです。

特長として
- Dockerfile作成などコンテナ化のための開発が大幅に減る
- イメージのビルドにDockerが不要
- ベストプラクティスに沿ったコンテナイメージ

作成されるイメージのベースは、distolessというGoogleが公開するプロダクション向けのベースイメージが採用されています。

参考:

この記事で想定している環境

  • 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採用事例があるようですが、
導入も非常に簡単で、確かにとうなづける感覚はしました。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away