1台のWindowsマシンでJenkinsマスターとスレーブを登録してみた

# 概要
1台のWindowsマシンで、Dockerで立ち上げたJenkinsマスターと、ホストのWindowsネイティブで動くJenkinsスレーブを共用できたので、備忘録として残します。

# モチベーション
- 会社の組み込みソフト用のCI環境立ち上げが目的
- 手元にあるのがWindows10マシン1台なので、まずはこれで全て賄いたい

# 構成
- Dockerホスト: Windows10
- Jenkinsマスター: Docker containerで立ち上げ
- Jenkinsスレーブ: DockerホストのWindows10で立ち上げ
![Structure](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274086/382e78d1-f549-ca1d-44d2-6d3a6a90771e.png)

# 何でそんな構成にしたの？
主な理由は以下の通りです。

1. Jenkins立ち上げの属人化を防ぐ一環で、DockerでJenkinsサーバーを立ち上げたかった
2. CI対象のビルド用ライセンスがWindows限定だった
3. JenkinsのDockerはLinuxコンテナ限定なのでWindowsのコマンドが動かせず、ビルドできなかった: [参考文献](https://www.jenkins.io/doc/book/installing/docker/#on-windows)

主に2、3のせいで若干複雑な構成になった感じです。
単に目的達成だけなら、JenkinsマスターをWindowsインストーラでセットアップすれば良かったんですが。
仮にサーバーマシンを入れ替えたりすることになっても、手順書をゴリゴリ書くよりはコマンド一発でセットアップできるほうが楽かなと。
（あと単純にDockerを使ってみたかったという理由もあります）

# 環境
- Windows10 Home Version 20H2 (Build 19042.685)
- Docker Desktop Version 3.1.0 (51484)
- WSL2
- Jenkins Version 2.263.2
- WMI Windows Agent Version 1.7

# リポジトリ
今回のDocker構成は [Github](https://github.com/m2enu/docker-jenkins) に置いています。

# Jenkinsマスター
## Dockerfile
コンテナ内のファイルを編集するときに備えて、（宗教上の理由から）Vimだけインストールしています。
本当はここにWMI Windows Slaveプラグインのインストールを書くべきなのでしょうが、今回は省略しました。

```dockerfile:jenkins/Dockerfile
FROM jenkins/jenkins:2.263.2-lts-slim

USER root
RUN apt-get update -y
RUN apt-get install -y vim

USER jenkins
```

## docker-compose.yml
ポートフォワーディングの設定を管理したり、将来的にnginxも使ったりするかもなので、今はJenkinsマスター単体ですがdocker-composeから起動するように準備してます。
データの永続化はVolumeで。
個人的に後から分かりやすいように`jenkins_`って付けてるんですが、Volume名なんかが`jenkins_jenkins_master_data`とかになっちゃう。フォルダ名が付与されちゃうのかな・・・？

```yaml:docker-compose.yml
version: "3"
services:
    jenkins_master:
        container_name: master
        build:
            context: .
            dockerfile: ./jenkins/Dockerfile
        environment:
            JENKINS_OPTS: '--prefix=/jenkins'
        ports:
            - 80:8080
            - 50000:50000
        volumes:
            - jenkins_master_data:/var/jenkins_home
volumes:
    jenkins_master_data:
```

## Dockerコンテナ起動
普通に `docker-compose up -d` で起動します。

## Jenkins起動まで
Unlockして、Suggested pluginをインストールして、Adminユーザーを作る。
今回は詳細は省略します。

## Jenkins初期設定
`Jenkinsの管理` -> `プラグインの管理` から [WMI Windows Agent](https://plugins.jenkins.io/windows-slaves/) プラグインをインストールします。

## Slaveノードの追加
`Jenkinsの管理` -> `ノードの管理` -> `新規ノード作成` から追加します。
最初のページは以下の通り設定。

- ノード名: windows-slave （今回の例。スレーブに合わせて適切に）
- Permanent Agent にチェック

次のページは以下の通り設定。

- リモートFSルート: `C:/tmp`
- 起動方法: `Launch agent by connecting it to the master`

これでノードの追加は完了です。
この時点では「×」のアイコン付きで表示されます。

![node](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274086/f3926514-7dcc-efef-7124-a4602c37b027.png)

スレーブのノードをクリックするとJNLPで接続する手順が表示されます。
今回はLaunchボタンでなく、`Run from agent command line`の手順で接続しました。
ちゃんと接続できると「×」のアイコンが消えます。

## ジョブ設定
`新規ジョブ作成`からテスト用のジョブを作成します。

- ジョブ名: SlaveJob1（今回の例。ジョブに合わせて適切に）
- フリースタイル・プロジェクトのビルドを選択

### General の設定
- [x] 実行するノードを制限
    - ラベル式: windows-slave

### ビルド の設定
- `ビルド手順の追加` -> `Windowsバッチコマンドの実行`
- コマンド: `..\..\build.bat`
    - あとでスレーブに作るビルド用のバッチファイル名と合わせます
    - 実行ディレクトリは `[リモートFSルート]/workspace/[JOB名]`となるようなので、2つ上の階層を指定しています

# Jenkinsスレーブ
リモートFSルートである`C:\tmp`を作成します。
その中に先ほど指定した`build.bat`を作成します。
今回はテスト用に中身は単純なechoとしますが、ここにビルド用のコマンドを記述します。

```batchfile:build.bat
@echo off
echo "Slave build job"
```

## ビルド実行
再度メニューから`ビルド実行`で手動ビルドします。
するとビルド履歴に`#XX`と履歴が表示されるので、そこから`コンソール出力`を見ると以下のように表示されるはずです。

```
Started by user USER_NAME
Running as SYSTEM
Building remotely on windows-slave in workspace C:/tmp/workspace/SlaveJob1
[SlaveJob1] $ cmd /c call C:\Users\USER\AppData\Local\Temp\jenkins12877575992189775420.bat

C:\tmp\workspace\SlaveJob1>..\..\build.bat
"Slave build job"

C:\tmp\workspace\SlaveJob1>exit 0 
Finished: SUCCESS
```

# 終わりに
若干複雑な構成になっちゃいましたが、1台のWindows10マシンでDockerのJenkinsマスターとホストネイティブのJenkinsスレーブを共用することができました。
ただ、実は2021/01/24現在、この記事の構成に成功したのは自宅のWindows10マシンで、会社のマシンではスレーブの接続あたりが上手く動いてません。
WSL2バックエンドでないとダメとかあったら嫌だなぁ・・・（会社マシンはWindowsビルドバージョンのせいでWSL2が入れられない）