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
以下を変更します。
- JENKINS_PORT 空いている適当なポートへ
- 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へ行って登録します。
Bitbucketでも登録します。
この状態でジョブを作ろうとしても、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の画面からフリースタイルプロジェクトを選択し、情報を設定していきます。
この状態で、保存し、ビルドを実行すると、無事Githubからデータが落ちてきます。しかしこれだけでは意味がないので、少し追加設定します。
今回自分はsample-pjというリポジトリを作成して、そこにeclipseのsample-pjプロジェクトを追加しました。そのため、リポジトリの下にsample-pjフォルダがあり、その下にpom.xmlがある状態です。pom.xmlのパスを上記で指定しています。Maven 3.1.11はJenkinsの管理画面から自分がインストールしたMavenを登録しているため、表示されています。
この状態でビルドを実行すると、goal testが走るので、JUnitが実行されます。結果は以下のようになり、正しくテストされていることが分かります。
最後にGithubにpushされたら、ビルドが走る、という設定を行います。
まずJobの設定を変更して、ポーリングをONにします。Git pluginの機能が利用するので必ず設定します。
スケジュールは特に入れる必要はありません。
次にGitHubのSettingsからService Hooks, Jenkins(Git plugin)を選択し、以下のように自分のJenkinsURLを入れます。今回の場合、冒頭でURLを/jenkinsに変更したので以下のようになっています。
これだけで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という項目もあります。
ただし、ここで設定を行っても上記のnotifyCommitへのGETリクエストは送られません。Jenkinsにはもう一つ、認証トークンを使ったビルドの起動方法があって、上記の設定はそれに対応した方法です。なので、今回のケースでは使えません。(※認証トークンを使った方式はとあるJenkinsの認証・認可の仕組み上、よろしくないので推奨しません)
そのため、POSTを使います。BitbucketのHookのPOSTでnotifyCommitへPOSTリクエストを送るようにしてやります。これでGithubと同じ状態にできるため、正しく動きます。
urlはSSHで落とすかHTTPSで落とすかで変わってくるかもしれないので、そこは注意した方が良いかもしれません。
Jenkinsの認証・認可設定
では最後にJenkinsのセキュリティ設定を行って完了です。
Jenkinsの管理からグローバルセキュリティの設定を選び、行列による権限設定を選びます。ここで、adminは全権限、匿名ユーザは権限なし、とします。これでログインしていないユーザを制限できます。
匿名ユーザの権限をなくしても、notifyCommitによるビルドの開始はできるので、安心してください。
以上で終わりです。Jenkinsのインストールからビルドの自動開始まで説明しました。