Jenkins のジョブ名に規約を設けて作業ゲームから抜け出す

## まえがき

Jenkins、環境やアプリケーションの数が増えれば増えるほど、似たようなジョブをたくさん画面から登録していく作業ゲームになりがちでつらい。

特に「フリースタイル・プロジェクト」でシェルを実行させるようなジョブ (デプロイ、受け入れテスト等) を設定するのがつらいと思う。

環境 `test_a, test_b, test_c, production_a, production_b, prudiction_c` があって、それぞれの環境に 2 台のアプリサーバーが存在し、それぞれのアプリサーバーに対して`ロードバランサー切り離し、モジュール入替、アプリ再起動、ロードバランサー再組み込み`のタスクを行いたいような場合とか。

ジョブを小分けにして汎用化したいなら、`環境数 6 * アプリサーバー数 2 * タスク数 4` で 48 個のジョブが必要になる。Jenkins の画面から 1 個ずつジョブを作って実行したいシェルを書き込んでいく作業が苦痛になってくる。

## ジョブ名に規約を設ける

自分なりに色々試して、「ジョブ名に規約を設ける」というやり方が割といい線を行っているように思えた。

たとえば Capistrano 3 によるデプロイを Jenkins から実行させたい場合に、ジョブ名に下記のような規約を設ける。

```
cap.環境名.タスク名.オプションキー.オプション値
```

これに従ってジョブを作成する。いずれも 1 行で済む。

* ジョブ名: cap.test_a.deploy_module.hosts.test_a-app001
* リポジトリ URL: https://github.com/tily/job-name-convention-pattern
* シェルスクリプト: bundle exec rake jenkins

このジョブを実行してみる。リポジトリに存在する Rakefile の中の jenkins タスクが JOB_NAME をパースし下記のデプロイコマンドが実行される。

```
cap test_a deploy_module --hosts test_a-app001
```

`jenkins` タスクはあまり複雑なことはやっておらず、単純化するなら下記のような処理を行っているだけ (より詳しい例は https://github.com/tily/job-name-convention-pattern/blob/master/Rakefile#L4 に置いておいた)。

```ruby
task :jenkins do
    ignore, env, task, target_type, target_name = ENV['JOB_NAME'].split('.')
    system "cap #{env} #{task} --#{target_type} #{target_name}"
end
```

## コピーして名前を変更するだけ

この仕組みを導入することにより、既存のジョブをコピーして名前を変更していくだけで、必要なジョブを量産していくことができる。

test_a 環境に必要な 8 個のジョブなら下記のようにコピーしていけばいい。

```
cap.test_a.detach_lb.hosts.test_a-app001
cap.test_a.detach_lb.hosts.test_a-app002
cap.test_a.deploy_module.hosts.test_a-app001
cap.test_a.deploy_module.hosts.test_a-app002
cap.test_a.restart_app.hosts.test_a-app001
cap.test_a.restart_app.hosts.test_a-app002
cap.test_a.attach_lb.hosts.test_a-app001
cap.test_a.attach_lb.hosts.test_a-app002
```

## コピーを自動化

今度はひたすら画面からポチポチとジョブをコピーしていく作業が待っているんだけど、それすらも面倒くさくなってきたので Jenkins の API を利用してジョブのコピーをコマンドラインから行えるようにした。

```
rake copy_job   # copy jenkins job
rake list_jobs  # list jenkins jobs
```

```
## コピーしたいジョブを探す
$ bundle exec rake list_jobs
I, [2015-04-04T09:46:12.258057 #45829]  INFO -- : Obtaining jobs matching filter '.+'
cap.test_a.deploy_module.hosts.test_a-app001

## コピー
$ bundle exec rake copy_job FROM=cap.test_a.deploy_module.hosts.test_a-app001 TO=cap.test_a.deploy_module.hosts.test_a-app002
I, [2015-04-04T09:47:17.645406 #45835]  INFO -- : Copying job 'cap.test_a.deploy_module.hosts.test_a-app001' to 'cap.test_a.deploy_module.hosts.test_a-app002'

## コピーできた
$ bundle exec rake list_jobs
I, [2015-04-04T09:47:38.408586 #45840]  INFO -- : Obtaining jobs matching filter '.+'
cap.test_a.deploy_module.hosts.test_a-app001
cap.test_a.deploy_module.hosts.test_a-app002
```

コピーしまくるシェルを書いて一括で大量にコピーするようにしている。

なお、`copy_job` を実行したあとになぜか Jenkins の画面で「ビルド実行」ボタンが出ない現象に遭遇したんだけど、メモリの内容とディスクの内容に不整合が発生している (?) せいらしく、	「Jenkins の管理」から「設定の再読み込み」を実行すると治った。

## 合わせて使いたい

* [Configuration Slicing Plugin - Jenkins - Jenkins Wiki](https://wiki.jenkins-ci.org/display/JENKINS/Configuration+Slicing+Plugin)
   * この手法で作った同じ設定内容で名前の違う大量のジョブを管理する際に便利だった
* [Build Flow Plugin - Jenkins - Jenkins Wiki](https://wiki.jenkins-ci.org/display/JENKINS/Build+Flow+Plugin)
   * この手法で作った汎用的なジョブを組み合わせさまざまな処理を実現することができる

## まとめ

ジョブ名に規約を設けることでジョブの設定をほぼ不要にし、コピーだけで汎用的なジョブが量産できるようになった。今回は ruby + capistrano + rake の例を書いたけど、どんな言語・ツール・タスク実行フレームワークにも適用できるはず。

もっといいやり方があったら教えていただけると助かります。