Jenkins のエージェントを起動する方法について考える

みなさんは Jenkins で分散ビルドをしたことはありますか? Jenkins には、標準でリモートにあるマシンを利用して分散ビルドをするためのプラグインが備わっており、 その中でいくつかの方法を選択することができます。 以前は Java Web Start を使う方法が簡単でしたが、この方法は Java のアップデートに伴って利用できなくなってしまいました。 そこで、この記事では Java Web Start に代わるエージェントの起動方法について解説します。

Java Web Start とは

そもそも Jenkins のエージェントの起動に使われていた Java Web Start とは何か。

Java Web Start は、Java アプリケーションを Web ブラウザーからワンクリックで起動し、クライアントのマシンに デプロイできるシステムです。 Java Web Start は、ユーザーに面倒なインストール作業を強いることなく、全自動でインストールから起動までを代行してくれます。

そんな便利な Java Web Start ですが、近年の OS のセキュリティー対策やアプリストアの煽りを受け、 Java 9 で deprecated に、 Java 11 でついに完全に廃止されてしまいました。

However, over the past decade, vendors of the most popular desktop operating systems have emphatically pushed for applications on their platforms to be delivered bundled with integrated, sandboxed runtimes. Increasingly they require desktop applications to be distributed through their own private "app stores."

The notion of an application being distributed separately from a standalone JRE is, therefore, quickly fading.

(Java Client Road Map Update (March 2018) より)

Jenkins における Java Web Start

Jenkins では、エージェントを起動する方法の一つとして Java Web Start を使う方法が用意されていました。 エージェントを起動するマシンからマスターの Jenkins にアクセスし、 Launch ボタンをクリックしてエージェントを起動するというものです。

この方法ですが、現在 Jenkins の実行環境としてサポートされている JRE バージョンが 8 と 11 しかないため、 実質的に 8 で実行しているときにしかこの方法は使うことができません[1]

今後の JRE / Jenkins のアップデートに備えるためには、Java Web Start を使わない方法で代替する必要が出てきます。 OpenJDK 8 のサポートも数年内に切れてしまうので、いつまでもこれを使い続けるわけにもいきません。

代替方法

ここでは、公式ドキュメント[2][3]を参考に 4 つの方法を紹介します。 なお、 Unix 系 OS であることを前提とするため、 Windows 向けの方法については省きます。

まず、それぞれの特徴について 2 つの側面から比較してみます。「設定方法」と「コネクションの方向」です。

設定が簡単 詳細な設定が可能
マスター起点 ① SSH ② コマンド指定
エージェント起点 ③ WebSocket ④ TCP ポート指定

1. SSH 経由で起動する

Jenkins Wiki[3] 曰く Unixにとっては、この方法がもっとも便利

これは、Jenkins 自身が SSH クライアントとなり、エージェントマシンの sshd を叩いてコネクションを張り、 そこで自動でエージェントを開始するものです。

エージェントを動かすマシンでは sshd を起動してマスターに登録する SSH 鍵の公開鍵を authorized_keys に登録しておき、 マスターのノード管理画面で以下のように登録します。

SSH 経由で起動する場合の設定

あとは Jenkins が勝手に起動してくれます。簡単ですね!

2. マスターで独自のコマンドを指定して起動する

上記の SSH 以外の方法でコネクションを張りたい場合もあると思います。その場合、Jenkins マスターに叩かせるコマンドを自分で指定することができます。 僕が「ローカルでエージェントを立てよう」となって最初に使ったのはこの方法でした。

独自のコマンドを指定して起動する場合の設定

(同じマシンなのをいいことに絶対パスで agent.jar を起動しています。)

同じ SSH 接続でも特定のオプションを付けたいといった需要が発生するかもしれないですし、最初からこちらを使ってしまうのもアリかもしれません。 なお、こちらを使う場合は先にエージェント用の JAR ファイルを手動でエージェントにするマシンに用意しておく必要があります。 wget https://<Jenkins アドレス>/jnlpJars/agent.jar でダウンロードしておきましょう。

3. 接続先 TCP ポートを指定して手動で起動する

これは、サーバー側でエージェントの接続を待ち受けるポートを開き、クライアント側からそこに接続してもらう方法です。

まず、Jenkins の管理グローバルセキュリティの設定 にある Agents の項目で、待ち受ける TCP ポートを設定します。 デフォルトでは無効になっているため、この方法は使うことができなくなっているはずです。 ポートの開け方としては、固定の単一ポートを開けるものと、接続ごとにランダムなポートを開けるものの 2 種類を選べます。

Agents 設定

その後、ノード管理画面にて Launch agent by connecting it to the master を選択します。 Use WebSocket のチェックは外した状態にします。 Either WebSocket mode is selected, or the TCP port for inbound agents must be enabled というメッセージが出て作成できない場合、最初の TCP ポートの設定がうまくいっていない可能性が高いので確認してください。

エージェントの設定

最後に起動方法です。本来はこのルートが Java Web Start を使う方法で使うルートなのですが、Java 11 の環境で実行している場合は そのアイコンが表示されません。代わりの起動方法として、クライアント側で実行するコマンドのみが残されています。 基本的にはここに書かれているコマンドのいずれかを実行すれば問題ありません。接続時のトークンをファイルに保存するか直接指定するかの違いのみです。

Java 11 では Java Web Start が使えないのでそのアイコンが表示されていない

ただし、 TCP ポートを固定している場合にはもう一つのコマンドを使って接続することができます。 「HTTP(S) ポートを経由せず、直接接続先 TCP ポートに接続する」 というものです。 上記の 2 のコマンドはあくまで一旦 jnlp ファイルを取得するために HTTP を経由する必要があるのですが、 以下に説明するコマンドではその必要がありません。

その方法ですが、まず Jenkins の管理スクリプトコンソール で、以下のスクリプトを貼り付けて実行します。

import org.jenkinsci.main.modules.instance_identity.InstanceIdentity
import hudson.remoting.Base64

Base64.encode(
  InstanceIdentity
  	.get()
  	.getPublic()
  	.getEncoded()
)

実行すると、ページ下部に Result として Base64 encoded な文字列が表示されます。これは Instance Identity というもので、 直接接続の際に secret とは別に必要な情報です。実体は公開鍵です。

この値が得られたら、エージェント側で以下のようにコマンドシェルを実行します。

INSTANCE_IDENTITY="先ほど取得した Instance Identity"
SECRET="エージェントの詳細画面のコマンドに記載されている secret の値"
AGENT="マスター側で設定したエージェント名"
WORKDIR="エージェントの作業ディレクトリ"
MASTER_HOST="domain:port 形式でマスターで公開しているポートを指定する"

java -cp agent.jar hudson.remoting.jnlp.Main \
  -headless \
  -workDir $WORKDIR \
  -direct $MASTER_HOST \
  -protocols JNLP4-connect \
  -instanceIdentity $INSTANCE_IDENTITY \
  $SECRET $AGENT

4. WebSocket モードで起動してでマスターに接続する

上記ページには載っていない、最近の Jenkins で使える方法です。具体的にはバージョン 2.217 から対応しているようです[4]

基本的には 3 の方法と同じですが、別の TCP ポートを指定する代わりにダッシュボードが公開されている HTTP(S) ポート上で WebSocket 通信を 流してしまう方法です。

設定方法は、ノード管理画面で Launch agent by connecting it to the master を選択し、 Use WebSocket のチェックを入れるだけです。

考察

上の表でも提示したように、これら 4 つの方法は 「コネクションの開始がマスター起点かエージェント起点か」 という点で二分されます。

1 と 2 はマスター側で接続先を指定していますが、 3 と 4 はエージェント側が起動時にマスター側の情報を指定しています。 エージェントのマシンがイントラネットなどファイアウォールの中にあり、マスターから接続できない構成の場合には後者の方法を使うことになります。

特に 3 の後半で紹介した方法はダッシュボードのポートすら公開することなく、エージェントの接続先のポートただ一つを開ければよいため、 よりセキュアな環境を保つことができるでしょう。一方で、標準の HTTP(S) ポート以外を開けるのが難しいという場合、 4 の WebSocket を使う方法は 他のポートを必要としない点で優秀といえます。

おわりに

以上、 Jenkins のエージェントの接続方法についてまとめてきました。 ブラウザからワンクリックで起動できる Java Web Start が失われてしまったのはやや痛いですが、依然として同じような通信方向で接続できる オプションは存在します。環境に応じてそれぞれを使い分けていきましょう。

参考リンク

1 : ^ Java requirements - Jenkins

2 : ^ jenkinsci/jenkins PR #3998

3 : ^ Distributed builds - Jenkins (Internet Archive)

4 : ^ WebSocket - Jenkins

このブログについて

KLabのゲーム開発・運用で培われた技術や挑戦とそのノウハウを発信します。

関連記事

このブログについて

KLabのゲーム開発・運用で培われた技術や挑戦とそのノウハウを発信します。