Dockerで構築したJenkinsで Dockerイメージを作成して Artifact RegistryにPushするまで

# 概要

Dockerで構築したJenkinsで Dockerイメージを作成して Artifact RegistryにPushするまでの作業手順を記録します。

- DockerでJenkinsを構築
- Azure ADを利用したJenkinsログインを設定
- Jenkinsで Spring Boot アプリケーション(jar)を動かすためのDockerイメージをビルド
- 作成したDockerイメージを GCPの Atifact RegistoryにPush

# 前提

- Dockerを利用しているのは、社内システムとしてのデプロイ先としてDockerを利用しているため
- Jenkinsを利用しているのは、社内システムデプロイ先が(IPアドレス制限などの制約)があるため、自前のCI環境を作る必要があったため

# 背景

Spring Boot で開発したWebアプリケーションをDocker化してクラウド環境にデプロイしたいと考え、Dockerレジストリに登録するまでの作業を行いました。

# DockerでJenkinsを構築

## 用意するもの
- Dockerfile
- docker-comopse.yml

### Dockerfile

Dockerをインストールしておく、Dockerサービス自体はホストマシンと共有する

```Dockerfile
FROM jenkins/jenkins:lts
 
USER root
 
RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
    sh ./get-docker.sh
```
※参考
https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script

### docker-compose.yml
ポイントは以下
- 上記で作成したDockerファイルを利用
- ホスト側の docker.sock をコンテナ側にマウント

```yaml
version: '2.0'
services:
  jenkins:
    build:
      context: .
      dockerfile: Dockerfile
    restart: always
    ports:
      - '8080:8080'
    environment:
      TZ: Asia/Tokyo
    volumes:
      - jenkins-volume:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
 
volumes:
  jenkins-volume:
```
### jenkins 起動

Dockerfile及び、docker-compose.ymlがあるディレクトリで以下コマンドを実行

```bash
$ docker-compose up -d .
```

# JenkinsでAzureADを利用したJenkinsログイン環境

社内アカウントはMicrosoft365を利用しているので、AzureADでのログイン設定を実施

## AzureADでアプリ登録

- 以下URLから新規登録を行う
https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade
- アプリケーション形式は 「Web」
- 「リダイレクト URI」は以下を設定
```
https://{Jenkinsサーバドメイン}/securityRealm/finishLogin
```
- 「フロントチャネルのログアウト URL」は以下を設定
```
https://{Jenkinsサーバドメイン}/securityRealm/OicLogout」
```
- 「証明書とシークレット」から新しいクライアントシークレットを作成し手元にメモしておく

## Jenkinsでプラグイン登録及び設定

- Microsoft Entra ID (previously Azure AD) Plugin　をインストール
- 「Jenkinsの管理」-> 「Security」に移動
- ユーザー情報を「Azure Active Directory」を選択
- Client ID、Client Secret、Tenant にアプリ登録及びクライアントシークレット生成で取得した値を設定する。

# Jenkinsで Spring Boot アプリケーション(jar)を動かすためのDockerイメージをビルド

- ビルド用Dockerfile
```Dockerfile
FROM eclipse-temurin:17-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
```
- JenkinsでPipelineジョブを作成
- 下記 Pipelineジョブを作成（一部抜粋）
```groovy
stage('before build') {
    steps{
        script {
            // build.gradle のバージョンを取得
            env.APP_VERSION = sh(script: "cat build.gradle | grep \"version = \" | sed \"s/'//g\" | awk '{print \$3}'", returnStdout: true).trim()
        }
    }
}
stage('build jar') {
    steps {
        sh './gradlew build'
    }
}
stage('build docker image') {
    steps {
        sh '''
        docker build --build-arg JAR_FILE=/build/libs/app-${APP_VERSION}.jar \
          -t {LOCATION}-docker.pkg.dev/{PROJECT-ID}/{REPOSITORY}/{IMAGE}:${APP_VERSION} .
        '''
    }
}
```

# 作成したDockerイメージを GCPの Artifact RegistoryにPush

## Artifact Registoryにリポジトリを作成

- 適当な「名前」を設定
- 形式は「Docker」を設定
- リージョンは適切に設定
- 他の設定はデフォルトにし「作成」を押下する

## JenkinsからPushする際の認証用サービスアカウントを作成

- 適当な「サービスアカウント名」を設定
- ロールに「Artifact Registory リポジトリ管理者」を設定
- それ以外は変更せず作成
- 作成したサービスアカウント -> キー から新しい鍵を「JSON形式」で作成

### Jenkinsにサービスアカウントで作成した鍵を登録する

- Jenkinsの管理 -> Credentialsに移動
- (global)Domainに対して Add Credentialsを行う
- 種類は「Secret file」を選択し、作成したJSON形式の鍵をアップロード
- ID、説明は適当に入力し作成

## PipelineScriptでArtifact RepositoryへのPushを定義する

下記Pipeline Scriptを作成
```groovy
stage('register docker image') {
    steps {
        withCredentials([file(credentialsId: '{設定したCredentialsID}', variable: 'AUTH_KEY_FILE')]) {
            sh '''
            # 認証
            cat "$AUTH_KEY_FILE" | docker login -u _json_key --password-stdin https://{LOCATION}-docker.pkg.dev
            # Push
            docker push {LOCATION}-docker.pkg.dev/{PROJECT-ID}/{REPOSITORY}/{IMAGE}:${APP_VERSION}
            '''
        }
    }
}
stage('after build') {
    steps {
        sh '''
        # PushしたDockerイメージを削除
        docker images -q "{LOCATION}-docker.pkg.dev/{PROJECT-ID}/{REPOSITORY}/{IMAGE}" | xargs docker rmi -f
        '''
    }
}
```

# 感想

- JenkinsのログインをAzureADにすると全てそれになり、ローカルID/PASS認証が出来なくなるので困りそう
- pipelineの書き方について少し学習できた
- dockerイメージの運用(バージョンの付け方とか)とかどうするのが良いかあまり分かっていないので、要検討(latestとその他とか)
- APP_VERSIONでDockerイメージのタグを指定しているけど、バージョンはrelease時に設定するので、デバック用のイメージ作成とか考えると、コミットハッシュの方がよさそう。そこは使い分けなのか。

# 参考資料

- https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script
- https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling?hl=ja