JenkinsでCI環境構築事始め

# はじめに

Jenkins を使って CI 環境を構築する方法について説明します。

Jenkins は 2.121.1 を使いました。
アプリケーションは Ruby on Rails を対象にしています。

尚、導入したプラグインは他にもあるかもしれませんが、あまり比較できていません。

# 想定する CI 環境と開発の流れ

## CI 環境

CI 環境は以下を想定しています。

- 開発は各人の PC で行う
- SCM は Git を用いる
- Git Repository は GitHub を用いる (public)
- Jenkins はサーバ上に構築する(GitHub と Jenkins が連携する)

## Jenkins とは

Jenkins はジョブ管理アプリケーションです。
何かしらの処理を行うジョブを作成し、ジョブを実行する条件をイベントとして設定して利用します。
(当然、手動で実行もできる)

今回想定する CI 環境においては、GitHub へ push が行われたことを Webhook によりフックして、ビルドとテストを行うジョブを実行することが出来ます。

## Slack 連携で Jenkins のジョブ実行結果を通知する

GitHub と Jenkins を連携することで最低限 CI 環境は構築できますが、ジョブが正しく終了したことを即時通知されると便利です。

そこで、Jenkins のジョブを実行した結果を Slack 通知することにします。
(Hipchat を使うことも出来ます)

## 想定環境のまとめ

1. 開発環境でソースコードを修正する
2. コミットと push を行う (何度か繰り返す)
    - push されたコードを使って Jenkins がビルドとテストを行い、結果を Slack にて通知する
3. GitHub 上で PR を作成する
    - 作成された PR のマージ元ブランチのコードを使って Jenkins がビルドとテストを行い、結果を Slack にて通知を行う
    - Jenkins が行うテストの結果が成功しない場合は GitHub 上でマージボタンが押せないように制限する
4. PR に対してレビューを行う
5. PR をマージする

# Jenkins に導入すべきプラグイン

- GitHub Plugin
    - 概要: GitHub との連携
    - https://wiki.jenkins.io/display/JENKINS/Github+Plugin
- GitHub Pull Request Builder
    - 概要: GitHub の PR が作成されたタイミングで Job を稼働させる
    - https://wiki.jenkins.io/display/JENKINS/GitHub+pull+request+builder+plugin
- Slack Notification Plugin
    - 概要: Job 実行結果等を Slack へ通知を行う
    - https://wiki.jenkins.io/display/JENKINS/Slack+Plugin
- Job Configuration History Plugin
    - 概要: Job設定の履歴を保存して、誰がいつどのような変更を行ったのか確認や元戻しが出来る
    - https://wiki.jenkins.io/display/JENKINS/JobConfigHistory+Plugin

# 事前準備

`[Jenkinsの管理] > [グローバルセキュリティの設定] > [マークアップ記法]` を辿り、Safe HTML を設定する。

これを設定することで GitHub Pull Request Builder によるビルド結果を HTML リンクで表すことが出来る。

# プラグインのクイックスタート

プラグインをとりあえず使えるようにするための設定概要を記載します。

設定項目の詳細についてや、記載内容以外にカスタマイズしたい場合は、項目ごとに公式情報へのリンクを貼りますので、そちらを参照して下さい。

インストール方法は共通で、Jenkins のプラグインマネージャからインストールします。
プラグインマネージャの画面は `[Jenkins の管理] > [プラグインの管理] > [利用可能]` から辿れます。

## GitHub 連携 Plugin 共通

### Jenkins Bot 用 GitHub アカウントの作成と権限の付与

個人の GitHub アカウントで設定することも出来ますが、Jenkins Bot 用 GitHub アカウントを作成することが推奨されています。

今回は各 repository 毎に必要に応じて Bot アカウントを collaborator として追加する方法を取りますが、複数の repository で同様の設定をしたい場合は GitHub で Organization を作成し、Team 設定にてアカウントを管理するのが望ましいでしょう。

1. GitHub で Jenkins Bot 用 GitHub アカウントを作成(Sign up)する (以降、jenkins-github-user とする)
    - GMail ユーザであれば `${ユーザ名(@より左)}+jenkins@gmail.com` 等としてアカウントを作るとアカウントが流用出来ます
2. GitHub で repository 管理者アカウントにログインし直す
3. repository の collaborator に jenkins-github-user アカウントを追加する

次に Jenkins から jenkins-github-user でログインするための SSH 接続設定を行います。

1. 適宜 jenkins-github-user 用の SSH 鍵ペアを作成する
2. GitHub で SSH 鍵の公開鍵を登録する
    1. jenkins-github-user ユーザでログインする
    2. 画面右上のプロフィール画像メニューから `[Settings] > [SSH and GPG keys]` を辿る
    3. `[New SSH key]` ボタンをクリックして公開鍵を登録する
3. Jenkins で SSH 鍵の秘密鍵を登録する
    1. `[認証情報] > [Stores scoped to Jenkins] > [(global)] > [認証情報の追加]` を辿る
    2. 次の内容で鍵を登録する    

        |項目名|内容|
        | --- | --- |
        | 種類 | SSH ユーザ名と秘密鍵 |
        | スコープ | グルーバル (「システム」ではJobで利用できない。別途作成したスコープがあれば適宜利用する) |
        | ユーザ名 | jenkins-github-user |
        | 秘密鍵 | 直接入力 (※適宜変えてもよい) |
        | パスフレーズ | ${秘密鍵に設定したパスワード} |
        | ID | ${適宜分かりやすいID。Job設定時にセレクトボックスとして表示される} |
        | 説明 | ${適宜分かりやすい説明。credential 一覧で表示される} |

## GitHub Plugin のクイックスタート

詳細は [GitHub Plugin の公式 Wiki](https://wiki.jenkins.io/display/JENKINS/Github+Plugin) を参照のこと。

1. GitHub の Webhook 設定にて GitHub Plugin 用の Webhook URL を設定する
    1. GitHub の Webhook 設定画面は Login 後、`[レポジトリTOP] > [Settings] > [Webhooks]` から辿れる
    2. 下記 URL を設定する
        - `http[s]://${JenkinsサーバのFQDN}/github-webhook/ `
    3. イベントは [Just the push event] を設定する
2. Jenkins の各 Job で GitHub Plugin に関連する設定を行う
    1. `[General] > [GitHub project]` を辿り、`[Project url]` に GitHub プロジェクトの URL を入力する (Clone 用 URL ではないので注意。例: https://github.com/jenkinsci/jenkins)
    2. `[ソースコード管理] > [リポジトリ]` を辿り次の項目を設定する

        |項目名|内容|
        | --- | --- |
        | リポジトリURL | ${GitHub リポジトリの clone 用 URL (use SSH)。<br>例: git@github.com:jenkinsci/jenkins.git} |
        | 認証情報 | ${事前に作成した jenkins-github-user の SSH 秘密鍵} |
        | ビルドするブランチ | ** |
    3. `[ビルド・トリガ]` を辿り `[GitHub hook trigger for GITScm polling]` にチェックを入れる
3. `[ビルド]` を辿り、 `[シェルの実行]` にビルドしたい内容を設定する
    - 設定内容は適宜。参考としてサンプルを[Jenkins ジョブサンプル](#Jenkins+ジョブサンプル)に示します。

## GitHub Pull Request Builder の設定方法の概要

詳細は [GitHub Pull Request Builder の公式 Wiki](https://wiki.jenkins.io/display/JENKINS/GitHub+pull+request+builder+plugin) を参照のこと。

1. GitHub の Webhook 設定にて GitHub Plugin 用の Webhook URL を設定する
    1. GitHub の Webhook 設定画面は Login 後、`[レポジトリTOP] > [Settings] > [Webhooks]` から辿れる
    2. 下記 URL を設定する
        - `http[s]://${JenkinsサーバのFQDN}/ghprbhook/`
    3. イベントは `[Let me select individual events] > [Pull Requests]` を設定する
2. GitHub で API token を作成する
    1. jenkins-github-user ユーザでログインする
    2. 画面右上のプロフィール画像メニューから `[Settings] > [Developer settings] > [Personal access tokens]` を辿る
    3. `[Generate New Token]` ボタンを押す
    4. Token description に分かりやすい説明を入力する
    5. Select scopes は repo にチェックを入れる
3. Jenkins で GitHub API token を登録する
    1. `[認証情報] > [Stores scoped to Jenkins] > [(global)] > [認証情報の追加]` を辿る
    2. 次の内容で GitHub API token を登録する

        |項目名|内容|
        | --- | --- |
        |種類|Secret Text|
        |スコープ|グローバル|
        |Secret|${jenkins-github-userで作成した API token}|
        | ID | ${適宜分かりやすいID。Job設定時にセレクトボックスとして表示される} |
        | 説明 | ${適宜分かりやすい説明。credential 一覧で表示される} |

4. Jenkins で GitHub Pull Request Builder に関するシステム設定を行う
    1. `[Jenkins の管理] > [システムの設定] > [GitHub Pull Request Builder]` を辿り、次のとおり設定する

        |項目名|内容|
        | --- | --- |
        |GitHub Server API URL|https://api.github.com|
        |Credentials|登録した API token|
        |Description|適宜分かりやすい説明(Jenkins Job 設定時のセレクトボックスで表示される)|
        |Auto-manage webhooks|チェックを入れる|

5. Jenkins の各 Job で GitHub Pull Request Builder に関する設定を行う
    1. `[General] > [GitHub project]` を辿り、 `[Project url]` に GitHub プロジェクトの URL を入力する (Clone 用 URL ではないので注意。例: https://github.com/jenkinsci/jenkins)
    2. [ソースコード管理] > [リポジトリ] を辿り次の項目を設定する

        |項目名|内容|
        | --- | --- |
        | リポジトリURL | ${GitHub リポジトリの clone 用 URL (use SSH)。<br>例: git@github.com:jenkinsci/jenkins.git} |
        | 認証情報 | ${事前に作成した jenkins-github-user の SSH 秘密鍵} |
        | Refspec | `+refs/heads/*:refs/remotes/origin/* +refs/pull/${ghprbPullId}/*:refs/remotes/origin/pr/${ghprbPullId}/*` |
        | ビルドするブランチ | `${sha1}` ※他にビルドするブランチ設定があれば削除しておく|

    3. `[ビルド・トリガ]` を辿り `[GitHub Pull Request Builder]` にチェックを入れ、次の設定を行う

        |項目名|内容|
        | --- | --- |
        | GitHub API credentials | https://api.github.com |
        | Admin list | ${リポジトリの管理者の GitHub アカウント名} |
        | Use github hooks for build triggering | チェックを入れる |

## Slack Notification Plugin の使い方

### Jenkins に Plugin の設定をする

`[Jenkins の設定] > [システムの設定] > [Global Slack Notifier Settings]` から辿り、次の設定を行う。

```
Base URL                        : https://${WS_NAME}.slack.com/services/hooks/jenkins-ci/
Team Subdomain                  : 空
Integration Token               : 空
Integration Token Credential ID : ${Slack用Tokenを選択}
Is Bot User?                    : レ
Channel                         : #${Slack_channel_name}
```

プラグインの設定は以上で終わりです。

後は、ジョブの `Slack Notifications` の設定でビルド結果に対して通知をするかどうかをチェックボックスで選択すれば、指定したチャンネルに通知されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206493/46356bfb-09cf-6e43-2960-47f6f5984979.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/206493/75f54cc3-3581-f9d9-9303-2f10f559d5eb.png)


# Jenkins ジョブサンプル

これで Jenkins のジョブを作成する準備が出来ました。

ここで Ruby on Rails のアプリケーションをテストし、ビルドを試すジョブを作ります。

## Ruby on Rails のビルド＆テスト

```sh:
IMAGE=ruby:2.5.1
JENKINS_UID=$(id -u jenkins)

docker run -u $JENKINS_UID -i -v $WORKSPACE:/usr/src/app --rm $IMAGE bash -c "$(cat <<EOF
cd /usr/src/app;
bundle install --path vendor/bundle;
bundle exec rake test;
bundle exec rake assets:precompile RAILS_ENV=production
bundle exec rake assets:clobber
EOF
)"
```

上記ジョブは Git リポジトリから取得したソースコードを Docker コンテナを使ってテスト及びビルドを行います。その他、備考は以下の点です。

- コンテナが溜まり続けないように Docker run は --rm をつけて実行が完了したらコンテナを削除するようにしましょう
- IMAGE に書かれた内容で Ruby のバージョンを指定します
- GitHub プラグインが fetch したコードは Jenkins のワークスペース内にあるため、Docker 実行時にマウントしている
    - コンテナ内で再度 fetch してもよいが SSH 鍵設定を注入するよりはマウントした方が望ましいと思った
- コンテナの実行ユーザ ID が Jenkins ユーザと同じになるよう設定している
    - 未設定の場合ワークスペースにオーナが root のファイルが作成されることになり、ワークスペースの削除ができなくなる

# GitHub の設定

[Node.js, Express, sequelize, React で始めるモダン WEB アプリケーション入門 (CI/CD編)#ビルドステータスを成功に保つ](https://qiita.com/tatsurou313/items/0f8af5b8dbd1e10340ed#%E3%83%93%E3%83%AB%E3%83%89%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%92%E6%88%90%E5%8A%9F%E3%81%AB%E4%BF%9D%E3%81%A4) を参考にして設定すると、作成された PR はテストとビルドが通らない場合(Jenkinsジョブが成功しない場合) はマージできなくなります。