Jenkinsを使った自動テスト環境を作る(後編)――Dockerコンテナを使って自動ビルドを実行する

継続的インテグレーション(CI)ツールとして有名なJenkinsは、ソフトウェア開発におけるテストやビルドと言った作業を自動化するツールだ。後編となる今回は、Dockerを使ってコンテナ内に構築したビルド環境をJenkinsから利用する例を紹介する。

Jenkinsの「マスター/スレーブ」機能

前回記事では、Jenkinsをインストールしたサーバー内でソフトウェアのビルドやテストを行うことを前提に環境を構築していった。Jenkinsをインストールしたサーバーと、対象とするソフトウェアのビルド/実行環境が同じで構わなければこれで問題はないが、たとえばそれぞれビルド/実行環境が異なる複数のソフトウェアをJenkinsで管理したい場合、このやり方では複数台のサーバーを用意しなければならない。

Jenkinsではこういった問題を解決するため、Jenkinsがインストールされたサーバーとは異なるサーバーを遠隔操作し、ビルドやテストをそのサーバー上で実行する機能が用意されている。このとき、Jenkinsがインストールされたサーバーは「マスター」と呼ばれ、ビルドを実行する、遠隔操作される側のサーバーは「スレーブ」と呼ばれる。Jenkinsではスレーブを遠隔操作するためのいくつかの手段を提供しているが、もっとも設定が容易なのはSSHでスレーブにリモートログインして操作を実行するというものだ。この場合、スレーブとして利用するサーバーにSSHでログインさえできれば、それ以外の設定はJenkinsが自動的に行う仕組みになっており、非常に簡単に利用できるのが特徴だ。

さらに、Dockerの連携を行うプラグインも近年活発に開発が進められている。今回紹介する「Docker Plugin」を導入することで、ビルドの実行時に自動的に指定したコンテナを立ち上げ、作業が完了したらコンテナを終了する、といった作業を自動化することが可能だ。

本記事では、まず基本となるJenkinsの「マスター/スレーブ」構成について紹介し、続いてDocker Pluginを利用してDockerコンテナ内でビルドを行う設定について解説する。

Jenkinsのスレーブ設定

Jenkinsがインストールされたサーバー以外のマシンでビルドを行うためには、まずJenkinsにそのマシンをスレーブとして登録しておく必要がある。スレーブの登録は、「Jenkinsの管理」画面内の「ノードの管理」画面で行える(図1、2)。

図1 「Jenkinsの管理」画面
図2 Jenkinsの「ノード」画面

 「ノード」画面では、デフォルトで「master」というノードが登録されている。これはJenkinsをインストールしているサーバーそのものを指している。

新たにスレーブとなるノードを追加するには、画面左側のメニュー内にある「新規ノード作成」をクリックする。すると「新規ノード作成」画面が表示されるので、ここでノード名を入力し、「Permanent Agent」が選択されているのを確認して「OK」をクリックするとノードが登録される(図3)。なお、このときインストールされているプラグインによってはこれ以外の選択肢が表示される場合もあるが、その場合も「Permanent Agent」を選択すれば良い。

図3 「新規ノード作成」画面

 続いてノードの情報を登録する画面が表示される(図4)。

図4 ノードの設定画面

 ここでまず指定しておくべき項目は「リモートFSルート」だ。Jenkinsは必要な各種ファイルをここで指定したディレクトリ以下にコピーするほか、ビルド作業もこのディレクトリ以下で実行される。また、「用途」では「このマシーンを特定ジョブ専用にする」を選択しておこう。これにより、明示的にこのノードを使用するよう指定された場合のみこのノードで作業が行われるようになる。

「起動方法」では「SSH経由でUnixマシンのスレーブエージェントを起動」を選択する。これを選択するとホストや認証情報の設定が可能になるので、「ホスト」にはスレーブとして使用するマシンのIPアドレスもしくはホスト名を、認証情報にはSSHログインに使用する認証情報を指定しておく。認証情報の設定については前回記事で説明しているので、詳しくはそちらを参照して欲しい。

スレーブ側ではこの認証情報に対応するユーザーでSSHログインを行えるよう設定しておこう。この例では、Jenkinsがインストールされたマシン(マスター)上の「jenkins」ユーザーを使ってスレーブにSSHログインが実行される。そのため、スレーブとなるマシン上にもjenkinsユーザーを作成し、また公開鍵認証を利用する場合は公開鍵の登録なども行っておく。

「ラベル」では、ノード指定などに使用できるラベル情報を付与できる。複数のノードを登録している場合、共通のラベルを指定することで空いているノードを自動で選択して使用するといったことが可能だが、ノードの指定はノード名でも可能なので、特に指定しなくても構わない。

設定が完了したら、「保存」をクリックして設定を反映させておく。続いて「ノード」画面を確認すると、追加したノードの状態が表示されるはずだ。

なお、ノードの登録時には指定された認証情報でログインできるかの確認が行われ、ログインに成功したらその場でスレーブに必要な各種ソフトウェアのインストールなどが行われる。これらの作業に失敗した場合、図5のようにアイコンに「×」が表示される。

図5 「ノード」画面でアイコンに「×」が表示されているノードは利用できない状態になっている

 エラーが発生した場合は、そのノードのノード名をクリックし、続けて画面左メニューの「ログ」をクリックすることで、ログを確認できる(図6)。この情報をヒントに、問題点を調べて見ると良いだろう。

図6 ノードを選択し、「ログ」をクリックするとログを確認できる

指定したノードを使用してビルドを行う

続いて、ビルドを行うジョブを開き、追加したノードを指定してビルドを行うように設定を変更しよう。まずジョブの設定画面を開き、ビルドスクリプト内で「node { 」と記述されていた部分を以下のように変更する。

node('<ビルドに使用するノード名もしくはラベル>') {

たとえば先ほど登録した「centos7」というノード上で処理を行うには「node('centos7')」のように指定すれば良い(図7)。

図7 「node」部分に使用するノード名を指定する

 スレーブを利用する設定は以上で完了だ。「保存」をクリックし、続いて「ビルド実行」をクリックして正しくビルドが行えるかどうかを確認してみよう。なお、どのノードを使用してビルドが実行されたかは、各ステージの実行結果右下に表示される(図8)。

図8 どのノードでビルドが実行されたかは、各ステージの実行結果右下に表示される

 もしビルドに失敗した場合、画面左に表示されるビルド結果の「#<数字>」横の「▼」をクリックしてドロップダウンメニューを開き、「コンソール出力」をクリックすると、詳細なログを確認できる(図9、10)。これを参考に原因を調べると良いだろう。

図9 ビルド結果一覧のドロップダウンメニュー
図10 コンソール出力画面

 なお、今回実行したビルドスクリプトは前回紹介したものと同一だ。この中には「SSH Agent」プラグインを使ってSCPでファイルをコピーする処理が含まれているが、このとき使われる認証情報はスレーブ上のものではなくマスター上のものである点には注意したい(マスターに登録されている秘密鍵がスレーブに一時的に送信されて使用される)。そのため、マスター上で適切に認証情報および秘密鍵が登録されていれば、スレーブ上で秘密鍵を用意しておく必要はない。また、ファイルのコピー先サーバーに登録しておく公開鍵もスレーブのものではなくマスター上のものになる。ただし、ホスト鍵のチェックについてはスレーブ上で行われるため、必要に応じてscpコマンドにホスト鍵のチェックを省略する「-o "StrictHostKeyChecking no"」オプションを追加しておくと良いだろう。

「Docker Plugin」を利用したコンテナ内でのビルド

さて、このようにJenkinsでは簡単に別のマシン上でのビルドが実行できるが、これを応用し、Dockerコンテナ上でビルドを行えるようにするプラグインが「Docker Plugin」だ。このプラグインを利用するにはDocker Pluginの導入に加えて、Dockerの設定やビルドに使用するコンテナの準備などが必要となる。続いてはこれらを説明していこう。

なお、DockerとJenkinsを組み合わせる際、Jenkins自身をDockerコンテナ内で稼動させることも可能ではあるが、今回はそれを利用せず、Jenkinsがホスト上で直接稼動している環境を前提とする。

Docker Pluginのインストール

Docker Pluginは、Jenkinsのプラグインマネージャからインストールできる。プラグインマネージャを開き、「利用可能」タブを選択すると利用できるプラグインが一覧表示されるので、「Docker Plugin」にチェックを入れ、「再起動せずにインストール」もしくは「ダウンロードして再起動後にインストール」をクリックすればインストールが行われる。このとき、「フィルター」に「docker」と入力すると探しやすい(図11)。

図11 Docker PluginはJenkinsのプラグインマネージャからインストールできる

Dockerの設定

Docker Pluginを利用するには、Jenkinsをインストールしているホスト上でDockerが利用できるだけでなく、Jenkinsを稼動させているユーザー(一般的には「jenkins」ユーザー)に対し、Dockerを操作できるようにするための権限を与えておく必要がある。

一般的な環境では、dockerコマンドが使用する「/var/run/docker.sock」ファイルに対しパーミッション設定を行うことで、Dockerを操作できるユーザーを制限している。通常、以下のようにrootユーザーおよびdockerグループに属するユーザーのみがこのファイルにアクセスできるようになっているはずだ。

# ls -l /var/run/docker.sock
srw-rw---- 1 root docker 0  6月  9 19:32 /var/run/docker.sock

この場合、次のようにしてjenkinsユーザーをdockerグループに追加することで、jenkinsユーザーがdockerコマンド経由でDockerを操作できるようになる。

# usermod -a -G docker jenkins

なお、docker.sockファイルへのアクセス権限を与えると、そのホスト上のすべてのファイル/ディレクトリへのアクセス権限を与えることになる(ホストのルートパーティションをコンテナ内にマウントすることで、コンテナ経由でroot権限ですべてのファイル/ディレクトリへのアクセスが可能になる)。そのため、サーバーのセキュリティには十分注意してほしい。

ビルドに使用するコンテナの準備

ビルドに使用するコンテナはどのようなものでも構わないが、下記の条件を満たしている必要がある。

  • Java 7以上の実行環境がインストールされている
  • sshd(sshサーバー)がインストールされている

DockerのコンテナイメージをホスティングしているDockerHubではCentOSUbuntuDebianなどの公式イメージを提供しているので、こちらをベースとしても良いし、Packerなどを利用して一からイメージを作成しても良いだろう(Packerに関する過去記事)。

コンテナの作成については本記事の範囲外なので解説は割愛するが、たとえばDebianをベースとする環境であれば、以下のようなDockerfileを使用してコンテナを作成すれば良い。

FROM debian:jessie
RUN apt-get update -y && apt-get upgrade -y

↓Jenkinsスレーブとしての動作に必要なJava実行環境およびsshdをインストールする
RUN apt-get install -y openjdk-7-jdk openssh-server

↓SSH接続で22番ポートを使用することを明示
EXPOSE 22

↓Jenkinsが各種処理の実行に使用する「jenkins」ユーザーを作成する
RUN groupadd jenkins && useradd -d /home/jenkins -g jenkins -m jenkins

↓jenkinsユーザーでログインするための公開鍵をコピー
※あらかじめfiles/id_jenkins.pubとして公開鍵を準備しておく
RUN mkdir /home/jenkins/.ssh
COPY files/id_jenkins.pub /home/jenkins/.ssh/authorized_keys
RUN chown -R jenkins:jenkins /home/jenkins/.ssh \
 && chmod 700 /home/jenkins/.ssh \
 && chmod 600 /home/jenkins/.ssh/*


続けてソフトウェアのビルドやテストに必要なパッケージのインストールや設定を記述する

コンテナを作成したらそのコンテナが正常に起動するか、また実際にSSHでログインできるかどうかを確認しておこう。たとえば作成したコンテナに「osdn/slash-build」というタグを付けておいた場合、以下のようにする。

# su jenkins ←jenkinsユーザーでテストを行う
$ docker run -ti --name slash-build osdn/slash-build /usr/sbin/sshd -D

これで問題なくコンテナが立ち上がれば、続いて別のコンソールからSSHでログインできるかを確認してみよう。

# su jenkins ←jenkinsユーザーでテストを行う
$ docker inspect -f {{.NetworkSettings.IPAddress}} slash-build
192.168.0.2 ←コンテナのIPアドレスが表示される
$ ssh 192.168.0.2

また、この時点でコンテナ内でビルド処理を手動で実行してみて、実際に実行できるかを確認しておくことをおすすめする。

Jenkinsでの設定

続いて、Jenkins側でDockerを利用できるよう設定を行う。まず、「Jenkinsの管理」内の「システムの設定」をクリックする(図12)。

図12 「Jenkinsの管理」内にある「システムの設定」をクリックする

 続いて、表示される「システムの設定」画面内にある「クラウド」以下の「追加」をクリックし、「Docker」を選択する(図13)。

図13 「システムの設定」画面内の「クラウド」で「追加」をクリックし「Docker」を選択する

 すると、Docker向けの設定項目が表示されるので、ここに各種設定内容を追加して行く(図14)。

図14 Docker向けの設定項目が表示される

 最低限入力が必要なのは、「Name」と「Docker URL」だ。「Name」は何でも良いが、ここでは分かりやすいよう「docker」とした。「Docker URL」は、Dockerの操作に使うエンドポイントをURI形式で指定する。今回はホスト上の/var/run/docker.sockファイルをエンドポイントとして使用するので、「unix:///var/run/docker.sock」とした。

ちなみに、Jenkinsが稼動しているホスト以外で稼動するDockerを利用することも可能だ。その場合、ここでは「http://<DockerサーバーのURL>:<ポート>」のような形で、Docker APIを受け付けるURIを指定することになる。これを利用して、以前の記事で紹介したDocker Swarmを使ったDockerクラスタを利用してビルドを行う、といったことも可能だ。

「Connection Timeout」と「Read Timeout」はDockerへの接続時やDockerの出力を読み取る際のタイムアウト時間を指定するものだ。「0」ではタイムアウトなしになってしまうので、適当な値を設定しておいたほうが良いだろう。

続いて、「Add Docker Template」→「Docker Template」をクリックすると「Docker Template」という項目が追加されるので、ここで使用するコンテナを指定する(図15)。

図15 「Docker Template」以下で使用するコンテナの情報を追加する

 ここで設定が必要なのは以下の項目だ。

  • 「Docker Image」:使用するイメージ名を入力する
  • 「Remote Filing System Root」:Jenkinsが各種作業に使用するディレクトリを入力する
  • 「Labels」:ノードを指定する際などに利用するラベルを設定する
  • 「用途」:このノードを利用するための条件を指定する
  • 「Launch method」:Jenkinsとコンテナがどのように通信を行うかを指定する。通常は「Docker SSH computer launcher」を指定し、SSH経由で接続するようにする
  • 「認証情報」:JenkinsがコンテナにSSHでアクセスする際に使用する認証情報を指定する
  • 「Pull strategy」:コンテナの実行前に自動的にコンテナリポジトリからpull操作を行うかどうかを選択する

なお、「Remote Filing System Root」で指定したディレクトリでは「認証情報」で指定したユーザーに対し読み書きアクセスできる権限が与えられている必要がある。

また、今回は特に設定していないが、「Container settings...」をクリックするとコンテナ起動時の設定をより詳しく設定することが可能だ(図16)。

図16 「Container settings...」でより詳細なコンテナ起動設定を行える

 以上の設定が完了したら、「保存」をクリックして設定内容を保存する。これで指定したコンテナをノードとしてビルドに利用できるようになる。

なお、「Add Docker Template」→「Docker Template」をクリックして設定内容を指定する作業を繰り返すことで、複数のコンテナが登録可能だ。

パイプラインの設定

ここまでの作業で登録したコンテナはJenkinsからはスレーブノードとして扱われ、ビルドスクリプト側でコンテナに設定したラベルを「node」命令で指定することで、そのコンテナ内でのビルドが可能になる。

たとえば先に追加したコンテナでは「slash-build」というラベルを指定しているので、ビルドスクリプトの「node」部分を次のようにすることでコンテナでのビルドを実行できる(図17)。

node('slash-build') {
図17 ビルドスクリプトの「node」命令に使用するノードを指定する

 以上の設定が完了すれば、Dockerコンテナを使ったビルドやテストが可能になる。実際にビルドを実行し、正しくビルドが実行できるか確認してみよう。なお、ビルドに使用されたコンテナは作業が完了した時点で自動的に停止される。また、ビルドログ等は実サーバーをスレーブとして利用した場合と同様の手順で確認できる。

マスター/スレーブ構成を取ることでより柔軟な設定が可能に

本記事ではJenkinsのマスター/スレーブ構成について解説を行ったが、このような構成を取ることで、より柔軟にビルド環境を構築できるようになる。また、今回紹介したDocker Plugin以外にも、たとえばクラウドサービス上のホストをスレーブとして利用できる様にするプラグインなども公開されている。

最近ではTravis CIのようなクラウド型のCIツールも広く使われているが(過去記事:GitHubと連携できる継続的インテグレーションツール「Travis CI」入門)、Jenkinsの最大の特徴はさまざまな環境や構成に適用できる柔軟性だ。また、フリーソフトウェアなので導入がしやすく、クローズドなリポジトリと組み合わせても利用しやすいというメリットもある。

サーバーやJenkins自体の管理を自前でやらなければならない、というデメリットはあるものの、一度セットアップを行ってしまえばその後は非常に手軽に自動ビルドなどが利用可能だ。CIを利用してみたいという方は、ぜひ一度使ってみてはいかがだろうか。