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


はじめに

「CI/CD Advent Calendar 2021」12日目です。本カレンダーの1日目にGCPのCloud BuildでCI/CD環境を構築しSpring Bootアプリをビルド、デプロイするを投稿しましたが、今回は少し違った構成でCI/CD環境を構築してみます。

構築するCI/CD環境構成

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

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

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

構築してみる

1. ビルドサーバーの構築

ビルドサーバーとなるGCEインスタンスにJenkinsをインストールをします。Jenkinsのインストール方法は公式サイトやWeb上で多くの記事があるのでここでの詳細は割愛しますが、今回はMavenを使ったビルドを行うため、以下のプラグインをインストールしておきます。

2. SonarQubeサーバーの構築

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

2-1 SonarQube Scanner for Jenkinsプラグインのインストール

2-2 Jenkinsのシステムの設定からSonarQubeサーバーの情報の登録

3. JenkinsとSlackの連携設定

JenkinsとSlackを連携させるための設定をします。こちらの記事がとても分かりやすく書かれていたため、こちらを参考に設定しましたが、今回はPipelineからSlack APIを呼び出すため以下も追加で行います。

3-1 Slack Notificationプラグインのインストール

4. Cloud Buildのビルドトリガーの作成

今回はJenkinsからCloud BuildのWebhookを利用するため、ビルドトリガーがWebhookイベントとなるトリガーを作成します。

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の作成

# 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ジョブが開始しました(ジョブ名が適当なのはスルーしてください)。

そして問題なく終了しました。

SonarQube側も見てみます。

色々と指摘されていますが、Jenkinsから静的解析を実行できました。

Slackも見てみます。

いくつか通知が混ざってますが、一番下のがそれに該当します、問題なく通知もできています。

最後にCloud Buildを確認します。

こちらも無事に完了しCloud Runへデプロイすることができました。

Appendix

  • ソースリポジトリ ※5年前くらいにCircle CI、CodeCov、Slack連携するのに作ったお試しリポジトリになります

以上です。