JenkinsとGitHubのWebhook連携の整理

JenkinsとGitHubのWebhook連携の整理

Jenkins と GitHub を連携させる Webhook について

GitHub と Jenkins を連携させる機能のひとつに「Webhook」と呼ばれるものがあります。この Webhook をつかうことで、 GitHub 上で管理しているリポジトリにブランチを push したときや、新たに Pull Request を作成した時などに Jenkins のジョブを走らせるといったことができます。

しかしながら、 Jenkins や Jenkins の複数のプラグインが Webhook の機能を提供しており、どの機能がどのサービスを提供しているのかがわかりづらくなっているのが現状です。そこでこのエントリーでは、それぞれの機能でどのような特徴があり、どういった設定が必要なのかを、実際に設定を行いながら整理したいと思います。

なお使用している Jenkins のバージョンはこの記事執筆時点での Jenkins の最新バージョンの安定バージョン 2.32.3 LTS です(各プラグインのバージョンについては項目ごとに記述)。

今回 Webhook で実現したい Jenkins ジョブ

  • GitHub 上で管理している Git リポジトリの master ブランチへ push したときに特定の Jenkins ジョブのビルドを実行する
    • master 以外のブランチの push イベントではビルドを実行しないようにできるのが理想です
  • ビルドの実行内容はリポジトリ内に含まれる README.md を表示するだけのものにします
#!/bin/sh -xe
cat README.md

Jenkins と GitHub を Webhook で連携させる様々な方法

Jenkins を GitHub と Webhook によって連携させる方法は、主に以下の3つがあります。

  • Jenkins 標準 Webhook
    • Jenkins の標準機能として提供されている「リモートからビルド」の設定
  • Git Plugin
    • Jenkins のビルドの際に git リポジトリをクローンして実行できる機能を提供できるプラグイン
  • GitHub Plugin
    • GitHub からの push イベントを受け取るプラグイン
    • Jenkins から GitHub に Webhook の自動登録ができる

それぞれの手段とプラグインについての特性をまとめると以下のようになります。

方法 設定の容易さ 汎用性 セキュリティ
Jenkins 標準 Webhook
適切に権限などを設定できればセキュリティは高い
Git Plugin ×
push イベントを受け取るのは容易
GitHub Plugin ×
GitHub 側の Webhook 登録も自動でできる(設定が必要)

次にプラグインごとの設定についてなど手順を追って詳しく見ていきましょう。

Jenkins 標準 Webhook

Jenkins のビルドトリガーの1つに、「リモートからビルド」という設定項目があります。これは Jenkins に標準の機能として提供されているもので、特にプラグインなどを導入しなくてもこの項目を有効にすれば Webhook の機能を利用することができます。

では Jenkins 標準の Webhook を使って実際に設定してみましょう。

Jenkins 側の設定

▷ ビルド・トリガ の設定

ジョブ設定の「ビルド・トリガ」の中から「リモートからビルド (例: スクリプトから)」を有効にすると、 Jenkins 側に http://[JENKINS_URL]/job/[JOB_NAME]/buildhttp://[JENKINS_URL]/job/[JOB_NAME]/buildWithParameters というエンドポイントができ、ここに Webhook を仕掛けることでジョブを実行させることができます。

/buildWithParameters を使うと GitHub から送られてくるイベントの詳細(payload)を Jenkins 側で受け取ることができます(payload については後述)。この URL を GitHub 側にセットする場合は、 Jenkins のジョブ設定にある「ビルドのパラメーター化」の項目に「payload」という名前の「文字列」項目をしておく必要があります(MUST)。このパラメーターを使う必要ないジョブの場合は /build を使うとよいでしょう。

ポイント
  • 認証トークンは空欄でも設定でき、 Jenkins のセキュリティ機能を有効化している(ビルドの際にログインが必須になっている)場合、 API Token を使ったBasic認証が必須になるのでそちらで認証を一任したほうがいいかもしれません
  • ここで入力する「認証トークン」とは、後述する GitHub Webhook 設定画面にある「Secret」の設定とは無関係です

▷ ソースコード管理

Git リポジトリのクローンには Git Plugin が必要で、有効になっていれば Git という設定項目があるはずなので、「リポジトリURL」欄に Git の remote アドレスを入力します。このとき「ビルドするブランチ」も指定できるので、「*/master」などを設定すれば master ブランチを pull してくることができます。

GitHub 側の設定

続いて GitHub で Webhook の設定を行います。リポジトリのページから「Settings」>「Webhooks」>「Add Webhook」から追加することができます。

なお GitHub では Webhook の設定を行えるのはリポジトリの管理者(Admin)のみです。もしあなたがそのリポジトリの管理者でない場合などは Settings のリンクがないかもしれません。

▷ Jenkins にアクセスするための API Token を取得する

Jenkins のセキュリティ機能が有効になっている場合、ビルドを実行するには「ユーザーID」と「API Token」が必要になります。 API Token とはログイン時に用いる「パスワード」ではなく、アカウントごとに発行される32文字のランダムな英数字列です。

Jenkins の管理画面から、「Jenkinsの管理」>「ユーザーの管理」>「(API Token を取得したいユーザーID)」>「設定」>「APIトークンの表示」で取得することができます。

▷ 設定項目

今回の設定する項目は以下のとおりです。

  • Payload URL
    • http://[USER_ID]:[API_TOKEN]@[JENKINS_HOST]/job/[JOB_NAME]/[build|buildWithParameters]?token=[TOKEN_NAME]
      • 例) http://admin:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@jenkins.example.com/job/webhook_job/buildWithParameters?token=hogehoge
  • Content type
    • application/x-www-form-urlencoded
  • Secret
    • (空欄)
      • Jenkins ジョブ設定で入力した 認証トークン の項目と連動していません。トークンを設定した場合は Payload URL の中にパラメーターとして入力する必要があります
  • Which events would you like to trigger this webhook?
    • Just the push event.
      • 今回目標としているジョブを実現するには push イベントだけ受け取れば良いので、今回は push イベントだけを通知してもらう設定にします
      • push 以外のイベントが必要な場合や、 Jenkins 側で push 以外のイベントを受け取って実行するジョブの内容を変更したりする場合などは、「Send me everything(送れる限り全てのイベントが送信されるようになる)」や、「Let me select individual events.(送ってくるイベントの種類を選択することができる)」などで設定しても良いと思います。

動かしてみる

そういえば「なう」っていつの間にか死語になりましたね

おそらく上記の設定が正しくできていれば GitHub に push するとビルドが実行されて、ビルド結果をみると README.md が表示されるようになっているはずです。

しかしながらここで1つ問題が発生します。ここまでの設定だけでは master ブランチ以外のブランチを push してもビルドが実行されてしまいます。今回のような処理が軽いビルドであれば大した問題ではありませんが、もし成果物のコンパイルやデプロイなどを行うジョブの場合は、今のようにブランチが push される度に動いたらたまったものではありません。

この問題の解決は GitHub の設定だけではできません。 GitHub は push イベントと Webhook を連動させることはできますが、「○○ブランチが push された時だけ hook する」など、イベントの内容による切り分けが行えないので、細かい部分については Webhook を受け取る Jenkins 側でうまく制御する必要があります。

master ブランチが push された時だけ実行されるようにするには

master ブランチ以外が push されたときにビルドを実行しないようにするには、ジョブの設定時に紹介した payload というパラメーターが役に立ちます。 payload は GitHub が Webhook で設定された URL にアクセスする際に「どういったイベントの発生によって叩かれたのか」であったり、どのブランチで起きたイベントなのかなど、原因となるイベントの詳細情報が入っているパラメーターです。

今回はこの payload という値と、 Jenkins プラグインの Conditional BuildStep Plugin を使って master ブランチが push された際にだけビルドを行うように制御したいと思います。

Conditional BuildStep Plugin が入っていれば、ビルド手順の追加の項目の中に「Conditional step (single)」というものがあると思うので、これを追加して以下のように設定します。

項目 内容
Run? Regular expression match
Expression .*"ref":"refs\/heads\/master".*
Label ${ENV,var="payload"}
On evaluation failure Don’t run

これを設定を追加したことで master ブランチ以外のビルドを push したときにジョブの実行はされますが、ビルドの処理内容である「README.md の表示」までは行われないようになっているはずではないでしょうか。

メリット

この機能をつかって GitHub と連携させることのメリットは、 payload パラメーターを取得することができることです。 今回は payload をつかって「master ブランチが push されたときのみ」という条件で処理を実行するかどうかを判断させましたが、これ以外にもいろいろな情報が payload には入っているので、もっと複雑な条件でもビルドの実行を判断させることができます。

デメリット

Jenkins の認証情報などセキュリティ情報を Webhook に設定する URL の中に含んで設定する必要があるのでセキュリティ的にはあまり良いとは言えないかもしれません。特に API Token などが流出した場合、そのユーザーの権限が許す限り Jenkins の設定内容を変更されてしまう可能性があります。

またこの Webhook の URL は1つのジョブにつき1ずつ発行されるので、1つのリポジトリのイベントから色々なジョブを実行したい場合などは、 Jenkins 側に受け口となるジョブを1つ用意するか、 GitHub 側に複数の Webhook を仕掛ける必要があります。

またメリットにあった payload の値によってジョブの実行内容を変えるといった処理は一見便利ですが、一方で設定が煩雑になりがちです。あなた以外の人がジョブの設定を行うことがある場合、あまり優しいとはいえないかもしれません。

Git Plugin

Jenkins の Git Plugin は「Jenkins のジョブ実行前に Git リポジトリをクローンしてくれる」という機能がメインですが、それだけではなく、実は Webhook の機能もあります。さらに GitHub 側もこのプラグインを使った Webhook Service の設定に対応しており、 Jenkins 標準 Webhook であったような煩雑な認証の設定などが必要なく、比較的簡単に導入することができます。

ブランチの push によってビルドを実行したい場合は、おそらくいちばん手頃な方法です。

使用した Git Plugin のバージョン

3.0.5

Jenkins 側の設定

▷ ビルド・トリガ

「SCMをポーリング」という設定項目を ON にすることで、 Webhook のエンドポイントへアクセスがあった場合にジョブが実行されるようになります。このとき「スケジュール」という欄を「空欄」にしていると警告が出ているかと思いますが無視して空欄のままでかまいません。本来この設定項目は、スケジュール欄に設定した間隔で定期的に Git リポジトリの状態を見て変更があればジョブを実行するという設定を行うためにあるものですが、 Git Plugin では便宜的にこの設定項目が ON になっているかどうかで Webhook からアクセスされた時に実行するかどうかを判断しているようです。

▷ ソースコード管理

Jenkins 標準 Webhook の設定時と同じく、「リポジトリURL」欄と、「ビルドするブランチ」欄にそれぞれ、Git の remote アドレスと、ビルド実行したいブランチの「*/master」を入力します。

またこの時入力した「ビルドするブランチ」欄の設定によって push 時にビルドが実行するかどうかを区別するようなので、別のブランチ(例えば development ブランチ)が push された時にビルドを実行したい場合は、この欄を「*/development」などに変更するとよさそうです。

GitHub 側の設定

Git Plugin を使う場合、 GitHub 側に既に設定が用意されています。 Webhook の設定を行うには「Settings」>「Integration & services」>「Add service」から「Jenkins(Git Plugin)」を選択します。

▷ Jenkins(Git plugin) 設定画面

  • Jenkins url
    • http://[JENKINS_HOST]/
      • 例) http://jenkins.example.com/
      • ジョブとかのパラメーター設定は必要ない

設定は以上で完了です。

動かしてみる

上記の設定が正しくできていれば、 GitHub の master ブランチに push することでビルドが実行され、他のブランチに push した場合はビルドが実行されないようになっているはずです。

メリット

Jenkins 側も、 GitHub 側も設定する項目が少なくかつシンプルです。単純にブランチの push イベントを受け取って実行したい場合にはこれで充分なものと思われます。

デメリット

基本的には push イベントのみを受け付けるためのもので、 Jenkins 標準 Webhook のように payload の値を受け取って Jenkins 側で条件別けを行ったり、 push 以外のイベント(例えば「GitHub 上で Pull Request が新たに作成された」だとか)は取得できません。

さらにセキュリティ的な問題もあります。このプラグインの Webhook の実行には認証が必要ありません。 Jenkins の URL とそれに連携しているリポジトリの URL が第三者に知られた場合、第三者の任意のタイミングでビルドが実行できるようになってしまいます(ただし Git リポジトリで前回のコミットからの変更がなければ、ビルドは実行されないようにはなっている)。これらを回避するためには、 nginx などのリバースプロキシなどを使用して、アクセス元の IP アドレスを GitHub からのものに制限するといった対策が考えられますが、あなたが Jenkins を動かしているサーバーの管理者でない限りその設定を行うことは難しいかもしれません。

GitHub Plugin

Jenkins のプラグインの中にはいかにも GitHub との連携をしてくれそうな GitHub Plugin というものがあります。名前だけみると GitHub と様々な連携を行ってくれそうですが、実際は Jenkins 上で GitHub へのリンクを表示したり、 GitHub からの push イベントを受け取る Webhook の機能を提供してくれることしかできないので過信しすぎないようにしましょう。

こちらも Git Plugin と同じく GitHub 側がこのプラグインを使った Webhook Service の設定に対応しています。

使用した GitHub Plugin のバージョン

1.26.1

Jenkins 側の設定

▷ GitHub Project

「GitHub Project」にチェックを入れて、GitHubのページのURLを設定します。

▷ ビルド・トリガ

ビルド・トリガの中にある「GitHub hook trigger for GITScm polling」という項目を ON にします。
ちなみに過去のバージョンでは 「Build when a change is pushed to GitHub」という項目名だったようで、過去にこのプラグインを取り上げた記事では項目名が違うことがあります。

▷ ソースコード管理

いままでの設定と同じく、「リポジトリURL」欄と、「ビルドするブランチ」欄にそれぞれ、Git の remote アドレスと、ビルド実行したいブランチの「*/master」を入力します。
また「ビルドするブランチ」欄の設定も Git Plugin と同じく、この欄に指定したブランチを push した時のみビルド実行を実行してくれるようになっています。

GitHub 側の設定

こちらも概ね Git Plugin と同じで、 GitHub 側に既に GitHub Plugin 用の設定が用意されています。 Webhook の設定を行うには「Settings」>「Integration & services」>「Add service」から「Jenkins(GitHub Plugin)」を選択します。

▷ Jenkins(GitHub plugin) 設定画面

  • Jenkins hook url
    • http://[JENKINS_HOST]/github-webhook/
      • 例) http://jenkins.example.com/github-webhook/
      • /github-webhook/ が必要なことに注意

設定は以上で完了です。

動かしてみる

上記の設定が正しくできていれば、 GitHub の master ブランチに push することでビルドが実行され、他のブランチに push した場合はビルドが実行されないようになっているはずです。

【番外編】Webhook 登録を Jenkins 側から自動で行う

実は GitHub Plugin の設定次第では、 Jenkins のジョブ設定画面上から GitHub のリポジトリ設定へ自動的に Webhook を登録させることができます。

▷ GitHub から Personal access token を取得する

Webhook の自動登録には Jenkins に GitHub API を使うための Personal access token が必要になるので、そのトークンを取得しましょう。

GitHub にログインしていれば ここから 、もしくは GitHub トップページ > 右上のユーザーアイコン >「Settings」>「Personal access tokens」から「Generate new token」をクリックします。

「Token description」にはそのトークンを登録する目的などを入力(例: Jenkins GitHub Plugin)し、「Select scopes」では「repo」と「admin:repo_hook」にチェックを入れて「Generate token」ボタンを押します。

すると画面の緑色の背景上に40文字の英数字列が表示されるので、その値をメモしておきます。

▷ Jenkins に Personal access token を登録する

さきほど取得した Personal access token を Jenkins 側に登録します。まず Jenkins から「Jenkinsの管理」>「システムの設定」の画面に飛ぶと「GitHub」という見出しの付いた設定項目があるはずです。

その設定画面上にある、「Add GitHub Server」ボタン >「GitHub Server」を選択すると、いくつか新しく入力項目が出現します。 Personal access token を登録するには、「Credentials」という項目の「追加」ボタン >「Jenkins」を選択します。

ウィンドウが出現するので、「種類」項目を「Secret text」に変更し、「Secret」項目にさきほど GitHub から取得した Personal access token を入力、「説明」には何かわかりやすいラベルを付けて「追加」をクリックします。

すると「Credentials」のプルダウンにさきほど登録した項目が出現するので、これを選択し、「Test connection」をクリックすれば正しいトークンが設定できたかどうかテストができます。
今回の目的である Webhook の自動登録は「Manage hooks」にチェックを入れておくことで行ってくれるようになるはずです。

最後にページ下部の保存をクリックすれば Webhook についても自動で登録してくれるようになったはずです。

メリット

基本的な Webhook の機能を使う分には Git Plugin とほぼ同等の機能であり、あまりこのプラグインを使って Webhook をしかけるメリットが少ないようですが、番外編として紹介した GitHub に Webhook の自動登録をしてくれる機能があるので、複数のリポジトリの Jenkins ジョブを、あえて1つの Jenkins で管理する場合などには便利かもしれません。

デメリット

ほぼ Git Plugin の機能と同等のものを実現するためのもので、特段こちらのプラグインを使わなければいけない理由もないでしょう。またセキュリティに関する部分についても GitHub Webhook の Secret Key の設定項目自体はあるのですが、 Secret に関する HTTP ヘッダーを送らなければチェックそのものを回避できてしまうバグのようなものがあるため、あまり意味はありません。

まとめ

今回 GitHub と Jenkins を Webhook で連携させるいくつかの方法について、1つ1つの特性を考え、手順を整理して詳しく掘り下げてみました。それぞれのプラグインによってできることとできないことがあり、それらをうまく使い分けてプロジェクトに役立てていければと思います。

参考文献