Google App EngineのJava 11アプリをGradleでビルドする（Spring Boot + Kotlin + Gradle Kotlin DSL）

*姉妹記事: [Ktor版](https://qiita.com/orangain/items/5acbdc4a7a0e3d14e0ae)*

この記事では、Google App EngineのJava 11ランタイムを使うアプリケーションをGradleでビルド・デプロイする方法を解説します。

この構成は意外と正確な情報がありません。App Engine [Java 11ランタイムのサンプルコード](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java11)はMavenが使われています。[Gradleを使うドキュメント](https://cloud.google.com/appengine/docs/standard/java11/using-gradle)もあるものの、プラグインのバージョンが古かったり、不要なはずのwarの設定があったりします。

さらにGradle Kotlin DSLを使う場合の詰まりどころもあるので、Spring Boot + Kotlin + Gradle Kotlin DSLのGradleプロジェクトをデプロイするまでをまとめておきます。

## 前提

### 環境

* macOS: 10.14.6
* Spring Boot: 2.1.8
* Gradle: 5.6.2
* Kotlin: 1.2.71

### 古い情報で混乱しないために知っておくと良いこと

* Google App EngineのJava 11はStandard環境第2世代のランタイム。
  - 設定には `appengine-web.xml` ではなく `app.yaml` を使う。
  - Webアプリケーションコンテナではなく、jarとしてビルドしたものを実行するので、war関連の設定は不要。
* Gradle 4.0以降では、Google App Engine Gradle plugin 2.xを使う。

## 手順

### 1. Spring Initializrでプロジェクトをダウンロード

[Spring Initializr](https://start.spring.io/)を開き、次の設定でzipファイルをダウンロードします。

* Project: **Gradle Project**
* Language: **Kotlin**
* Spring Boot: **2.1.8**
* Project Metadata -> Options:
  * Packaging: **Jar**（デフォルト）
  * Java: **11**
* Dependencies: 「**Spring Web**」[^1]を検索して追加

なおLanguageでKotlinを選ぶと、Gradleの設定ファイルはKotlin DSL（`build.gradle.kts`）になります。

[^1]: 以前はSpring Web Starterという名前だったと記憶していますが、最近変わったようです。

### 2. Controllerを作成して動作確認

ダウンロードしたdemo.zipを展開して、 `src/main/kotlin/com/example/demo/HelloController.kt` を次の内容で作成します。

```kotlin:HelloController.kt
package com.example.demo

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloController {

    @RequestMapping("/")
    fun hello(): String {
        return "Hello, world!"
    }
}
```

次のコマンドで起動します。

```
./gradlew bootRun
```

`http://localhost:8080` にアクセスして、 `Hello, world!` と返ってくることを確認します。

### 3. Google App Engine Gradle pluginを追加

[Google App Engine Gradle plugin](https://github.com/GoogleCloudPlatform/app-gradle-plugin)を使うために、`build.gradle.kts` に次の設定を追加します。

```kotlin:build.gradle.kts
plugins {
    id("org.springframework.boot") version "2.1.8.RELEASE"
    id("io.spring.dependency-management") version "1.0.8.RELEASE"
    kotlin("jvm") version "1.2.71"
    kotlin("plugin.spring") version "1.2.71"
    id("com.google.cloud.tools.appengine") version "2.1.0" // 追加

}
```

これだけだとプラグインをロードできないので、 `settings.gradle.kts` に次の設定を追加します。この設定については、 https://stackoverflow.com/a/48510049 を参照してください[^2]。

[^2]: https://github.com/GoogleCloudPlatform/app-gradle-plugin#how-to-use や https://stackoverflow.com/a/55075378 のように`buildscript`と`apply`を使う方法もありますが、Kotlin DSLでは`appengine {...}`と書けなくなってしまい、代わりに `configure<AppEngineAppYamlExtension> {...}` と書く必要があります。

```kotlin:settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
    }
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "com.google.cloud.tools.appengine") {
                useModule("com.google.cloud.tools:appengine-gradle-plugin:${requested.version}")
            }
        }
    }
}
```

### 4. app.yamlを追加

`src/main/appengine/app.yaml` を次の内容で作成します。この設定は[サンプルコードのapp.yaml](https://github.com/GoogleCloudPlatform/java-docs-samples/blob/master/appengine-java11/springboot-helloworld/src/main/appengine/app.yaml)と同じにしてます。

```yaml:app.yaml
runtime: java11
instance_class: F2
env_variables:
  JAVA_TOOL_OPTIONS: "-XX:MaxRAM=256m -XX:ActiveProcessorCount=2 -Xmx32m"
```

次のコマンドで、デプロイ用の成果物を作成できることを確認します。

```
./gradlew appengineStage
```

次のように `build/staged-app` に `app.yaml` と jarファイルができていればOKです。

```
$ ls build/staged-app/
app.yaml		demo-0.0.1-SNAPSHOT.jar
```

### 5. Google App Engineにデプロイ

`build.gradle.kts` に次の設定を追加します。これらの設定は、プロジェクトIDをgcloudコマンドで設定済みのものにし、バージョンを自動生成するという意味なので、明示的に他の値にしても構いません。

設定については[Google App Engine Gradle pluginのUser Guide](https://github.com/GoogleCloudPlatform/app-gradle-plugin/blob/master/USER_GUIDE.md#deploy-1)を参照してください。

```kotlin:build.gradle.kts
appengine {
    deploy {
        projectId = "GCLOUD_CONFIG"
        version = "GCLOUD_CONFIG"
    }
}
```

次のコマンドでデプロイします。

```
./gradlew appengineDeploy
```

`https://<プロジェクトID>.appspot.com` にアクセスして、 `Hello, world!` と表示されたら成功です。

なお `gcloud` コマンドでデプロイしたい場合は、 `./gradlew appengineStage && cd build/staged-app/ && gcloud app deploy` とすればOKです。

## まとめ

以上でデプロイまで完了です。この記事の実行結果のコードは次のリポジトリにあります。
https://github.com/orangain/appengine-java11-gradle-spring-boot


## 参考文献

* [Using Gradle and the App Engine Plugin  |  App Engine standard environment for Java 11 docs  |  Google Cloud](https://cloud.google.com/appengine/docs/standard/java11/using-gradle)
  * 公式の解説だが、Gradleプラグインのバージョンが1.xで古い点と、不要なはずのwarの設定がある点は注意。
* [Spring Boot Application Google App Engine Standard with Java 11](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java11/springboot-helloworld)
  * Spring Boot + Java 11のサンプルコード。Java (not Kotlin) かつ Maven (not Gradle) である点がちょっと違う。
* [google app engine - How to configure AppEngine Gradle plugin using Kotlin DSL - Stack Overflow](https://stackoverflow.com/questions/48502220/how-to-configure-appengine-gradle-plugin-using-kotlin-dsl)
  * build.gradle.ktsでGoogle App Engine Gradle pluginを使う方法。
* [app-gradle-plugin/USER_GUIDE.md at master · GoogleCloudPlatform/app-gradle-plugin](https://github.com/GoogleCloudPlatform/app-gradle-plugin/blob/master/USER_GUIDE.md#app-engine-appyaml-based-projects)
  * App Engine Gradle Pluginのリファレンス。Java 11ランタイムの場合は「App Engine app.yaml based projects」を読むことに注意[^3]。

[^3]: https://cloud.google.com/appengine/docs/standard/java11/gradle-reference にもリファレンスがありますが、`projectId` が古い `project` のままになっていたして、やや古いようです。