CentOS に Jenkins と Maven

VirtualBox 上の CentOS 5.6 に、Jenkins と Maven を入れるメモ。WebDAV で共有リポジトリも作る。あちこちのサイトの切り貼り。


以下ほぼ全部 root でやってしまったので、そのまま "#" で書く。たぶん本当は sudo とか使ってもうちょい安全にできるはず。

JDK のインストール

Oracle JDK は下記のサイトにある。

"Download JDK" ボタンを押すとページ遷移。ライセンスに同意して "Accept License Agreement" のラジオボタンを選択した後、"Linux x86 - RPM Installer" のリンクを右クリックし、URL をコピーする。今回は 32 ビット版を使うが、64 ビット版を使う場合は Linux x64 にするなど、自分の環境に合わせて適切なものを選ぶ。
これを書いている時点の最新だと JDK 6 Update 25 なのでこんな感じ。わざわざ -O オプションで保存ファイル名を指定しているのは、ダウンロード中にリダイレクトが行われるため。指定しないとリダイレクト後の名前になってしまう。

# mkdir /tmp/jdk
# cd /tmp/jdk
# wget -O jdk-6u25-linux-i586-rpm.bin http://download.oracle.com/otn-pub/java/jdk/6u25-b06/jdk-6u25-linux-i586-rpm.bin

プロキシを通さないといけない場合、環境変数 http_proxy を指定してから wget を実行する。

# export http_proxy=proxy.example.com:8080

ダウンロードしたら、実行権限を付加してから実行する。

# chmod a+x jdk-6u25-linux-i586-rpm.bin
# ./jdk-6u25-linux-i586-rpm.bin

自己展開とインストールが行われる。展開された rpm ファイルがそのまま残されるので、インストールが終わったら消しておく。

# rm *.rpm

ところで、CentOS 5.6 のインストール中、インストールするソフトウェアの選択で「Java 開発」を選んでいると、Java 1.4.2 相当の gcj がインストールされる。邪魔になるのでアンインストールする。なお、これをアンインストールすると、Java 関連のパッケージが他にもいくつか一緒にアンインストールされる。チラ見した限りでは、別に問題なさそうだったので今回は気にしないことにする。

# yum remove java-1.4.2-gcj-compat

ついで /etc/profile を編集。Oracle JDK は /usr/java/default にインストールされるので、パスを通す。ログインシェルを csh 系にしている場合は /etc/csh.login になるのかな。

export JAVA_HOME=/usr/java/default
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar

確認するにはその場で /etc/profile を実行し、java と javac のバージョンを表示する。

# . /etc/profile
# java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) Client VM (build 20.0-b11, mixed mode, sharing)
# javac -version
javac 1.6.0_25
# 

ほんとはこの後 alternatives という仕組みを使って JDK を切り替えられるように設定するみたいなんだけど、使い方を調べてないので今回はスキップ。
その他の環境でのインストール手順については、上記の JDK ダウンロードページで Installation Instruction を参照。

Jenkins のインストール

Jenkins 公式サイトのトップページで "Red Hat/Fedora/CentOS" のリンクを選択すると、インストール手順の説明ページが表示される。これはダウンロードページを兼ねているようで過去の各バージョンへのリンクもあるが、そこから手動でダウンロードはしない。

まずはページに書いてあるとおりの手順を実行する。

# 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 jenkins

これで /var/lib/jenkins に Jenkins がインストールされた。jenkins ユーザも作成されており、そのホームディレクトリが /var/lib/jenkins になっている。
起動する前に設定が 2 つ。まず、Jenkins が使用する JDK のパスを設定する。デフォルトでも /usr/bin/java などにコマンドがあればそれを使ってくれる。alternatives でシンボリックリンク作成済みであれば問題ないんだけど、今回はそこをスキップしているので代わりにパスを明示的に設定する。手順としては、/etc/sysconfig/jenkins を編集して、

JENKINS_JAVA_CMD=""

となっている箇所を以下のように変更して保存する。

JENKINS_JAVA_CMD="/usr/java/default/bin/java"

最後にもうひとつ必要なのが、ファイアウォールの設定を変更して、Jenkins が使用するポート 8080 を開けること。設定ファイル /etc/sysconfig/iptables を直接編集して以下の内容を追加する (ファイル末尾ではダメ。REJECT とか COMMIT とか書いてある行よりは前でないといけない)。

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT

iptables コマンドを使って追加するのでもいいらしい。ただし、iptables コマンドでの設定変更は再起動で失われるので、続けて以下を実行することで内容を上記の設定ファイルに保存する必要がある。

# service iptables save

なお、service コマンドは /etc/init.d 配下のスクリプトを実行するだけの便利コマンドなので、

# /etc/init.d/iptables save

でも同じことである。また、この場合に限っては iptables-save コマンドを実行することでも保存できる。今回の手順では、基本的には service コマンドを使うことにする。
iptables への設定変更が終わったら再起動する。

# service iptables restart

で、次はいよいよ Jenkins の起動。

# service jenkins start

ブラウザで http://ホスト名:8080 を開いて、Jenkins のダッシュボードが表示されれば OK。「あんまりかっこいいロゴじゃないよねコレ……」とやるせない気持ちになっておく。
Jenkins のシステム設定に進みたいところだけど、それは後回しにして Maven の設定を行う。

Maven のインストール

下記のページから Maven 最新バージョンのリンクをコピーする。

これを書いている時点の最新版は Maven 3.0.3。Maven 3 系のリポジトリは、デフォルトでは Maven 2 系と同じ場所が使われるにも関わらず実は一部互換性がない (Maven 3 がファイルを保存した後に Maven 2 から利用できなくなることがある) らしいので、まだ Maven 2 現役という場合は要注意。
今回は最新の 3.0.3 を使いたいので、そのままゴー。と思ったんだけど、リンクをコピーしてそのまま以下のようにしたらダメだった。

# wget http://www.apache.org/dyn/closer.cgi/maven/binaries/apache-maven-3.0.3-bin.tar.gz

これだと、ミラーサイト選択ページに飛ばされて、HTML が保存されてしまった。中に書かれているミラーからひとつ選んでやり直し。今回は、KDDIW3C 加入を記念して ftp.kddilabs.jp を使うことにした。ダウンロードしたらアーカイブを展開して任意の場所に置く。
どこに置くかは人によって違うようで、Maven のダウンロードページ下部にあるインストール手順説明では /usr/local/apache-maven/apache-maven-3.0.3 が例として挙げられている一方、/usr/share に入れている人も多いようだ。例えば Mac OS X 10.6 (Snow Leopard) の場合、/usr/share/java/maven-3.0.2 に Maven が入っていて、/usr/share/maven からシンボリックリンクが張ってある。迷った末、今回は /usr/share 直下に入れて、/usr/share/maven からシンボリックリンクを張っておくことにした。

# wget http://ftp.kddilabs.jp/infosystems/apache//maven/binaries/apache-maven-3.0.3-bin.tar.gz
# tar xvzf apache-maven-3.0.3-bin.tar.gz
# mv apache-maven-3.0.3 /usr/share
# ln -s /usr/share/apache-maven-3.0.3 /usr/share/maven

再び /etc/profile を編集して、以下の内容を追加する。環境変数 M2 はなくてもいいような気がするけど、公式サイトのインストール手順に設定するよう書かれているので、一応入れておくことにする。

export M2_HOME=/usr/share/maven
export M2=$M2_HOME/bin
export PATH=$M2:$PATH

確認するにはその場で /etc/profile を実行してバージョンを表示する。

# . /etc/profile
# mvn --version
Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900)
Maven home: /usr/share/maven
Java version: 1.6.0_25, vendor: Sun Microsystems Inc.
Java home: /usr/java/jdk1.6.0_25/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "2.6.18-238.9.1.el5", arch: "i386", family: "unix"
# 

プロキシを通らないとインターネットに出られない場合は、ここで $M2_HOME/conf/settings.xml にプロキシの設定を追加しておく。

Maven リポジトリの作成 Part 1 - ディレクトリの作成

Maven のデフォルトでは、central リポジトリからダウンロードしたファイルをローカルリポジトリにキャッシュするという 2 段構成になっているが、チームで開発する場合にはチーム内で共有するためのリポジトリを用意すると便利だという噂。作り方には色々あるみたいだけど、今回は Apache HTTP Server で WebDAV 使って書き込める場所を作るというオーソドックスなやつでいく。ちなみに他には、例えば jenkins ユーザのローカルリポジトリをそのまま HTTP で公開してしまうという手法があるらしい。
まず、リポジトリとして使うディレクトリを作る。これもどこに置くかは人によるみたいで、今回はよくわからないまま /var/maven3/repo にすることにした。これを http://<ホスト名>/maven3/repo として公開する。

# mkdir -p /var/maven3/repo
# chown -R apache:apache /var/maven3

で、世のサイトを色々見ていると、これで終わりというか、chown や chmod を使ってオーナや権限を変更すれば OK と説明されているんだけど、CentOS の場合はちょっと事情が違う。
CentOS ではデフォルトで SELinux が有効になっていて、そっちにしたがって設定をしないと意味がない。僕はここがよくわかってなくて、2 時間ほどハマった。Apache の設定をすませたつもりなのに、ブラウザで見ると "403 Forbidden" って言われるし、ログ (/etc/httpd/logs/error_log) には "Permission denied: Can't open directory for index" って出ていて、パスの指定の仕方とかを色々変えても状況が変わらず泣きそうになった。
まず、/var/maven3/repo ディレクトリを作成した直後の状態で、/var/maven3 が SELinux 的にどういう状況になっているかを確認してみる。比較対象として、Apache の文書が置かれるデフォルトのパスである /var/www と合わせて確認してみたのが以下の出力。

# ls --context /var | grep maven3
drwxr-xr-x  apache apache user_u:object_r:var_t            maven3
# ls --context /var | grep www
drwxr-xr-x  root root  system_u:object_r:httpd_sys_content_t www

www には system_u:object_r:httpd_sys_content_t という「ラベル」が付けられている一方、maven3 の方は user_u:object_r:var_t という状態になっている。この違いがあるために、chown や chmod だけやっても Apache からの参照が禁止される (言い方を変えると、Web で公開されるつもりのない文書が Apache 経由で外に見えてしまうのを防いでいる) ので、このままだと 403 Forbidden になってしまう、ということらしい。
そこで、chcon コマンドを使って、maven3 の方にも www と同じ「ラベル」を設定しておく。実行後に再度状態を確認すると、www と同じになっていることがわかる。

# chcon system_u:object_r:httpd_sys_content_t /var/maven3 -R
# ls --context /var | grep maven3
drwxr-xr-x  apache apache system_u:object_r:httpd_sys_content_t maven3
#

正直、SELinux の仕組みをちゃんと理解せずにやっているので、これでセキュリティ的に問題ないのかは不明。

Maven リポジトリの作成 Part 2 - httpd の設定

/etc/httpd/httpd.conf を編集する。WebDAV を使うためにはモジュール dav_module と dav_fs_module を有効にする必要がある。デフォルトで有効になっているみたいだけど、念のため以下の行がコメントアウトされていないことを確認しておく。

LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so

ドキュメントとかネットの解説を見るとこの他に dav_lock_module を有効にしろと書かれていることが多いんだけど、今回は必要ないみたいだ。Apache では WebDAV のバックエンドをいくつかの選択肢から選ぶことができるそうで、そこに汎用のロック機能を提供するのが dav_lock_module なんだけど、バックエンドのひとつである dav_fs_module には独自のロック機能があるので dav_lock_module を使用しない。
続いて、ファイル末尾あたりの適当な場所に以下の内容を追加。リポジトリを外部に公開した上で、WebDAV を有効にする。

Alias /maven3/repo/ "/var/maven3/repo/"

<Directory "/var/maven3/repo">
    Options Indexes
    AllowOverride None
    Order allow,deny
    Allow from all
    Dav On
</Directory>

なお、今回は認証をかけていないので、誰でも自由に読み書きできる状態になっている。必要であれば、書き込み時には認証を要求するようにここで設定しておく。以上が終わったら httpd.conf を保存する。
Apache HTTP Server のポートはデフォルトでは 80 なので、Jenkins のときと同様にファイアウォールの設定を変更してポートを開放する。/etc/sysconfig/iptables を再び編集して、以下の内容を追加する。

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

そして、同じように iptables の再起動を行った後、Apache HTTP Server の起動を行う。

# service iptables restart
# service httpd start

ここで、ブラウザで http://<ホスト名>/ や http://<ホスト名>/maven3/repo を開いたり、WebDAV クライアントで http://<ホスト名>/maven3/repo につないで更新できるか確認しておく。WebDAV で普通にファイルを更新できた時は、ちょっと嬉しかった。ちなみに、WindowsMac OS X の場合、WebDAV クライアントは OS に付属している。

  • Windows では、http://<ホスト名>/maven3/repo をネットワークプレースとして登録することで、普通のファイル共有サーバと同じようにエクスプローラ上でファイルの参照・更新が行える。WebDAV サーバ側で認証を設定していなくて、Windows XP から接続する場合、URL の末尾に '?' を付けて http://<ホスト名>/maven3/repo? として登録しないと、なぜか延々 ID・パスワードの入力を求められるようだ。バグ?
  • Mac OS X では、Finder のメニューで「移動」→「サーバへ接続」を選択し、http://<ホスト名>/maven3/repo を入力して「接続」ボタンを押せば OK。

ところで、デフォルトでは httpd自動起動は無効になっている。以下を実行することで、OS を再起動すると httpd が自動的に起動するようになる。

# chkconfig httpd on

Maven リポジトリへの配備 Part 1 - サードパーティ製ライブラリ

作成したリポジトリに、WebDAV 経由でサードパティ製のライブラリを入れたい場合がある。ライセンス上の理由で central リポジトリには置かれていない Oracle JDBC Driver とか。
これには、以下のコマンドを実行すれば、配備できるはず……なんだけど、実はエラーになってしまう。

$ mvn deploy:deploy-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.1.0 -Dpackaging=jar -Durl=dav:http://<ホスト名>/maven3/repo -Dfile=<jarファイル>

回避する方法は、以下のページにあった。

つまり、カレントディレクトリに、以下の内容で pom.xml を作成してから上記のコマンドを実行すれば配備できる。この POM ファイルは、WebDAV Wagon (WebDAV での配備を行うための拡張機能) を有効にするためだけのものなので、ファイル中の groupId や artifactId を修正する必要はない。実際にどう配備されるかは、あくまで deploy:deploy-file 実行時のオプションで決まる。

<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/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.example</groupId>
   <artifactId>webdav-deploy-pom</artifactId>
   <packaging>pom</packaging>
   <version>1</version>
   <name>Webdav Deployment POM</name>

   <build>
      <extensions>
         <extension>
            <groupId>org.apache.maven.wagon</groupId>
            <artifactId>wagon-webdav</artifactId>
            <version>1.0-beta-2</version>
         </extension>
      </extensions>
   </build>
</project>

それともうひとつ、指定するリポジトリ URL は、先頭に dav: を付ける必要があることに注意。

-Durl=dav:http://<ホスト名>/maven3/repo
×
-Durl=http://<ホスト名>/maven3/repo

Maven リポジトリへの配備 Part 2 - 自分のプロジェクト

自分が開発しているプロジェクトを共有リポジトリに配備できるようにするには、プロジェクトの POM に設定を追加する。前項と同じように WebDAV Wagon を使うための設定と、配備先リポジトリの情報という 2 つが必要。
依存関係とかが何もない最小の pom.xml の場合、以下のようになる。リポジトリの ID は、認証を使わない場合は何でもいい。名前は説明用で、レポートに出力されるくらいだろうからなおさら何でもいい。

<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>foo</groupId>
	<artifactId>bar-baz</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<build>
		<extensions>
			<extension>
				<groupId>org.apache.maven.wagon</groupId>
				<artifactId>wagon-webdav</artifactId>
				<version>1.0-beta-2</version>
			</extension>
		</extensions>
	</build>
	
	<distributionManagement>
		<repository>
			<id>oursharedrepo</id>
			<name>Our Shared Maven Repository</name>
			<url>dav:http://ホスト名/maven3/repo</url>
		</repository>
	</distributionManagement>
</project>

Jenkins の設定

(TODO)