日本語 : Distributed builds

オリジナル:Distributed builds

Jenkinsは"マスター/スレーブ" モードをサポートしています。このため、プロジェクトビルドのための作業負荷を、複数の"スレーブ"ノードに分割して割り当てることが出来ます。さらに、1つのJenkins上で、非常に多くのプロジェクトを管理することも、テスト / ビルドのために必要な異なる環境を提供することも可能です。

このドキュメントは、"マスター/スレーブ" モードと、その利用方法について解説します。

Contents

どういう仕組みで動くの?

インストールされたJenkins自身は、"マスター" となります。
利用しているJenkinsが、"マスター/スレーブ" モードをサポートしていなければ、そのJenkinsがマスターそのものになります。また、"マスター/スレーブ" モードであっても、マスターとして担う役割には変わりありません。
マスターは、すべてのHTTPリクエストを受け付け、マスター自身に割り当てられたプロジェクトのビルドを担当します。

マスターのために、プロジェクトをビルドできるようにセットアップされたコンピュータが、スレーブとなります。Jenkinsは "slave agent" という独立したプログラムを、スレーブの上で走らせます。スレーブエージェントを起動するためには、さまざまな方法がありますが、最終的には、スレーブエージェントとJenkinsのマスター間で、双方向のバイトストリーム通信を確立することが必要となりす。(たとえば、TCP/IPソケット通信など)

スレーブがマスターに登録されると、マスター/スレーブ間でのビルドの負荷分散が開始されます。
実際にスレーブ上でジョブが実行されるかどうかは、プロジェクトの設定次第になります。ビルドのために、特定のマシン上に張り付いていないといけないプロジェクトもあれば、自由にスレーブの場所を選んでビルドできるプロジェクトもあります。
Jenkinsのウェブサイトにアクセスすると、透過的にビルドが行われていることが分かります。
また、スレーブによるビルドが完了したことは全く知らなくとも、javadocやテスト結果、ビルドの成果物をマスターからダウンロードすることが出来ます。

次のステップ Step by step guide to set up master and slave machines では、分散ビルドを利用するためのクイックスタートについて解説します。

スレーブエージェント起動の様々な方法

皆さんが利用している環境と、マスターとスレーブが稼動するOSにとって、適切な方法を選んで下さい。

ssh経由でマスターからスレーブエージェントを起動する

JenkinsはビルトインのSSHクライアントを備えており、リモートのsshdと通信し、スレーブエージェントを開始することが出来ます。これは、Unixのスレーブに対しては、もっとも便利でお勧めの方法です。というのも、たいていのUnixでは、何もしなくとも、そのままでsshdが利用できるからです。
『Jenkinsの管理』をクリックし、『ノードの管理』に進んで、"新規ノード作成"をクリックします。ここでは、接続情報を設定します。(スレーブのホスト名や、ユーザ名、sshの資格情報です) なお、スレーブの ~/.ssh/authorized_keys に、マスターの公開鍵をコピーする必要がありますので、ご注意下さい。(sshについてのヘルプは、適切なHowto がこちらにあります。)
設定が済むと、スレーブエージェントに必要なバイナリのコピーも含め、スレーブの起動から停止といった残りの作業を、Jenkins自身が行ってくれます。
ただし、プロジェクトに外部の依存関係がある場合は、自分自身で設定を行う必要があります。[Where is this documented?]

Unixにとっては、この方法がもっとも便利です。

マスターからWindows上のスレーブエージェントを起動する

Windowsスレーブの場合は、Jenkinsは、Windows2000以後のバージョンにビルトインとなっている、リモート管理の機能を利用できます。(詳しい仕様は、こちらを参照して下さい。WMI+DCOM)
この場合は、システムの管理者権限を持ったユーザ名とパスワードを設定すれば、JenkinsがリモートからWindowsサービスを作成し、リモートからの開始/停止を行います。

この方法は、Windowsにとってはもっとも利用しやすいものですが、画面上の対話が必要なプログラムを起動することは出来ません。(たとえば、GUIテストなど)

注意 : 他のノード設定と違い、ノード名の設定が非常に重要になります。ノード名を、サービスを作成するための実際のアドレスとして利用するからです !

自分でスクリプトを作成し、Jenkinsのスレーブを開始する

もし、上記のように標準で提供されている方法では、柔軟性の要件を満たせない場合、自分自身でスレーブを起動するスクリプトを書いて対応することもできます。

典型として、SSHやRSH、その他類似の方法のような、リモートプログラム実行のためのメカニズムを、スクリプトに利用します。(Windowsではcygwinpsexecを通して、同じプロトコルを利用することができます。) ただし、Jenkins自身が、接続のための特定の方法を持っているわけではありません。

Jenkinsがスクリプトに求めているものは、結局のところ、 java -jar slave.jar のように、適切なコンピュータ上でスレーブエージェントプログラムを実行することであり、コンピュータ上の標準入出力を、スクリプトの標準入出力につなげることです。例えば、 "ssh myslave java -jar ~/bin/slave.jar" というスクリプトが、これに該当します。
(Jenkinsは、標準入出力をスレーブエージェントとのコミュニケーションチャンネルとして利用するので、Jenkins自身にこのコマンドを実行させることが重要となります。このため、シェルから手動でスレーブエージェント実行することは、あまりお勧めではありません。).

slave.jar のコピーは、http://yourserver:port/jnlpJars/slave.jar から、スレーブ上にダウンロードすることが出来ます。
実際、スクリプト中に、この160KB程度のjarファイルをダウンロードするコードを記載する例も多く見られます。これは、確実に、(マスターとの間で)一貫性を持ったバージョンの slave.jar を利用するためのものです。SSH Slavesプラグインでは、この動作を自動で行っており、SSH Slavesプラグインを利用して設定されているスレーブは、常にマスターのJenkinsと整合性の取れた slave.jar を利用することができます。

slave.jarのアップデートについて

技術的な観点から言うと、スレーブのセットアップにおいては、Jenkinsのバージョンを新しくする度に、 slave.jar もアップデートされるべきです。しかし、実際は、 slave.jar を頻繁に更新する必要は無いので、起動時にプログラムが深刻な問題を引き起こさない限りは、更新しないでおく、というのが現実的なところです。

この方法によるスレーブの起動では、たいていスレーブ上での追加の初期設定が必要になります(特にWindowsでは、リモートログインのメカニズムはデフォルトでは利用できません)。ただし、このアプローチのメリットは、コネクションの悪化(切断)が発生しても、JenkinsのWebインタフェースを利用して、コネクションを再開できる、という点です。

Java Web Startを利用してスレーブエージェントを起動する

スレーブエージェントを起動するもう1つの方法が、Java Web Start (JNLP) を利用することです。このアプローチでは、まず対話的にスレーブノードにログインし、ブラウザを開き、Jenkins上のスレーブのページを開きます。次に、JNLPのラウンチアイコンが表示されたら、そのアイコンをクリックすると、Java Web Startが呼び出され、ブラウザが実行されているコンピュータ上に、スレーブエージェントが起動されます。
このモードは、カスター側からスレーブへのコネクションを開始できないような状況、例えば、マスターがファイアウォール外で稼動しているが、その他のスレーブはファイアウォールの中にある、といった状況下で、利便性の高い方法です。

Windowsでは、いったん手動でJNLPを利用してスレーブエージェントを起動すると、Windowsのサービス としてエージェントをインストールできます。このため、スレーブを対話的に起動しなおす必要はなくなります。

Windows上で、画面上の対話 (GUIテストなど) が必要で、専用の(仮想の)テストマシンが用意できるなら、それにふさわしいオプションがあります。
Jenkinsのユーザアカウントを作成し、自動ログインを有効化し、スタートアップアイテムの中にJNLPへのショートカットを作成します。(信頼されたスレーブエージェントの証明書を取得した後で)

ヘッドレスな環境でのスレーブエージェントの起動

このラウンチモードは、GUIを利用せずに稼動させるという点を除いて、Java Web Startと非常に良く似たメカニズムを利用します。このモードは、Unix上のデーモンのようにエージェントを実行させる場合に便利な方法になります。
このスレーブをJNLPスレーブとして設定するために、まずは上記で解説した通り、 slave.jar を取得します。次に、このようなコマンドを実行します:

$ java -jar slave.jar -jnlpUrl http://yourserver:port/computer/slave-name/slave-agent.jnlp

"slave-name" は、実際のスレーブ名に合ったものに置き換えて下さい。

Other Requirements / その他必要事項

スレーブはクラスタの一種であり、クラスタを運用することは、non-trivial な作業であることに注意して下さい。(特に、非常に大規模なクラスタや、異種環境が混在するクラスタにおいて)
例えば、全てのスレーブに、JDK、Ant、CVS、あるいはその他ビルドに必要なツールを備えるようにする必要があります。また、全てのスレーブが起動し、稼働中であることも保障しなくてはいけません。Jenkinsはクラスタリングのミドルウェアではないので、この点でいくぶん(環境をそろえることは)難しくなります。

例: Unix上の設定

このセクションでは、Sunの中で、日常のジョブのために私が利用しているJenkinsのスレーブの現在の設定について解説します。
私のマスターのJenkinsはSPARC Solarisで稼動しており、たくさんのSPARC SolarisスレーブやOpteron Linuxスレーブがあります。また、少しばかりWindowsのスレーブもあります。

  • 各コンピューターは jenkins と言うユーザと、 jenkins と言うグループを持っています。全てのコンピューターは同じSIDとGIDを利用しています。(もしNISサービスにアクセスできるのであれば、このあたりの設定はもっと簡単になるでしょう)
    この設定はJenkinsの利用の上での必須条件ではありませんが、こうすることで、スレーブを管理がしやすくなります。
  • 各コンピューターには、 jenkins ユーザのホームディレクトリとして、 /var/jenkins というディレクトリを設定しています。 再度述べますが、こちらは必須ではありません。ですが、同じディレクトリ構成を持つことで、管理しやすくなります。
  • 全てのマシンでSSHDが稼動しています。Windowsスレーブでは、sygwinのsshdが起動しています。
  • 全てのマシンにはNTPクライアントがインストールされていて、通常、同じNTPサーバと時刻同期されています。
  • マスターの /var/jenkins の下には、2~3種類のバージョンのAntやMaven、JDKといった全てのビルドツールがインストールされています。 JDKはネイティブプログラムなので、私は必要となる全てのアーキテクチャ用のJDKのコピーを持っています。ディレクトリ構成は、下記のようになっています:
    /var/jenkins
      +- .ssh
      +- bin
      |   +- slave  (スレーブに関しては下記に詳細があります)
      +- workspace (Jenkinsがこのディレクトリを作成し、この中に全てのデータファイルを格納します)
      +- tools
          +- ant-1.5
          +- ant-1.6
          +- maven-1.0.2
          +- maven-2.0
          +- java-1.4 -> native/java-1.4 (シンボリックリンク)
          +- java-1.5 -> native/java-1.5 (シンボリックリンク)
          +- native -> solaris-sparcv9 (シンボリックリンク; 各コンピュータで異なります)
          +- solaris-sparcv9
          |   +- java-1.4
          |   +- java-1.5
          +- linux-amd64
              +- java-1.4
              +- java-1.5
    
  • マスターの /var/jenkins.ssh には、秘密鍵/公開鍵と authorized_keys があります。こうして、マスターは 公開鍵認証 を利用し、SSHを通してスレーブのプログラムを実行することが出来ます。
  • 私は、マスター上に、マスターの /var/jenkins をrsyncしてスレーブに同期させるための小さなスクリプトを用意しています。(/var/jenkins/workspace は除きます)
    このスクリプトを使い、全スレーブにツールをレプリケートしています。
  • /var/jenkins/bin/launch-slave は、Jenkinsがジョブをリモートで実行するためのスクリプトです。このシェルスクリプトは、まず PATH を通し、slave.jar の起動に先立って、ほかにも幾つかの処理を行います。下記は、非常にシンプルなサンプルスクリプトです。
    #!/bin/bash
    
    JAVA_HOME=/opt/SUN/jdk1.6.0_04
    PATH=$PATH:$JAVA_HOME/bin
    export PATH
    java -jar /var/jenkins/bin/slave.jar
    
  • 最後に、全てのコンピューターに、 svncsv と言った、その他の標準的なビルドツールをインストールし、パスを通しておきます。

スケジューリング戦略

速度の速いスレーブもあれば、遅いスレーブもあります。また、ネットワーク的にマスターに近いスレーブもある一方、非常に遠いスレーブもあります。このため、良好な分散ビルドを行うことは、ある意味チャレンジでもあります。現在、Jenkinsは次のような戦略を執っています:

  1. プロジェクトがあるコンピューター専用に設定されている場合は、常にそれを優先します。
  2. Jenkinsは以前ビルドしたコンピュータと同じコンピュータ上で、プロジェクトをビルドするように試みます。
  3. Jenkinsは、長時間のビルドはスレーブで実行させるように試みます。なぜなら、マスターとスレーブのネットワーク通信の量は、ビルド継続の時間に対数的に関係するからです。(言い換えると、たとえプロジェクトAがプロジェクトBに比べ、ビルドに2倍の時間がかかるとしても、2倍のネットワーク転送は必要されないだろう、ということです)
    こうすることで、ネットワーク上のオーバーヘッドを減少させることに繋がります。

もし面白そうなアイディア(あるいはもっと良くなるためアイディアや実装)があれば、ぜひ教えて下さい。

マスターオンリーから、マスター/スレーブへの変換

通常、マスターのみのインストールでJenkinsを開始し、ずっと後になって、プロジェクトの増加に伴いスレーブを追加する、という流れになります。マスター/スレーブモードを有効にすると、Jenkinsは自動的に、全ての既存のプロジェクトをいったんマスタノード専用に設定します。これは、既存プロジェクトが分散されることを防ぐための対策です。なぜなら、試行錯誤無しにスレーブを正しく設定することは、ほとんど不可能だからです。スレーブ設定に成功したら、マスター/スレーブ間で自由にビルドを行えるように、個別にプロジェクトの設定を調整する必要があります。
この作業は退屈なものですが、同時に、あるプロジェクトを改善させる機会でもあります。

マスター/スレーブ対応となった後で、Jenkins上に新規に作られたプロジェクトは、デフォルトでは、いずれかのノードで実行できるタイプとして設定されます。

パブリックネットワーク上のマスターとファイアウォール内のスレーブ

パブリックネットワーク上(みんなが参照できる場所)にマスターを設置し、一方で、ファイアウォール内にビルド用スレーブが存在する構成を考えないといけない状況があるかもしれません。(インターネット上に多数のマシンを保持することはコストがかかるからです)
この動作を可能にするには、2つ方法があります:

  • マスターからファイアウォール内のスレーブに対しポートフォワードを行います。スレーブに接続するためのIPを把握しているマスターしか、スレーブに接続できないため、ポートフォワードには制限があります。ファイアウォールが存在しないかのようにJenkinsが認識されるように、ファイアウォールを設定しないといけません。
  • 他に方法が無い場合、JNLPスレーブを利用し、スレーブはマスターに接続します。このケースでは、スレーブがコネクションを開始するため、NATを利用したファイアウォールでは正常に動作します。

どちらの場合でも、マスターがいったん危険にさらされると、残りの全てのスレーブも簡単に危険にさらされうることに注意して下さい。(言い換えると、悪質なマスターはスレーブの任意のプログラムを実行できてしまう、ということです)
ですから、どちらの設定においても、セキュリティ上の弱点(セキュリティホール)を切り離すという点に関しては、いくぶん課題を残すことになります。Build publisher plugin が、よりセキュリティに配慮した形で、パブリックネットワーク上のマスターとプライベートネットワーク上のスレーブを繋ぐための、別の解を提供してくれています。

同じマシンで複数のスレーブを稼動させる

Windowsマシン上に複数のスレーブインスタンスを走らせることが可能です。また、それらを別のサービスとしてインストールし、システムの起動時に開始させることも出来ます。エグゼキュータを適切に利用すれば、同じマシンで複数のスレーブインスタンスを利用する必要を減らすことができますが、その上で、下記のように特殊な事例を検討する必要もあるでしょう:

  • ノード間でもさらに設定の柔軟性がほしい場合。あるノードはできるだけ利用し、他のノードは必要になったときだけ利用したい、という場合です。
  • 別々のビルドを行うために、複数のマスターのJenkinsをインストールすることも可能です。この設定により、同一マシン上で、複数のマスターのためのスレーブを持つことができます。実際その通りで、Jenkinsでは、マスターを2つ提供することもできます。

同じWindowsマシン上で、複数のスレーブを稼動させるためは、下記のステップに従って下さい:

  • 最初のスレーブノードをJenkins上に追加します。そして、専用のワーキングディレクトリを割り当てます。(e.g. jenkins-slave-a)
  • スレーブ設定画面からスレーブのページを開き、JNLPを使ってスレーブを起動します。次に、メニューから、『サービスとしてインストールする』、を実施します。
  • いったんサービスが開始されると、jenkins-slave.exeとjenkins-slave.xml が、スレーブのワーキングディレクトリに出来ていることが分かります。
  • Windowsサービスの設定画面に移動し、Jenkinsのスレーブサービスを停止します。
  • Shell(DOS)プロンプトを開き、スレーブのワーキングディレクトリに移動します。
  • まず、"jenkins-slave.exe uninstall" というコマンドを実行し、JNLPで起動しインストールされたアプリケーションのアンインストールを行います。
  • 次に、jenkins-slave.xml を編集します。IDと名前の値を変更すれば、複数のスレーブを識別できるようになります。たとえば、私の場合、IDをjenkins-slave-aとし、名前を Jenkins Slave Aと指定していました。
  • "jenkins-slave.exe install"を実行し、サービスがインストールされているかどうか、Windowsサービスの一覧
    からチェックして下さい。サービスを開始し、Jenkins上でスレーブインスタンスがアクティブになっているかどうかを確認して下さい。
  • マスターの設定画面で、新しいノードの設定を先に行ってから、次のスレーブにも同じ手順を繰り返して下さい。
  • 追記: 新たに設定したノードに割り当てられたURLと、jenkins-slave.xml中に記載している <arguments/>のJnlpURLが同じになるようにして下さい。

2番目のノードを作成する時は、既存のノードや、最初にあなたが作ったノードをコピーして作成することが出来ます。そうすれば、後はそのノードに固有のリモートFSルートを調整したり、残りの設定を行うだけで済みます。作業が終われば、2つ(もしくはそれ以上)のJenkinsのスレーブが、Windowsサービスの一覧に加わることになります。

トラブルシューティング

Windowsスレーブを利用する場合は、注意すべきページや問題(それに対する解決策)がいくつかあります:

  • DCOM経由でWindowsスレーブを起動できない
  • SSH経由でWindowsスレーブを起動できない
  • JNLP経由でWindowsスレーブを起動できない

一般的なトラブルシューティングは、こちらになります:

  1. ローカル/リモートに関わらず、Jenkinsがプログラムを起動する度に、コマンドライン(出力)はログに書き出されます。リモートでの実行に失敗したら、マスターが稼動するコンピュータに、マスターを起動しているのと同じユーザアカウントを使ってログインし、シェルからコマンドを実行してみて下さい。たいていこの方法で手際よく問題を解決することが出来ます。
  1. 各スレーブには、マスターとスレーブエージェントとの間で交わされる情報を表示するためのページがあります。このログ画面には、エラーレポートもたびたび表示されます(ので、ログを参考にして下さい)。
  1. スレーブ起動のために、telnetのような (バイナリデータを直接扱えない) binary-unsafe なリモート操作用のメカニズムを使っているなら、 slave.jar の実行時に -text オプションを追加して下さい。そうすれば、Jenkinsが、ネットワーク上にバイナリデータが流れないように調整を行います。
  1. Jenkins上では失敗してしまうのに、Jenkinsの外部で同じコマンドを実行するとうまくいく場合は、Jenkinsを起動する時と同じユーザアカウントでテストを行ってみて下さい。特に、Windows上でJenkinsのマスターを稼動させている場合は、 システムユーザ(アカウント)としてコマンドプロンプトを利用する方法 で、調査してみて下さい。
  1. トラブルがある場合は、遠慮なく こちらのいずれかのML に相談してみて下さい。

その他の参考情報

  • HudsonWindowsSlavesSetup
    [http://community.jboss.org/wiki/HudsonWindowsSlavesSetup

Comments:

日本語の文章を加えています。
適切でない、分かりにくい表現があれば、書き換えていただければ幸いです。

英語版のオリジナルは下記になります。コメントなども多数ありますので、トラブルの際はオリジナルも参照してみて下さい。

Posted by akiko at Nov 22, 2010 14:31