Jenkinsを導入してGithub, Bitbucketから自動ビルドを可能にするまで


今日はJenkinsがテーマです。

JenkinsはCIツールです。ちょっとした設定を行うと、VCSからソースを取得し、ソースコードのビルド(テスト)を継続的に行ってくれます。これにより、ソースが壊れることを防ぐことができます。正確には防ぐ、ではなく知らせる、ですが。

Jenkinsは以前あるプロジェクトで利用したことがあって、セットアップも自分がやったので最低限の知識はあります。ですが、そのときはJenkinsと同サーバ上にあるSVNからソースを取得する形式でした。

今回はこのblogと同じサーバにJenkinsをインストールし、GithubとBitbucketからソースを取得するということを行います。さらに、その2つのサービスのgitリポジトリに対してpushを行ったタイミングでビルドをする、ということを行います。以前利用したときは、内部のSVNからソースの取得を、ビルドはスケジューリングして毎晩、と言う感じでしたので、今回はそれらの点が異なります。

Jenkinsインストールと設定

まずはJenkinsをCentOS6上にインストールします。インストールは特に苦労しませんでした。インストール~自動起動登録まで。

# Jenkinsインストール
wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
yum install -y jenkins

# サービス開始
service jenkins start

# 自動起動登録
chkconfig jenkins on

Tomcatを既にインストールしている場合はJenkinsとポート(8080, 8009)が被ってしまいます。また、できればパスも変更したい(http://tsukaby.com/jenkins/)ので、設定を弄ります。

vi /etc/sysconfig/jenkins

以下を変更します。

  1. JENKINS_PORT 空いている適当なポートへ
  2. JENKINS_AJP_PORT 空いている適当なポートへ(勿論上記とは別ポート) JENKINS_ARGS “—prefix=/jenkins”と記述し、パスを/jenkinsに。

これだけではhttp、つまりポート80でアクセスしたときにHTTPサーバがリクエストを受け取って、そこで完結してしまうので、お目当ての変更したポートの/jenkinsのリソースまで辿り着けません。自分の場合はHTTPサーバにnginxを利用しているので、/etc/nginx/conf.d/tsukaby.com.confに以下を追加します。

vi /etc/nginx/conf.d/tsukaby.com.conf
(※適宜自分の設定ファイルに読み替えます)
server {
    listen 80;

    ...

    location /jenkins { proxy_pass http://127.0.0.1:(上記で変更したJENKINS_PORT)/jenkins; }
    ...
}

これで/jenkinsへのリクエストは正しいポート、つまりJenkinsへ行くので正しくアクセスできます。

Jenkinsへアクセスしたら、管理画面からGit pluginを入れておきます。

Mavenのインストール

今回はMavenプロジェクトを作って、JenkinsにMavenプロジェクトをビルドさせます。そのためにはMavenが必要なので、以下のコマンドでインストールします。

# MavenをDL
wget http://ftp.jaist.ac.jp/pub/apache/maven/maven-3/3.1.1/binaries/apache-maven-3.1.1-bin.tar.gz

# 解凍
tar zxvf apache-maven-3.1.1-bin.tar.gz

# gzを削除
rm -f apache-maven-3.1.1-bin.tar.gz

# Maven公式のインストール手順通りのディレクトリを作成
mkdir /usr/local/apache-maven

# 移動
mv apache-maven-3.1.1 /usr/local/apache-maven/apache-maven-3.1.1

# viで以下を編集。末尾に以下3行の環境変数を追加。
vi /etc/profile

export M2_HOME=/usr/local/apache-maven/apache-maven-3.1.1
export M2=$M2_HOME/bin
export PATH=$M2:$PATH
# 保存したprofileを読み込んで、現在のシェルに反映
. /etc/profile
# 確認
mvn -version

自分の場合はJDKを入れていなかったため、Mavenにjavacがないよ!と怒られました。なので、以下の手順でJREを捨ててJDKを入れました。

# 前にrpmで入れたJREを調べる
rpm -qa | grep jre

# 判明したバージョンのJREを削除
rpm -e jre-1.7.0_25-fcs.x86_64

# 以下のOracle公式からrpmを入手し、Linuxに転送
http://www.oracle.com/technetwork/java/javase/downloads/index.html

# インストール
rpm -ivh jdk-7u45-linux-x64.rpm

これでソフトウェアの準備は整いました。次はビルド対象を用意します。

適当なリポジトリとプロジェクトの作成

GithubとBitbucketで適当なリポジトリを作成しておきます。勿論両方でなくても構いません。自分はGithub(publicリポジトリ)とBitbucket(privateリポジトリ)両方用意しました。

次にリポジトリに入れるサンプルプロジェクトを作成します。今回はEclipseを使って以下のソースのようなものを作成しました。ただのサンプルです。

CalcUtil.java

package com.tsukaby.samplepj;

public class CalcUtil {
  private CalcUtil() {

  }

  public static int add(int a, int b) {
    return a + b;
  }
}

CalcUtilTest.java

package com.tsukaby.samplepj;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class CalcUtilTest {

  @Test
  public void testAdd() {
    int actual = CalcUtil.add(1, 2);
    assertEquals(3, actual);
  }

}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>sample-pj</groupId>
  <artifactId>sample-pj</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>sample-pj</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>
  </dependencies>
</project>

この状態でEclipseからMaven testを実行すると正しくテストが実行されます。(一部省略)

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

これをGithubまたは、Bitbucketにpushすれば準備はOKです。

 

Jenkinsを利用するための設定

ここまでの状態ではSSLの関係上、外部のVCSからソースを取得する、ということができません。そこでいくつかの設定を行います。(ここが一番苦労しました)

# 編集
vi /etc/passwd

# jenkinsユーザでログイン可能な状態にする
# 以下の/bin/falseを/bin/bashに変更する
jenkins:x:494:495:Jenkins Continuous Build server:/var/lib/jenkins:/bin/false

# rootからjenkinsへsu
su jenkins
cd /var/lib/jenkins
mkdir .ssh
cd .ssh

# 鍵を作成
# パスフレーズなど聞かれるが、空にする
ssh-keygen -t rsa -C tsukaby@tsukaby.com

# lessして公開鍵の内容をコピー
less id_rsa.pub

ここで公開鍵の内容をコピーしたので、Githubへ行って登録します。

github_key

Bitbucketでも登録します。

bitbucket_key

この状態でジョブを作ろうとしても、Gitリポジトリを指定した段階で以下のようなメッセージが出てしまいます。

Failed to connect to repository : Command "ls-remote -h git@github.com:tsukaby/sample.git HEAD" returned status code 128:
stdout: 
stderr: Host key verification failed. 
fatal: The remote end hung up unexpectedly

なので、以下のコマンドを叩きます。

git ls-remote -h git@github.com:tsukaby/sample.git HEAD

The authenticity of host 'github.com (192.30.252.131)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes (※ここでyes)
Warning: Permanently added 'github.com,192.30.252.131' (RSA) to the list of known hosts.

stderr: Host key verification failed.が出たら、git ls-remoteを叩く!ということを覚えておくとよいです。上記はGithubの例ですが、Bitbucketを指定したときも同様です。

それではいよいよJenkins Jobを作成していきます。

Jenkinsのジョブ作成(Github用)

Githubで自動ビルドを行う設定をします。まずは予め対象のソースをGithubにpushしておきます。

その後でJenkinsの画面からフリースタイルプロジェクトを選択し、情報を設定していきます。

jenkins_job

この状態で、保存し、ビルドを実行すると、無事Githubからデータが落ちてきます。しかしこれだけでは意味がないので、少し追加設定します。

jenkins_setting

今回自分はsample-pjというリポジトリを作成して、そこにeclipseのsample-pjプロジェクトを追加しました。そのため、リポジトリの下にsample-pjフォルダがあり、その下にpom.xmlがある状態です。pom.xmlのパスを上記で指定しています。Maven 3.1.11はJenkinsの管理画面から自分がインストールしたMavenを登録しているため、表示されています。

この状態でビルドを実行すると、goal testが走るので、JUnitが実行されます。結果は以下のようになり、正しくテストされていることが分かります。

jenkins_build

 

最後にGithubにpushされたら、ビルドが走る、という設定を行います。

まずJobの設定を変更して、ポーリングをONにします。Git pluginの機能が利用するので必ず設定します。

jenkins_setting2

スケジュールは特に入れる必要はありません。

次にGitHubのSettingsからService Hooks, Jenkins(Git plugin)を選択し、以下のように自分のJenkinsURLを入れます。今回の場合、冒頭でURLを/jenkinsに変更したので以下のようになっています。

github_hooks

これだけでOKです。後は適当にソースを修正・追記してcommit, pushすると、ビルドが走ってくれます。

これはどうなっているのか、というと仕組みはシンプルで、単なるHTTPリクエストです。

上記のHookを設定した状態で、Githubにpushを行うと、Githubは設定したURLに以下のようなリクエストを行います。

192.30.252.49 - - [14/Dec/2013:16:37:56 +0900] "GET /jenkins/git/notifyCommit?url=https%3A%2F%2Fgithub.com%2Ftsukaby%2Fsample-pj&from=github&branches=master HTTP/1.1" 200 99 "-" "GitHub Hookshot a82b348" "-"

Jenkins側ではnotifyCommitにGETやPOSTリクエストがあった場合、パラメータを解読します。パラメータのurlにはリポジトリのURLが入っていますので、JenkinsはこのURLを使ったJobがあるかどうかを探します。先ほど、作ったJobではこのURLのリポジトリを利用しているので、そのJobが動く・・・という仕組みです。

一つ自分がはまったことがあって、GithubのHookの画面にTest Hookというボタンがあります。これを押すと上記のnotifyCommitのリクエストが確かに送られてくるんですが、何度やってもビルドが開始されませんでした・・・。これはソースに何も変化が無いから、というオチでした。Hookを確かめるときは実際にソースを変更してpushすることをお勧めします。

Jenkinsのジョブ作成(Bitbucket用)

ほとんどGitHubと同じです。Privateリポジトリでも今回の仕組みを導入できます。ただし、1つだけ注意点があります。

BitbucketにもHookは確かにあって、Jenkinsという項目もあります。

bitbucket_hook

ただし、ここで設定を行っても上記のnotifyCommitへのGETリクエストは送られません。Jenkinsにはもう一つ、認証トークンを使ったビルドの起動方法があって、上記の設定はそれに対応した方法です。なので、今回のケースでは使えません。(※認証トークンを使った方式はとあるJenkinsの認証・認可の仕組み上、よろしくないので推奨しません)

そのため、POSTを使います。BitbucketのHookのPOSTでnotifyCommitへPOSTリクエストを送るようにしてやります。これでGithubと同じ状態にできるため、正しく動きます。

POST先のURLは「http://tsukaby.com/jenkins/notifyCommit?url=(JenkinsのJobで設定したリポジトリのURL)&from=bitbucket&branches=master」などとしてやればOKです。

urlはSSHで落とすかHTTPSで落とすかで変わってくるかもしれないので、そこは注意した方が良いかもしれません。

Jenkinsの認証・認可設定

では最後にJenkinsのセキュリティ設定を行って完了です。

Jenkinsの管理からグローバルセキュリティの設定を選び、行列による権限設定を選びます。ここで、adminは全権限、匿名ユーザは権限なし、とします。これでログインしていないユーザを制限できます。

匿名ユーザの権限をなくしても、notifyCommitによるビルドの開始はできるので、安心してください。

以上で終わりです。Jenkinsのインストールからビルドの自動開始まで説明しました。