GCPでJenkins Pipelineを使ってSonarQubeと連携させたCI/CD環境を構築し結果をSlackへ通知する

## はじめに
「CI/CD Advent Calendar 2021」12日目です。本カレンダーの1日目に[GCPのCloud BuildでCI/CD環境を構築しSpring Bootアプリをビルド、デプロイする](https://qiita.com/NagaokaKenichi/items/c88fddbeb8ef56df7a51)を投稿しましたが、今回は少し違った構成でCI/CD環境を構築してみます。

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/0dbb35b7-769b-b44f-5af7-3c311c2194e0.png)

■概要
※Jenkinsは全てPipelineを利用します
1. GitHubへのPushをトリガーにJenkinsからSonarQubeの静的解析を実行します。
2. Jenkinsでビルドとテストを実行します。
3. [GCPのCloud BuildでCI/CD環境を構築しSpring Bootアプリをビルド、デプロイする](https://qiita.com/NagaokaKenichi/items/c88fddbeb8ef56df7a51)で作成した環境を利用してCloud Runへデプロイします（Cloud BuildのWebhookをJenkinsからCurlコマンドで実行させることで実現します）
4. 最後にJenkinsの結果をSlackへ通知します。

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

## 構築してみる

### 1. ビルドサーバーの構築
ビルドサーバーとなるGCEインスタンスにJenkinsをインストールをします。Jenkinsのインストール方法は公式サイトやWeb上で多くの記事があるのでここでの詳細は割愛しますが、今回はMavenを使ったビルドを行うため、以下のプラグインをインストールしておきます。
![maven integrationのインストール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/09b5db96-9486-8969-6c8c-953754a55e4b.png)


### 2. SonarQubeサーバーの構築
ビルドサーバーとは別にSonarQubeサーバーを構築します。SonarQubeはGCPのマーケットプレイスに登録されているものを利用しました。こちらも数クリックで構築できるため詳細は割愛しますが、Jenkins側の設定としては以下を行います。

#### 2-1 SonarQube Scanner for Jenkinsプラグインのインストール
![sqon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/c0cf1dbe-2f1a-b4e5-2a2e-26d414e88a3c.png)

#### 2-2 Jenkinsのシステムの設定からSonarQubeサーバーの情報の登録
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/805360d9-a0ca-0356-9662-09aaf5b093bd.png)

### 3. JenkinsとSlackの連携設定
JenkinsとSlackを連携させるための設定をします。[こちらの記事](https://qiita.com/i_12121226/items/36dc0a9302e16ce29c11)がとても分かりやすく書かれていたため、こちらを参考に設定しましたが、今回はPipelineからSlack APIを呼び出すため以下も追加で行います。

#### 3-1 Slack Notificationプラグインのインストール
![Slack Notification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/96acd715-af6c-c209-02ac-555316d16c98.png)

### 4. Cloud Buildのビルドトリガーの作成
今回はJenkinsからCloud BuildのWebhookを利用するため、ビルドトリガーがWebhookイベントとなるトリガーを作成します。
![trigger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/b58e3e1d-ad68-69a7-c624-74f4ec265807.png)
Webhookイベントのトリガーを作成するとURLが発行されるので、そのURLをJenkinsから叩く形になります。

### 5. Jenkinsfileの作成

```jenkinsfile.
pipeline {
  agent any
  stages {
    stage('SonarQube Analysis') {
      steps {
        script {
          def mvn = tool 'My Maven'
          withSonarQubeEnv() {
            sh "${mvn}/bin/mvn sonar:sonar"
          }
        }
      }
    }
    stage('Build & Test') {
      steps {
        sh "mvn test"
      }
    }
    stage('Create Container Image & Deploy') {
      steps {
        sh "curl -X POST -d '{}' 'https://cloudbuild.googleapis.com/v1/projects/PROJECT_NAME/triggers/Webhook-for-jenkins:webhook?key=KEY&secret=SECRET'"
      }
    }
  }
  post {
    // Allureレポートを入れてみたがうまく動かないので後で対応
    always {
      script {
        allure([
          includeProperties: false,
          jdk: '',
          properties: [],
          reportBuildPolicy: 'ALWAYS',
          results: [[path: 'target/allure-results']]
        ])
      }
    }
    success {
      slackSend color: "good", message: "Build Successful $JOB_NAME. See $BUILD_URL"
    }
    failure {
      slackSend color: "danger", message: "Build Failure $JOB_NAME. See $BUILD_URL"
    }
  }
}
```

### 6. Dockerfileの作成


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

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

# 成果物(jarファイル)を作成(CompileとTestはJenkinsで行うためSkip)
RUN mvn package -Dmaven.test.skip

# 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. cloudbuild.yamlの作成

```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']
# Clourd RunへDockerイメージをデプロイ
- 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'
```

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

Jenkinsジョブが開始しました（ジョブ名が適当なのはスルーしてください）。
![スクリーンショット 2021-12-12 020800.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/277d61d1-8c78-54f9-8fdf-793ffcdfb6f2.png)

そして問題なく終了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/a187154e-ba76-0aa7-9da7-a61feb86f47b.png)

SonarQube側も見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/62fa50a4-89f3-ff4e-df96-550758952205.png)
色々と指摘されていますが、Jenkinsから静的解析を実行できました。

Slackも見てみます。
![aaaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/582936cb-ec0a-87f9-9fd2-0bcfd32cd2bf.png)
いくつか通知が混ざってますが、一番下のがそれに該当します、問題なく通知もできています。

最後にCloud Buildを確認します。
![aasa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/434da35d-0e55-41aa-923e-69c23cfbe53f.png)
こちらも無事に完了しCloud Runへデプロイすることができました。

## Appendix

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

以上です。