あらしおブログ

技術ネタ中心の粗削りブログ

Jenkins のパイプラインでマスターで実行するタスクを node ブロックで囲む意味

Jenkins のパイプラインスクリプトはマスターで処理されます。 また、各ノードで実行するタスクは node ブロックで囲んで表現します。

パイプラインスクリプトがそもそもマスターで処理されるのであれば マスターで実行したいタスクがあるとき、わざわざ node ブロックで囲む意味は何でしょうか? 例えば下の2つのパイプラインスクリプトを見てください。 どちらもマスターで echo が実行されます。この2つのスクリプトの違いについて書きたいと思います。

node('master') {
    echo 'Hello world'
}
echo 'Hello world'

エグゼキュータータイプ

この違いにはエグゼキューターのタイプが関係しています。

エグゼキューターとは実際にタスクを実行する計算リソースのことですが、 エグゼキューターには以下の2種類があります。

  • 軽量エグゼキューター (flyweight executor)
  • 重量エグゼキューター (heavyweight executor)

後者はジョブを実行するときに必要なふつうのエグゼキューターです。 マスターやスレーブの各ノードで並列実行が許可されている場合は、その数だけエグゼキューターが存在します。 ここでは軽量エグゼキュータと比較するために「重量」と表現していますが、一般的なただのエグゼキューターです。

それに対して軽量エグゼキューターはエグゼキューターのスロットにカウントされない一時的なものです。 軽量エグゼキューターはスロットにカウントされないので、たとえ実行中であっても Jenkins のビューから見えるエグゼキューターのスロットに表示されません。 スロットに空きがなくても実行できるのです。

node で囲む意味

話をもとに戻して、node で囲む意味ですが、

  • node で囲われていない処理は、軽量エグゼキューターによりマスターで実行される
  • node で囲われた処理は、重量エグゼキューターにより対応するノードで実行される

という決まりがあります。 最初の例では実際のところ、ほとんど処理がないのでどちらでもあまり関係ないのですが、 本来はビルドなどリソースを多く消費する処理が想定されます。 node で囲わずに軽量エグゼキュータでこのような処理を実行するとマスターサーバが重くなり Jenkins サービス自体のパフォーマンス低下をまねく恐れがあります。 スロット数で制御することもできないので、裏でいつの間にか重い処理がいくつも動いていたということにもなりかねません。 基本的にはタスクを node 内で実行するようにしましょう。

ただし、node で囲わずに軽量エグゼキューターで実行したほうがよいものもあります。 例えば、ジョブをユーザの承認待ちにする、input を使うときです。 下の例は stage1 が終わった後、user1 というユーザの承認を待ってから stage2 を実行するパイプラインスクリプトです。

stage('stage1') {
    node {
        echo 'stage1: 実際は重い処理'
    }
}

timeout(time:5, unit: 'DAYS') {
    input message: '承認しますか?', submitter: 'user1'
}

stage('stage2') {
    node {
        echo 'stage2: 実際は重い処理'
    }
}

この例では、stage1 が成功した後、ユーザが認証しない限り、stage2 が実行されません。 ユーザの認証はいつ実行されるかわからないブロッキング処理です。 したがって、input を node 内で実行してしまうと、承認されるまで無意味なブロッキングタスクがエグゼキューターを1つ占有し続けてしまうことになります。 このようなタスクは軽量エグゼキューターで実行するのがよいでしょう。

ちなみに、input に対するユーザアクションは、ブラウザでジョブのコンソールログなどから続行するかアボートするかを選択することができます。

参考: Getting Started with Pipeline