GCPのCloud BuildでCI/CD環境を構築しSpring Bootアプリをビルド、デプロイする

## はじめに
🎄🎅🎄🎅🎄🎅🎄「CI/CD Advent Calendar 2021」記念すべき1発目です🎄🎅🎄🎅🎄🎅🎄
（そして2年ぶりのAdvent Calendar参戦です）
というわけで早速、、、、、、本記事ではGCPのCloud Buildを使ってCI/CD環境を構築しSpring Bootアプリをビルド、デプロイしてみようと思います。

## CI/CD環境を構築する上での課題
CI/CD環境は今や多くの現場で導入されており、私自身も利用した経験はあります。CI/CDのメリットは多くの記事で紹介されていますが、環境構築という点で以下のデメリットがあると思っています。

* CI/CDツールの選定工数がかかる
* CI/CD環境の構築工数がかかる

## Cloud Buildとは
GCPが提供するサービスの1つで、GitHub等のVCSからソースを取得しビルド、デプロイを行うサービスです。定義ファイルに一連のビルドステップを記述することでビルド、デプロイを実行します。ビルドを行うための機能がBuilderという形で提供されているため、上記課題に頭を抱えることなくCI/CD環境を構築することができます。

* [Cloud Build公式ページ](https://cloud.google.com/build/docs?hl=ja)

## 構築するCI/CD環境構成
今回構築するCI/CD環境構成は以下の通りです。

![aaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/1712329c-69b8-8332-6cda-0a88e9b7e5b7.png)

■概要
1. GitHubへのPushをトリガーにCloud Buildでビルドを実行します。
2. ビルド成果物をContainer Registory(GCPのDockerイメージリポジトリ)へPushします。
3. Container RegistoryへPushしたDockerイメージをCloud Run(GCPのフルマネージドのサーバーレスプラットフォーム)へデプロイします。

つまり、GitHubへPushするだけでビルドからデプロイまでを自動化(CI/CD)することが可能になります。

なお、利用する資材はJava8で書かれたSpring Boot + H2DBのRest APIのサンプルです。

## 環境構築してみる
### 1. ビルドトリガーを作成
何をトリガーにビルドを実行するか、、を最初に設定する必要があるのでそちらから作成していきます。2021年12月時点でサポートされているトリガーは以下の通りです。
![スクリーンショット 2021-11-24 215805.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/936e9682-e2b5-cca2-69e3-d14476ebf798.png)


##### 1-1. GCPのナビゲーションメニューからCloud Buildを選択する
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/1a2a641c-6dc4-955f-ddc5-4b4130b86b11.png)

##### 1-2. トリガーを作成ボタンをクリック
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/b48ebf0e-7083-af2e-b95b-afed7554f227.png)

##### 1-3. トリガーの名前とイベントを入力してリポジトリの設定
任意のトリガーの名前を入力し、Cloud Buildのトリガーとなるイベントを選択します。本稿では、VCSにコードがPushされたことをトリガーにCloud Buildを回すので「ブランチにpushする」を選択します。そのあとに、どのリポジトリにPushされたら、、を設定するために新しいリポジトリへ接続をクリックします。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/47e8159a-88a4-3254-89be-b4af3cd9e9c0.png)

以降は下記キャプチャの赤枠の通り進めます。最終的に設定したものがリポジトリに表示されていればOKです。ブランチは任意のブランチを選択します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/7c4eda70-73d3-e9a8-7a1c-fd73ca98ccf6.png)
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/d3c65182-7491-635f-4b56-bad8bd378b5d.png)
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/f9ac9e5c-a7b8-330f-be67-7d34547dddfe.png)
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/153a8193-ca68-fad1-3862-c7c0d417bb44.png)
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/f225abdd-0808-8d10-1398-8b870df1de06.png)

##### 1-4. 構成の設定

Cloud Buildへの指示はCloud Build 構成ファイル（yaml または json）やDockerfileから行えます。本稿ではcloudbuild.yamlというyamlファイルをGitHubリポジトリの直下に配置することで指示するものとします。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/e88919bd-01fa-38b8-bfe4-8a93841dbe0b.png)

最後に作成ボタンをクリックして完了です。

##### 1-5. リポジトリの確認
トリガーで設定したリポジトリへアクセスし、Settings→IntegrationsにGoogle Cloud Buildが表示されていればOKです。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/f71f69ea-4861-ac0e-c149-4b870778ae69.png)

### 2. Cloud Build APIを有効化
GCPのナビゲーションメニューからAPIとサービスを選択→APIとサービスの有効化→Cloud Buildで検索しCloud Build APIを選択し有効化します（キャプチャでは有効化した状態になっています）
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/f0e934f3-d72c-eda5-b9de-36de53efa75d.png)
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/706bdaca-9749-759f-5124-6452e7f7c6d7.png)
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/53bb67d0-c17d-7fa1-035e-e92cc2acd91b.png)

### 3. Cloud Buildのサービスアカウントの有効化
Cloud Buildの設定から、Cloud Run管理者とサービスアカウントユーザーのステータスを有効化します。
![スクリーンショット 2021-11-25 001242.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/bb9fe408-315b-ee7e-b3a3-6ad6e5a41232.png)

### 4. 必要なロールをCloud Buildサービスアカウントに付与
IAMにてCloud Buildサービスアカウントに必要なロールをします。具体的にはCloud Run管理者ロールとService Usage管理者ロールを追加します。以下の図のようになっていればOKです、他の2つのロールは最初から付与されているはずですが、なければ追加します。
![スクリーンショット 2021-11-25 001851.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/33e2aa1f-6c79-5f6c-db70-8819c854e36a.png)



### 5. ビルド構成ファイル（cloudbuild.yaml）の作成
続いて、ビルド構成ファイル（cloudbuild.yaml）を作成します。ビルド構成ファイルには、Cloud Buildに実行させたいアクションをビルドステップごとに記述していきます。今回はシンプルに以下の流れとなるのでその通りに記述します。

 Dockerイメージの作成
 　　　↓
 DockerイメージをContainer RegistryへPush
　 　　↓
 Clourd RunへDockerイメージをデプロイ

```cloudbuild.yaml
steps:
# Dockerイメージの作成
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/spring-boot-restful-api-test:$COMMIT_SHA', '.']
# DockerイメージをContainer RegistryへPush
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/$PROJECT_ID/spring-boot-restful-api-test:$COMMIT_SHA']
# DockerイメージをClourd Runへデプロイ
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  entrypoint: gcloud
  args:
  - 'run'
  - 'deploy'
  - 'spring-boot-restful-api-test'
  - '--image'
  - 'gcr.io/$PROJECT_ID/spring-boot-restful-api-test:$COMMIT_SHA'
  - '--region'
  - 'asia-northeast1'
images:
- 'gcr.io/$PROJECT_ID/spring-boot-restful-api-test:$COMMIT_SHA'
```

ビルド構成ファイルのスキーマ構成やクラウドビルダーの詳細については以下の公式を参照ください。
[・ビルド構成ファイルのスキーマ構成](https://cloud.google.com/build/docs/build-config-file-schema)
[・クラウドビルダー](https://cloud.google.com/build/docs/cloud-builders)
[・gcloud runコマンド](https://cloud.google.com/sdk/gcloud/reference/run/deploy)

### 6. Dockerfileの作成
続いて、Dockerfileを作成します。Dockerイメージを作成するタイミング(docker buildのタイミング)でビルドとテストを行うため、RUNコマンドでmvn packageを実行させます。デプロイ後のdocker runのタイミングでSpring Bootを起動(Embedded Tomcatを起動)させるために、CMDコマンドでjavaコマンドを実行するという流れで記述します。

```Dockerfile
# Spring BootアプリをビルドするためMaven/Java8のDockerイメージを利用
FROM maven:3.5-jdk-8-alpine AS builder

# ビルド時のワークディレクトリの設定
WORKDIR /app
COPY pom.xml .
COPY src ./src

# Maven packageでビルドとテストを実行し成果物(jarファイル)を作成
RUN mvn package

# Spring Bootアプリの実行環境にopenjdk:8-jre-alpineを利用
FROM openjdk:8-jre-alpine

COPY --from=builder /app/target/Spring-Boot-RESTful-API-Test-Sample-1.0.0-SNAPSHOT.jar /app.jar

# Docker Run時にjavaコマンドでSpring Bootを起動(Embedded Tomcatを起動)
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
```

### 7. 動作確認
任意のファイルを適当に修正しGitHubへPushして動作確認をしてみます。

無事ビルドが開始しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/c01a079e-8d17-49dc-6ffe-91f81a9c755c.png)

そして問題なく終了しました。
![aaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/1dc74455-67c0-09aa-962f-8bf6e0aecb84.png)

ビルドログも確認したところ、テストもちゃんと回っていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/8a5e1487-a701-cc56-2148-82a4ad0be980.png)

Cloud Runの方も問題なさそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/c9067542-2650-2dc2-fcf9-80dbabea7b1d.png)

実際に任意のAPIを叩いてみても期待通りのレスポンスが返ってきました（H2DB接続も問題なさそうです）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/9b178455-f66c-a40a-d50d-399d1be9b293.png)

### 8. 最後に
Cloud Buildを使ってCI/CD環境を構築してみましたが、大きな躓きもなく構築することができました（強いて言うならIAMの設定周りとDockerfileの書き方くらい）。所要時間としては、調査時間等も含めて約10時間ほどになります。今回は対象外としましたが、Cloud Buildで以下をどのように実現するか等も今後試してみたいと思います。

* テストレポートやカバレッジをグラフィカルに表示させる
* Sonar等を用いた静的解析も行う
* デプロイ後にSlack等のチャットへ通知


## Appendix

* [ソースリポジトリ](https://github.com/kenichi-nagaoka/spring-boot-restful-api-test) ※5年前くらいにCircle CI、CodeCov、Slack連携するのに作ったお試しリポジトリになります


以上です。