Ubuntu Weekly Recipe

第379回Jujuを使ってJenkins環境を構築する

JenkinsはJavaで作られた継続的インテグレーションツールです。今回はこのJenkinsのマスター/スレーブ環境をJujuを用いて構築する方法をご紹介します。

Jenkinsについて

Jenkinsは現代のソフトウェア開発においてほぼ必須とも言える継続的インテグレーションにおける標準的なツールの1つなので、実際に使っている方も多いでしょう。よって今更説明する必要もないかもしれませんが、せっかくなので概要だけでも書いておきます。

Jenkinsは継続的インテグレーション(CI:Continuous Integration)を実現するためのJava製のサーバーソフトウェアです。オンプレミスでも構築できるFLOSSで高機能なCIツールとして、世界中で幅広く使われています。UbuntuでもQAチームがAutoPkgTestなどの自動化に利用しています。

Jenkinsのインストール方法

Jenkinsのインストール方法は、JenkinsのWikiにまとめている方法が一番参考になるでしょう。

最近のソフトウェアと同様にJenkinsも、短いリリース周期を採用しています[1]⁠。しかしながら週一という頻度でソフトウェアがリリースされると、Ubuntuの公式リポジトリ上で互換性やセキュリティ対応を維持したままLTSの期間サポートを続けることが非常に難しくなってきます[2]⁠。

そこでUbuntu 14.04 LTSからは公式リポジトリからJenkinsパッケージが削除されました。Jenkinsを使う場合は、JenkinsのWikiにもあるようにJenkinsが用意するリポジトリからインストールすることが推奨されています。

$ wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
$ sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
$ sudo apt-get update
$ sudo apt-get install jenkins

同じWikiに掲載されているもう1つの方法が、今回紹介するサービスモデリングツールであるJujuを使う方法です。といってもJujuのCharm(構築スクリプト)が行っている「Jenkinsのインストール」自体は、上記のコマンドと同じでJenkinsのリポジトリを追加してパッケージをインストールするだけです。

Jujuの方法を使うメリットの1つは「Jenkinsをインストールするマシンを用意する」ことが簡単になるということです。つまりAmazon EC2なりWindows AzureなりGoogle Compute EngineなりLXC……といったさまざまな環境にUbuntuをインストールする部分から自動化できます。Jenkinsをマスター/スレーブ構成にして、スレーブをどんどん増やしてスケールしたい場合、OSのインストールを省けるのは大きな作業簡略化になるでしょう。

そもそもJujuはWindows/OS X用のクライアントソフトウェアも提供しているため、操作側にUbuntuを用意する必要すらありません。

しかもこれらのクラウドサービスやベアメタルに応じて操作方法を変える部分はJujuが吸収してくれるため、使うサービスによって操作方法を覚え直す必要もないのです。今回はAmazon EC2を使って説明しますが、Windows Azureを使う場合であっても初期設定(APIキーの設定)だけ変えれば、あとは同じコマンドを「コピペ」すれば動きます[3]⁠。

というわけで今回はJujuを使ってJenkinsのマスター/スレーブ環境を構築します。さらにJenkinsのGitHub PullRequest Builder Pluginをインストールして、PullReqがきたらJenkinsにビルドジョブを走らせるようにしてみましょう。

Jujuのインストール

Jujuのインストールについては、これまでにも何度説明してきました。JujuそのものはJujuのコアサービス(jujudとMongoDBの組み合わせ)とそれを操作するためのクライアントからなります。コアサービスについてはJujuクライアントでインストールできるので、実質的にユーザーが明示的にインストールする必要があるのはJujuクライアントだけになります。

先ほども述べたように、JujuクライアントはUbuntuだけでなくWindowsやMac OS X向けのクライアントもあります。テクニカルプレビューという位置づけではありますが、CentOS向けのクライアントも存在します。各自の環境に合わせたクライアントをインストールしてください。ちなみにJujuのコアサービスにアクセスさえできれば、2つの異なるマシンにクライアントをインストールして操作することも可能です。

ここではUbuntu上にクライアントをインストールしてみます。Jujuそのものもまた、Ubuntuのリリースサイクルとは関係なくアップデートが進むプロダクトですので、UbuntuにクライアントをインストールするならPPAを使ったほうが良いという状態には変わりありません。

$ sudo apt-add-repository ppa:juju/stable
$ sudo apt-get update

# クラウドやVagrantなどにサービスをインストールする場合
$ sudo apt-get install juju

# ローカルマシンのLXC上にサービスをインストールする場合
$ sudo apt-get install juju-local

ローカルマシン上でLXCだけでサービスを構築する場合は、juju-localパッケージをインストールしてください。

Jujuクライアントがインストールできたら、初期設定です。まずSSH鍵を作成します。既に持っているのであれば、この処理はスキップできます。

$ ssh-keygen -t rsa -b 4096

設定ファイルのテンプレートを生成します。Ubuntuであれば「~/.juju/environments.yaml」にファイルが作られます。

$ juju generate-config

Jenkinsサービスを展開したいクラウドサービスに応じて、⁠~/.juju/environments.yaml」を書き換えます。サポートしているサービスごとの詳しい手順はJujuの設定ドキュメントを参照してください。Amazon EC2であれば、region、access-key、secret-keyあたりを設定するだけです。

amazon:
    type: ec2
    region: ap-northeast-1
    access-key: <secret>
    secret-key: <secret>

上記のうち最初の行が環境名です。任意の文字列を設定できます。typeはサービスのタイプです。Amazon EC2を使うならec2ですし、Google Compute Engineならgce、LXCを使うならlocalになります。type以降の設定は、typeの種類に依存します。ec2であれば、regionとAPIキー辺りを設定します。access-key/secret-keyは設定していない場合、環境変数のAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYを参照します。

次に標準で使用する環境名を設定します。

$ juju switch amazon

最後にbootstrapコマンドで、Jujuのコアサービスをセットアップしましょう。クラウドサービスを使う場合、このbootstrapコマンドからインスタンスの生成が発生する、つまりサービスによっては使用料が発生するということに注意してください。

$ juju bootstrap
Bootstrapping environment "amazon"
Starting new instance for initial state server
Launching instance
 - i-4553a7b7
Installing Juju agent on bootstrap instance
Waiting for address
(中略)
Bootstrapping Juju machine agent
Starting Juju machine agent (jujud-machine-0)
Bootstrap agent installed
Waiting for API to become available
Bootstrap complete

bootstrapは最初に新しいUbuntuインスタンスを立ち上げ、そこにJujuのコアサービスなどをインストールします。そのため若干時間がかかります[4]⁠。インストールの状況はstatusコマンドで確認できます。

$ juju status
environment: amazon
machines:
  "0":
    agent-state: started
    agent-version: 1.24.0
    dns-name: AA.BB.CC.DD
    instance-id: i-4553a7b7
    instance-state: running
    series: trusty
    hardware: arch=amd64 cpu-cores=1 cpu-power=100 mem=1740M root-disk=8192M availability-zone=ap-northeast-1a
    state-server-member-status: has-vote
services: {}

Jenkinsのデプロイ

では、さっそくJenkinsをデプロイしましょう。Jujuはサービスをデプロイするためのスクリプトを「Charm」と呼びます。JujuのサイトでJenkinsを検索するとJenkins関連のCharmが見つかります。現在のCharmは12.04(precise)用と14.04(trusty)用が混在している状況です。14.04用がある場合はできるだけそれを使うほうがいいのですが、共に表示されるダウンロード数や作成者なども参考にどれを使うか決めてください。

今回はCharmerstrusty/jenkins-4を使用します。

$ juju deploy jenkins

juju statusで確認すると新たにUbuntuインスタンスが立ち上がり、Jenkinsをインストールしようとしていることがわかります。ちなみにdeployコマンドに「--to 0」を与えると番号で示したマシン(0なら最初に作ったブートストラップノード)を指定できます。unitsにあるjenkins/0ユニットのagent-stateが「started」になったら準備完了です。

Jenkinsの管理者パスワードを設定した上で、Jujuのサービスを外部に公開しましょう。

$ juju set jenkins password=gihyojp
$ juju expose jenkins

「http://(juju statusのpublic-address):8080/」にアクセスすることで、Jenkinsの画面が表示されるはずです。管理者のアカウント名は「admin」です。これは「juju set jenkins username」で変更できます。

GitHub Pull Request Builder Pluginのインストール

Jenkins Charmの便利な点は、コマンドラインからプラグインをインストールできるところです。さっそくGitHub Pull Request Builder Pluginとそれに必要なプラグイン一式をインストールしましょう。

$ juju set jenkins plugins="scm-api git-client git github-api github ssh-agent plain-credentials ghprb"

ちなみにpluginsの設定は、⁠列挙したプラグインをインストールする」になっています。追加で新しいプラグインをインストールしたい場合は、前のプラグインを再び列挙する必要はありません。逆に列挙したものだけインストールし、列挙していないものはアンインストールしたい場合は、あらかじめ「juju set jenkins remove-unlisted-plugins=yes」を設定しておいてください。

プラグインに足りないものがあったために、Jenkinsがエラーを出力しているかもしれません。そんなときはJenkinsが動いているユニット「jenkins/0」にSSHログインして/var/log/jenkins/jenkins.logを確認することが一番確実でしょう。

$ juju ssh jenkins/0
ubuntu$ less /var/log/jenkins/jenkins.log
ubuntu$ exit

必要なプラグインを追加で「juju set jenkins plugins」で指定したうえで、⁠juju resolved jenkins/0」を実行するとプラグインの追加インストールが行われます。

うまくいけば「Jenkinsの管理」にある「プラグインの管理」「インストール済み」に指定したプラグインがリストアップされます。

スレーブノードの追加

Jenkinsの準備が整ったので最初のジョブの作成、といきたいところですが、先にスレーブノードを作ってしまいましょう。これはビルドに必要なパッケージをすべてスレーブノードのみにインストールし、マスターノードはただジョブのハンドリングのみを担当させるようにするためです。

Jenkinsのスレーブは別途Charmが存在します。ただCharmersのJenkinsはprecise用しか存在しません。そこでここではtrusty用も存在するJuju GUI Charmersのjenkins-slaveを使いましょう。

$ juju deploy cs:~juju-gui-charmers/trusty/jenkins-slave jenkins-slave
$ juju add-relation jenkins jenkins-slave

この状態でJenkinsの管理画面を見るとスレーブの起動に失敗した旨のエラーメッセージが表示されると思います。これはスレーブを起動するためのシークレットキーがスレーブ側に設定されていないためです。そこでまずスレーブの「設定」から「起動方法」「JNLP経由でスレーブを起動」に変更して「保存」ボタンを押します。次に「状態」を選択して「-secret」とその後ろの文字列を控えます。

今度はjenkins-slave/0ユニットにログインしてください。/etc/default/jenkins-slaveのJENKINS_ARGSの末尾に、先ほど控えた-secretを追加します。

$ juju ssh jenkins-slave/0
ubuntu$ sudo vi /etc/default/jenkins-slave
JENKINS_ARGS="-jnlpUrl $JENKINS_URL/computer/$JENKINS_HOSTNAME/slave-agent.jnlp -secret 文字列"
ubuntu$ sudo restart jenkins-slave
ubuntu$ exit

ついでにマスターのほうの設定から「同時実行数」を0に設定して、マスターノードではジョブが起動しないようにしておきます。

スレーブノードではビルドジョブを走らせます。このためビルドに必要なソフトウェアが一通り必要です。パッケージとしてインストールできるソフトウェアであれば、jankins-slave charmのtoolsオプションを利用できます。

$ juju set jenkins-slave tools="ruby zip"

apt-getでインストールできないソフトウェアはsshログインしてインストールするという手もあります。

$ juju ssh jenkins-slave/0 sudo gem install review

ただしこのような「環境構築」は本来自動化すべきものです。jenkins charmもjenkins-slave charmも、基本的にはソースコードのブランチを作成し、特定のビルド環境用のCharmを作る雛形が揃っていますので、一通り動作確認などをしたら、次はCharmをカスタマイズすることも考えてください。

具体的には次のようにJujuのクライアントを動かしているマシン上にブランチを作成し、カスタマイズしたCharmをデプロイするだけです。

$ bzr branch lp:charms/jenkins-slave
jenkins-slave/hooks/install.d/以下にインストールスクリプトを追加する
$ juju deploy --repository=. local:jenkins-slave

jenkins-slave/hooks/installの内容を見てもらえればわかると思いますが、install.d以下のスクリプトがsourceされることで駆動します。また、このスクリプトは設定を変更した時にも実行されるので注意してください。もちろんCharmのリポジトリがGitであれば、bzr branchの部分はgit cloneになります。

ジョブの作成

ビルドの下準備は一通り揃ったので、最後はジョブの作成です。といってもここから先はただのJenkinsの設定なので、かんたんに概要だけを説明していきましょう。

GitHubからクローンする際にSSH鍵を使う場合は、あらかじめマスターノードで作っておきましょう。

$ juju ssh jenkins/0
ubuntu$ sudo -i -u jenkins
jenkins$ ssh-keygen -t rsa -b 4096
jenkins$ exit
ubuntu$ exit

マスターノードで作っておけば、スレーブノードでも鍵を利用できるようにJenkinsがうまくとりはからってくれます。Jenkinsの管理の「認証情報の管理」から「認証情報の追加」「SSHユーザー名と秘密鍵」を選択し、⁠Jenkinsマスター上の~/.sshから」を選択して保存ボタンを押しておきます。

「新規ジョブの作成」では「フリースタイルプロジェクトのビルド」を選びます。あとはGitHub Pull Request Builder PluginのWikiに従って設定します。

  • ソースコードの管理はGitに変更
  • Repository URLに「git@github.com:user/repository.git」などを設定
  • Credentialに先ほど追加した「認証情報」を設定
  • 高度な設定のRefspecに「+refs/pull/*:refs/remotes/origin/pr/*」
  • Branch Specifierに「${sha1}」
  • ビルド・トリガでGitHub Pull Request Builderをチェック
  • Webhookを使わない場合は、Crontab lineに「H/5 * * * *」などの周期を設定

リポジトリの設定権限があるのであればCrontabのかわりにGitHubのWebhooksを使う手もあります。ただCrontabであればより汎用的にいろいろなサービスでも利用できます。

「ビルド」「シェルの実行」ではリポジトリをクローンし、クローンしたディレクトリに移動した状態からスクリプトを開始できます。最後に保存ボタンを押せば設定は完了です。実際にリポジトリにPull Requestを送って、ジョブが開始することを確認しましょう。

まとめ

以上、駆け足になってしまいましたが、Jujuを使ってJenkinsのマスター/スレーブ環境を構築できました。

今回はスレーブノードが1つだけでしたが、この先スレーブを10台増やしたい場合は以下のコマンドを実行するだけで実現できます。

$ juju add-unit -n 10 jenkins-slave

いくつか制約はあるものの、Jujuの上でDockerを使うことも可能です。うまく組み合わせれば毎回Dockerインスタンスを構築・破棄することで、常にクリーンな環境でのビルドジョブを走らせることも実現できますし、ビルドジョブが成功したらDockerインスタンスを作成して そこにデプロイするといったことも実現できるでしょう。

Jujuは「サービスモデリングツール」と名乗っているとおり、ユーザーが実現したいサービスをモデリングし、必要なコンポーネントを選択し、コンポーネント間の関係を設定するためのツールです。今回の場合ですと「GitHubのPull Requestを自動ビルドする」というサービスのために、⁠Jenkins」というコンポーネントを選択し、2つのインスタンスを「マスターとスレーブ」という関係に設定しました。

このときJujuのユーザーは「Jenkinsそのもののインストール方法」「Jenkinsをインストールするホストの構築」には関知しません。それはあくまでCharmを書く人(究極的にはソフトウェアやアプライアンス、ソリューションなどからなるコンポーネントそのものの開発者)の仕事なのです。

このように分業することで、Jujuを使うユーザーは要求を満たすコンポーネントを選び、それを使うことに注力できます。理想的には。たぶんできると思います。そのうち。もしJujuがそれなりに成功すれば。とにかく「何かのサービスをインストールする」という本来サービスを使うだけなら必要ない作業を、その作業手順を一番わかっている人にやってもらおうというのがJujuの目指しているゴールの一つです。

「わかっている人」にCharmを作ってもらうためにも、Charmにはできるだけ作る人の裁量にあわせて好きな開発言語を使えるようにし、他のCharmから提供されるサービスを再利用できるような仕組みになっています。もしあなたが何か複数のソフトウェアから構成される複雑なサービスの構築方法に詳しいのであれば、その構築手順に詳しくないユーザー(もちろん未来のあなたを含みます)のためにも、ぜひ一度そのサービスのCharmを作成しCharm Storeで公開してください。

おすすめ記事

記事・ニュース一覧