2016.07.01

Jenkins 2.0 Pipeline as Code

Pocket

直近はレガシー環境の改善に取り組んでいる D.M. です。今回は 2016 年 4 月に発表された Jenkins 2.0 を取り上げ、最大の特徴である Pipeline as Code について具体的な設定を解説します。

継続的インテグレーションツール戦国時代

継続的インテグレーション(以下 CI )の自動化ツールは現状さまざま出ています。直近の私のチームがメインで使っている Bamboo は Atlassian 社製の有料ツールで、同社の Jira 、 Stash との連携機能を多く備えており、業務効率化に大いに役に立っています。また他の選択肢として Github との連携を謳った Travis CI 、 Circle CI といった有料サービスがあり、ある程度規模のある開発を行う際は高機能有料ツールを選択することが一般的な状況とも言えると思います。

こんな状況の中、 10 年以上の歴史を持つ老舗の無料 CI ツールである Jenkins が、ついに 2.0 となりました。有料ツールほど高機能ではないにせよ、がんばれば比較的何でも実現できてしまう CI ツールとして依然としてスタンダードな地位を確立している Jenkins は、この 2.0 化によって非常に強力な機能を新たに備えましたので、ここで紹介したいと思います。 Jenkins は今後も多くの場面で第 1 の選択肢として検討される対象となることでしょう。

Jenkins 2.0 の特徴

登場の背景や基本機能については開発者川口さんの以下のスライドが大変詳しいです。
Jenkins 2.0 (日本語) 川口耕介
http://www.slideshare.net/kohsuke/jenkins-20

サマリーとして Jenkins 公式ページに記載された以下の説明が簡潔に表現しています。
jenkins2_title

これによると特徴は 3 つです。

1. Pipeline as Code
2. 初期セットアップとUIの向上
3. 既存 Jenkins との後方互換性

ちょっと順序を入れ変えて、まず 2、3 ですが、これらはすぐに確認できる非常にシンプルな特徴です。

「2. 初期セットアップとUIの向上」
⇒ インストールして始めてアクセスした際に立ち上がるセットアップウィザード画面がデフォルトで提供されました。ワンクリックで推奨 Plugin を一括でインストールできる機能が備わっています。

「3. 既存 Jenkins との後方互換性」
⇒ 既存の Jenkins に対して、基本差し替えで動作します。インストールは yum 等でも可能ですし、 war をダウンロードするだけでも大丈夫です。 java コマンドですぐに起動できます。

そして「1. Pipeline as Code」ですが、これは新しい概念です。現状最も分かりやすい資料は公式の英語ドキュメントですが、ただこちらは概要説明がメインとなっていて、細かい設定を確認できる資料が充分ではありません。以下で掘り下げてみようと思います。

Jenkins 2.0 の目玉機能 Pipeline as Code

Pipeline とは

パイプラインとは、 CI ツールで実行したい「一連のジョブの流れ」を意味します。例えば CI ツールではビルドが開始されると、コンパイルして、テストして、デプロイして、リスタートをかけたり、チャットに通知したりします。この連続する CI タスクの実行をパイプライン機能が担います。

Pipeline Plugin

Jenkins 上の連続的なジョブをパイプラインとして定義できる機能です。以前から Workflow Plugin として用意されていたものが、 Pipeline as Code 機能とともにリニューアルされ、 Jenkins 2.0 から標準の Plugin となりました。( Jenkins 2.0 から推奨 Plugin がデフォルト導入できる機能が付いたため、似たような Plugin が乱立して開発者を混乱させていた状況が大きく改善されています)

Pipeline as Code の真髄

CI ツールの運営上よく問題となるのが、ブランチ追加の際のビルド設定の複製作業です。既存の Jenkins では、ブランチが増えた際は Plugin の提供する画面 UI で細かい設定を手動で行う必要がありました。開発中ではブランチの増減があるのが一般的ですが、その都度管理コストが発生していました。 Jenkins API でごにょごにょやる方法もありますが、あまりすっきりした方法ではありませんでした。

そのアンサーとして登場したのが Pipeline as Code です。これは名前そのままパイプラインがコードで書ける機能です。 Jenkins の GUI でちまちま設定していた基本的なビルド設定がすべて Jenkinsfiile 内の Groovy コードで書けるようになりました。 1 レポジトリ内でビルドの流れが大きく変わることはあまりないため、他のブランチで利用していた設定ファイルを新ブランチ内に配置するだけで Jenkins のビルドの用意が完了できます。
そして特に面白いのが Branch Indexing です。これにより git に新ブランチを push した瞬間に、 Jenkins のビルド設定をブランチ内の Jenkinsfile から読み込んで生成することができます。 1 レポジトリ内のビルド設定・実行を自動管理することができます。

ユースケース

私の管理するいくつかのプロジェクトでは Jenkins + Gitlab の無料ツールコンビを使っているので、そこで利用する想定で、以下を考えます。

・ Gitlab にて test-project/test-app.git を開発している。
・ test-app.git に新しいブランチ jenkins-test-branch を push した際に、 Gitlab からの Webhook が Jenkins の Branch Indexing をトリガーし、 jenkins-test-branch の Jenkinsfile から Jenkins のビルド設定を初期化する。
・ test-app.git に commit を push した際に、 Jenkinsfile 設定済みブランチについて Jenkins ビルドが実行される。(コンパイル⇒テスト⇒デプロイ⇒サーバリスタート)

上記のように新規ブランチの設定を自動化するためには Multibranch Pipeline を使いこなす必要があります。以下ではその設定方法を説明します。

前提として、以下に今回利用したのはヴァージョンを記載しておきます。これらは初期セットアップ時の推奨設定でインストールされた Plugin でした。新たに追加した Plugin は今回はありません。
Jenkins 2.11
Pipeline 2.2
Pipeline: Multibranch 2.8

設定作業の詳細

1. git にて test-app.git レポジトリに jenkins-test-branch を作成し Jenkinsfile を add/commit/push
2. Jenkins にて New Item 名 test-pipeline を Multibranch Pipeline として作成
3. Jenkins の test-pipeline の Configure にて git レポジトリを設定
4. Gitlab にて Webhook を設定
5. Jenkins にて test-pipeline の Branch Indexing を実行
6. Jenkins の jenkins-test-branch の個別設定
7. git の jenkins-test-branch を変更し commit/push

1. git にて test-app.git レポジトリに jenkins-test-branch を作成し Jenkinsfile を add/commit/push

Jenkinsfile はパイプライン処理を Groovy スクリプトで記載したものです。今回利用するレポジトリ test-app.git の jenkins-test-branch ブランチ直下に add/commit/push します。

ブランチ push 時に コンパイル⇒テスト⇒デプロイ⇒サーバリスタート のパイプラインを実行するための Jenkinsfile の内容は以下のようになります。
node {

   // Maven の変数設定
   // 'm3' は Jenkins の Global Tool Configuration 画面で設定しておく必要アリ。
   def mvnHome = tool 'm3'

   // ステージ = パイプライン内の個別ジョブ
   // 関連ライブラリをチェックアウトしてビルドする。
   stage 'Checkout Common Library'

   // 関連プロジェクトのチェックアウト
   git url: 'ssh://git@gitlab.url/test-project/common.git'

   // mvn は以下のように書く。
   sh "${mvnHome}/bin/mvn clean install"

   stage 'Checkout test-app'

   // Jenkins に設定済みのバージョン管理システムからチェックアウトするときは以下のように書く。
   checkout scm

   stage 'Build test-app'

   // Maven のビルド実行
   sh "${mvnHome}/bin/mvn clean install"

   stage 'Deploy'
   // (デプロイの処理省略)

   stage 'Restart Tomcat'
   // (サーバ再起動の処理省略)
}
この Jenkinsfile の Syntax は公式ページに説明があります。何か実現したいことが決まっている場合はここで機能を確認することができます。
https://jenkins.io/doc/pipeline/

2. Jenkins にて New Item 名 test-pipeline を Multibranch Pipeline として作成

インストールしたばかりの Jenkins 2.0 はまずセットアップウィザードが表示され、デフォルトの推奨 Plugin をインストールします。初期セットアップ完了後、メニューから New Item を選び、以下のような種別を選択を行います。
※ Jenkins タスクの単位としてジョブと表記してきましたが、最近のものはジョブ = Item となります。

jenkins-test-pipeline

パイプライン機能は Pipeline と Multibranch Pipeline の2種類がありますが、自動設定の恩恵を享受するには Multibranch Pipeline を選択する必要があります。この Multibranch Pipeline は同一レポジトリ内の複数のブランチを同時に管理するための機能で、ブランチを push したり削除したりする際に各ブランチから Jenkins 設定ファイルを自動で検知してビルドを設定・実行してくれます。

3. Jenkins の test-pipeline の Configure にて git レポジトリを設定

通常のジョブはブランチ単位で作成することが一般的と思いますが、 Multibranch Pipeline のジョブはレポジトリ単位で作成します。このジョブの子ジョブにブランチがぶら下がります。
test-pipeline のメニューから Configure を選び、以下のように git のレポジトリの URL と Jenkins 用接続ユーザを設定します。

jeniins-configure

ドハマリポイント1

この Configure には Build Triggers という重要そうな設定項目があります。何を持ってビルドを発火するかという設定なのですが、ここでは意図していた Webhook の受け口の設定ができませんでした。

jenkins-build-triger

Trigger builds remotely
特別な URL に対してスクリプトなどで外部からビルドをトリガーするモード。トークンを設定できます。しかし現状役立たず。チェックをオンにして Save/Apply しても、次に画面を開くと未設定に戻ってしまいます。
※この箇所は Jenkins の Grobal Security 設定が不十分である場合はそもそも設定が表示されません。ただ現状は何をしても動かない!

Periodically if not otherwise run
もしビルドがされていなければ定期実行するモード。上と下の間の子を実現する機能です。 Webhook が通信の問題で届かないようなケースでも時間でトリガーできるので、必要に応じて設定します。これは普通に動きました。

Build periodically
cron と同じようにビルドを定期実行するモード。ただ CI なら push 時に hook してすぐビルドするべきだろうとあるので、あまり使うチャンスはなさげ。

Trigger builds remotely によって push の Webhook でトリガーするつもりでしたが、ここでトークン設定が保存できませんでした。これはバグなのか?ちょっとセキュリティがシビアな環境では問題になりますが、仕方がないのでトークンなしで進みます。次の Gitlab 設定にて Webhook の発行⇒ビルドの発火の動作自体は可能になります。

4. Gitlab にて Webhook を設定

ドハマリポイント1では Jenkins の Webhook の受け取りトリガーの設定がうまくできない状況になりました。
解決策ですが、 Trigger builds remotely をチェックオンにするとにビルド用 URL が記載されているのですが、よく見ると Jenkins の画面 UI にある Build のリンクと同じ URL を同じであることがわかります。単純にこの URL に Gitlab から push 時の Webhook でリクエストすることで、画面でクリックしてビルドをトリガーしているのと同じことを実現できそうです。

以下は Gitlab の test-app.git レポジトリの Webhook URL の設定内にて、ユーザとトークンをつけて POST でリクエストを発行する設定です。

jenkins_webhook

設定の際は上記の username の箇所を Jenkins 側のユーザに変更してください( Gitlab に Jenkins ユーザの作成が必要)。 token の箇所には Jenkins のユーザの管理画面にて発行されるユーザ個別のトークンを設定します。このユーザ設定がうまくできていないと Webhook を発行した際に Jenkins に以下のログが出ます。

情報: While serving http://jenkins.url/job/test-pipeline/build: hudson.security.AccessDeniedException2: anonymous には、ジョブ/Build パーミッションがありません。

上記は Gitlab ⇒ Jenkins の通知を発行するための設定ですが、逆に Jenkins ⇒ Gitlab の通知を発行するには Jenkins 内に Gitlab Plugin を設定します。今回はここは割愛します。

5. Jenkins にて test-pipeline の Branch Indexing を実行

Branch Indexing はレポジトリ内の Jenkinsfile を読み込みビルドの設定・実行を行う機能です。上記の Webhook で発火できますが、初回は動作を見るため Jenkins 画面 UI からリンクで手動実行してみます。

jenkins_branch_indexing

実行すると上記のようなログが見られます。レポジトリをフェッチして、 Met criteria となったブランチは Jenkinsfile が読み込まれたことを意味します。master と develop には Jenkinsfile を設定していないのでビルドは設定されません。また既存で Jenkinsfile が設定されていた test/test ブランチは変更がないためビルドが動作しないというログになっています。
ブランチから Jenkinsfile が削除されると、この Branch Indexing のタイミングで自動的にビルド設定も削除されます。

6. Jenkins の jenkins-test-branch の個別設定

Branch Indexing が成功すると jenkins-test-branch の UI からビルド設定の詳細を見ることができます。メニューから View Configuration をクリックして詳細に進みます。

jenkis-build-triger

ドハマリポイント2
さあ個別のブランチのビルド設定を書くぞと思ったところですが、実態として、上の Configuration では設定を変更できません!チェックボックスをいじっても、保存ボタンが出てこないので反映できないようになっています。実はこの UI は Multibranch Pipeline 用ではなく、単純な Pipeline の場合に設定するために利用するものです。紛らわしいので使えないなら編集画面への導線を出さないでほしいですホント。
結論として Multibranch Pipeline の場合はそもそもそのコンセプト上 GUI で個別ブランチの設定は不要です。これは Multibranch Pipeline がすべて Branch Indexing をトリガーとして Jenkinsfile をベースに動作することで、全設定の自動化を実現しているためです。この Branch Indexing のタイミングで変更が検知できたものだけがビルド対象となります。

7. git の jenkins-test-branch を変更し commit/push

ブランチの何かを変更して commit/push してやります。
すると。。

jenkins-builkd

う ご い た ! !

Webhook によって Branch Indexing が動作し、 jenkins-test-branch のビルドが実行されました。上記のように Jenkins の UI 上でパイプラインの実行状態が可視化されます。

ポイント

Pipeline as Code のポイントは以下のとおりです。
・ レポジトリ全体を Multibranch Pipeline のジョブとして設定できる。
・ 各ブランチのビルドのパイプラインを Jenkinsfile で記載できる。 Jenkins の UI で設定する必要はない。
・ git へ push したときに自動的に Branch Indexing が走り、レポジトリ内の全ブランチから Jenkinsfile がセットされているブランチだけビルド設定が自動構築され、ビルドが実行される。

有料ツールには優れたものが多いですが、無料で使える Jenkins でもかなりの自動化を図れます。特に Multibranch Pipeline は今後積極的に利用していきたいと思っています。

最後に

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。

皆さんのご応募をお待ちしています。