SpringBootアプリをGAE/FE(Custom Runtime)にデプロイする

# はじめに
素のSpringBootアプリを [GAE/FE(Custom Runtime)](https://cloud.google.com/appengine/docs/flexible/custom-runtimes/) へデプロイするまでを軽くまとめます。
本来は Java Runtime を使えばいいんですがそこは個人的な事情（？）がありまして。

# 事前準備
- [適当なGCPプロジェクト](https://console.cloud.google.com/getting-started?hl=ja)
- JDK
- Maven
- [Cloud SDK](https://cloud.google.com/sdk/docs/?hl=ja) ( ひとまずインストールまで )
- [適当な素のSpringBootサンプル](https://github.com/tnagao3000/deploy-springboot-webapp-to-gae-fe-custom-using-cloud-build-and-kms/tree/springboot-webapp)

プロジェクト ～ Cloud SDK まではリンク先などを参考に用意してください。
今回プロジェクト名(ID)は `springboot-webapp-project` にしました。

SpringBootサンプルソースは上記リンク先にあります。
GAEにデプロイするための前提条件は8080番をLISTENしてることぐらい。

試しにローカルで動かしてみると次のような感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/e88c80df-1e70-51a1-b2a7-a6eb4df07e4d.png)

# ソースを修正する
GAEにデプロイできるようにするため、素のソースにGAE向け設定やデプロイ用プラグインを追加します。
具体的には次の３点です。

- app.yaml を追加
- Dockerfile を追加　※ Java Runtime にデプロイするなら要らない
- pom.xml にプラグインを追記

なお修正済みの [ソースはこちら](https://github.com/tnagao3000/deploy-springboot-webapp-to-gae-fe-custom-using-cloud-build-and-kms/tree/deploy-springboot-webapp-to-gae-fe-custom) にあります。

### app.yaml を追加
[公式を参考](https://cloud.google.com/appengine/docs/flexible/java/reference/app-yaml?hl=ja) に app.yaml を作成して `src\main\appengine\` に設置します。
これには使用するGAEの種類やその設定を記載します。

今回作成した内容は次のような感じ。

```yaml:app.yaml
runtime: custom
env: flex
api_version: 1

handlers:
  - url: .*
    script: dynamic

env_variables:
  SPRING_PROFILES_ACTIVE: 'prod'

resources:
  cpu: 1
  memory_gb: 2
```
設定値の詳細については公式を参照してください。

### Dockerfile を追加
GAE Custom Runtime は開発者が作成した Dockerimage を動かしてくれます。
開発者側は イメージの元になる [Dockerfile を用意して](https://cloud.google.com/appengine/docs/flexible/custom-runtimes/build) `src\main\docker\` に設置します。

今回作成した内容は次のような感じ。

```docker:Dockerfile
FROM gcr.io/google-appengine/openjdk:8
EXPOSE 8080

RUN /bin/cp -f /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN groupadd ore_group && useradd -g ore_group wai_user
USER wai_user

ENV JAVA_OPTS=""
ADD springboot-webapp-*.jar app.jar

ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
```

### pom.xml にプラグインを追記
pom.xml に [GAEにデプロイしてくれるプラグイン](https://cloud.google.com/appengine/docs/standard/java/tools/using-maven?hl=ja#adding_the_app_engine_maven_plugin_to_an_existing_project_optional) を追記します。
このプラグイン導入して [`mvn appengine:deploy`](https://cloud.google.com/appengine/docs/standard/java/tools/using-maven?hl=ja#deploying_your_application) を実行すると
[gcloudコマンド](https://cloud.google.com/sdk/gcloud/?hl=ja) が叩かれデプロイが行われます。

このプラグインは頻繁にアップデートされているのか（？）たまに見ると
書き方が変わっている気がするのですが現在は次のような感じでいいようです。

```xml:pom.xml(追記個所を抜粋)
<!-- mvn clean package appengine:deploy -->
<plugin>
    <groupId>com.google.cloud.tools</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>2.0.0</version>
    <configuration>
        <deploy.projectId>GCLOUD_CONFIG</deploy.projectId>
        <deploy.version>GCLOUD_CONFIG</deploy.version>
    </configuration>
</plugin>
```
プラグインについてより詳しくは [こことか](https://github.com/GoogleCloudPlatform/app-maven-plugin/blob/master/USER_GUIDE.md) [こことか](https://cloud.google.com/appengine/docs/standard/java/tools/maven-reference) にあるようです。
pom.xml の [全体はこちら](https://github.com/tnagao3000/deploy-springboot-webapp-to-gae-fe-custom-using-cloud-build-and-kms/blob/deploy-springboot-webapp-to-gae-fe-custom/pom.xml)。

以上でソース修正は完了です。

# Cloud SDK を初期化する
gcloudコマンドはインストール後、実際に使用可能にするまでに次の２つの作業が必要です。

- configをいい感じに設定する
- 認証する

上記は [`gcloud init`](https://cloud.google.com/sdk/gcloud/reference/init?hl=ja) で行うか、または [`gcloud config set`](https://cloud.google.com/sdk/gcloud/reference/config/set?hl=ja) ＋ [`gcloud auth login`](https://cloud.google.com/sdk/gcloud/reference/auth/login?hl=ja) で行います。

### configをいい感じに設定する
config には具体的には `account` と `project` （[ここの AVAILABLE PROPERTIES](https://cloud.google.com/sdk/gcloud/reference/config/set?hl=ja) 参照）の
２つを設定すれば取り合えずOKです。

CUIで行います。
コマンドは次のような感じ。

```
# まずはconfig値を確認してみる
> gcloud config list
[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = wai@gmail.com
disable_usage_reporting = False
project = ore-no-project

Your active configuration is: [default]

# 今回使用するaccountを設定
> gcloud config set account xxxxx@gmail.com
Updated property [core/account].

# 今回使用するprojectを設定
> gcloud config set project springboot-webapp-project
Updated property [core/project].

# config値を再確認
> gcloud config list
[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = xxxxx@gmail.com
disable_usage_reporting = False
project = springboot-webapp-project

Your active configuration is: [default]
```
以上で config がいい感じになりました。

### 認証する
config に指定した `account` で GCP を操作できるようにするため認証を行います。

```
# 次のコマンドを叩くとブラウザが開いて認証画面が出てくる。（出なければURLをブラウザにコピペ）
> gcloud auth login
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?redirect_uri=http%3A%2F%2F...[以下略]

WARNING: `gcloud auth login` no longer writes application default credentials.
If you need to use ADC, see:
  gcloud auth application-default --help

You are now logged in as [xxxxx@gmail.com].
Your current project is [springboot-webapp-project].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID

```
以上で gcloudコマンド 経由で GCP が操作できるようになりました。

# GAEのリージョンを指定する
GAEはプロジェクト毎に初回利用時にデプロイ先の [リージョンを指定する](https://cloud.google.com/appengine/docs/locations?hl=ja) 必要があります。
これは一度設定するとその後変更できないため、本番では慎重に決める必要があります。

リージョン指定は WEBコンソール または [`gcloud app create`](https://cloud.google.com/sdk/gcloud/reference/app/create?hl=ja) でできます。
後者を使うと次のような感じ。

```
> gcloud app create
You are creating an app for project [springboot-webapp-project].
WARNING: Creating an App Engine application for a project is irreversible and the region
cannot be changed. More information about regions is at
<https://cloud.google.com/appengine/docs/locations>.

Please choose the region where you want your App Engine application
located:

 [1] asia-east2    (supports standard and flexible)
 [2] asia-northeast1 (supports standard and flexible)
 [3] asia-northeast2 (supports standard and flexible)
 [4] asia-south1   (supports standard and flexible)
 [5] australia-southeast1 (supports standard and flexible)
 [6] europe-west   (supports standard and flexible)
 [7] europe-west2  (supports standard and flexible)
 [8] europe-west3  (supports standard and flexible)
 [9] europe-west6  (supports standard and flexible)
 [10] northamerica-northeast1 (supports standard and flexible)
 [11] southamerica-east1 (supports standard and flexible)
 [12] us-central    (supports standard and flexible)
 [13] us-east1      (supports standard and flexible)
 [14] us-east4      (supports standard and flexible)
 [15] us-west2      (supports standard and flexible)
 [16] cancel
Please enter your numeric choice:  2

Creating App Engine application in project [springboot-webapp-project] and region [asia-northeast1]....done.
Success! The app is now created. Please use `gcloud app deploy` to deploy your first app.
```

以上でデプロイ可能になりました。

# デプロイする
全て準備が整ったので [このへんを参考](https://cloud.google.com/appengine/docs/standard/java/tools/using-maven?hl=ja#deploying_your_application) に `mvn package appengine:deploy` でデプロイします。
ざっくり１０分程度かかります。

```
# GAEにデプロイする
> mvn clean package appengine:deploy
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< com.example:springboot-webapp >--------------------
[INFO] Building springboot-webapp 0.1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
(中略)
[INFO] GCLOUD: ...................................done.
[INFO] GCLOUD: Deployed service [default] to [https://springboot-webapp-project.appspot.com]
[INFO] GCLOUD:
[INFO] GCLOUD: You can stream logs from the command line by running:
[INFO] GCLOUD:   $ gcloud app logs tail -s default
[INFO] GCLOUD:
[INFO] GCLOUD: To view your application in the web browser run:
[INFO] GCLOUD:   $ gcloud app browse
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 07:24 min
[INFO] Finished at: 2019-08-05T12:35:49+09:00
[INFO] ------------------------------------------------------------------------
```

# デプロイしたアプリにアクセスしてみる
WEBコンソール を下記スクショの通りたどるか、またはCUIから [`gcloud app browse`](https://cloud.google.com/sdk/gcloud/reference/app/browse?hl=ja) でデプロイしたアプリにアクセスできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/703dc3ba-af8f-1f38-4c5b-e778d4b32a74.png)

GAE上のアプリにアクセスした結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/b13a67aa-a1a1-3c4e-0258-e12a3e1dd86b.png)

# デプロイしたアプリを停止する
今回デプロイしたアプリはサービス名が未指定のため「default」となり、これは削除できません。
その代わりにサービスを停止させます。（今回はWEBコンソールでポチポチやります）


次の通りサービス「default」は削除できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/ee9819e6-edc2-d042-8c83-88dda0ac30c0.png)

代わりに バージョン タブで稼働中のものを選択して停止させる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/d59d4c0a-6ac4-d0a8-d8d5-c39fabefd400.png)

以上で完了です。

# おまけ１

さきほどサービスとかバージョン（とかインスタンス）という用語が
出てきましたが、これは [公式のこのへん](https://cloud.google.com/appengine/docs/flexible/java/an-overview-of-app-engine?hl=ja) で解説されています。

上記の中に次のような図が出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/a7e9bd64-7855-07c0-be13-cd23503d7fc5.png)

これをよくあるような案件に当てはめると、次のように対応します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/e22ef4f5-80a9-d40b-3081-3b77b7331828.png)

# おまけ２
なんかよくわからないけど動かないぞーといった場合は、インスタンスにログインして様子をみましょう。
次の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/cf7b344a-7a1d-3897-b12c-329242ccdd81.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26808/90bfdfc6-769f-0fc4-6c05-dc06f1845a2d.png)


以上です。