JenkinsのSlaveノードの冗長化を考えてみた

f:id:suganoo:20181015183735p:plain
Jenkinsを構築しているんですが、障害対策を検討してみたお話です。

自分ところで使ってるJenkinsはそれほどクリティカルではないにせよ、Jenkinsのノードが落ちてしまったりするとリカバリがなかなか大変なので障害対策をしておきたかったわけです。

Masterに関しては、Docker上で動かしてイメージをエクスポートしてバックアップしておき復旧をお手軽にしておいた。

そんでSlave側ノード、つまり接続先が何かしらで応答しなくなった時どうしよっか?と考えて、対処tipsを書いておこうと思いました

何をしたいのか

  • 設定が全く同じSlaveノードを2台用意してある。(slave01, slave02とする)
  • 通常はslave01を使う。
  • 接続先のSlave01ノードが落ちてた場合、すぐに別の冗長化したSlave02ノードに接続してジョブを実行してもらいたい。

f:id:suganoo:20181015170343p:plain

これを考えた時、LBで振り分けるか、DNSで死活監視してIPを書きかえるか、を考えたけどめんどくさくなりそうなのでやめた。

  • LBで振り分ける
    • 書いてなかったけど、Jenkinsはaws EC2上でSlaveはオンプレ。ELB使おうと思ったけど、ELBはオンプレのIPを振り分けられない。
    • ALBとかほかのサービスとか、LBサーバー建てるかとか、考えたけど管理するものを増やしたくなかったのでボツ。
  • DNSで振り分ける
    • 監視して振り分けようかと思ったけど、これもそこまでDNS使う必要あるか?と思ってやめた。
    • また、監視スクリプトとかもDNSに紐づいてると、こちらも管理が増えそうだったのでやりたくなかった。

ちなみにだけどプラグインでそういうことができるものがあるか調べたのだけど、どうもやりたいことを実現できそうなプラグインが見つからなかった。

どうやって解決したか

プロジェクトの設定にある「実行するノードを制限」のラベル式を使ったのと、Jenkinsのapiを使ってノードをオンライン/オフラインで切り替えてみた。

ラベル式

プロジェクト設定の「実行するノードを制限」のラベル式を使ってみた。
f:id:suganoo:20181015181952p:plain
このラベル式はなかなか曲者で、これ使えば意図通りにできるんじゃないの!?っと淡い期待をした。
けども、いろいろ試した結果ラベル式はよくわからなかった。。。。

けども試したところ、|| (ラベルのOR条件)は使えそうとわかったのでOR条件を使うことにした。

ラベル式に(!hoge)とかhoge && fuga とか指定できるけど、正直振る舞いがよくわからない。
良くおできになるエンジニアの方は頑張って調査してみてほしいです。

ちなみに

ちなみに||でつなぐとこうなりました。

実行するノードを確認のため、ジョブで実行するシェルスクリプトにhostnameを実行し実行ノードを確認しています。

実行するノードのラベル式 実行ノード
slave01 || slave02 slave02
slave02 || slave01 slave02
hoge || slave02 || slave01 slave02

なぜslave02が優先されるのかはわからなかった。
名前順なのか?、接続レスポンスが短い順?かと思ったけどそうでもなさそう。わからんーー!

ですが、ノードのオフライン条件を加えると意図通りになった。

ラベル式 slave01の状態 slave02の状態 実行ノード
slave01 || slave02 online offline slave01
slave01 || slave02 offline online slave02

これを使おうというわけです。

Slaveのオンライン/オフラインの切り替え

探してみたところ切り替え用のapiがあるようです。ラッキー!!

下記のようにcurlを実行するとSlaveノードのオンライン/オフラインが切り替えられる。

curl -s -XPOST --user (user):(apitokenxxxx) 'http://localhost:8080/computer/slave02/toggleOffline'

APIトークンですが、jenkinsの画面の右上にある「ユーザー→設定」で下記のようにAPIトークンが取得できます。
f:id:suganoo:20181015181906p:plain
APIトークンは一回しか表示されないから注意!

またオフラインの状態確認は下記のcurlで取得できます。

curl -s -XGET --user (user):(apitokenxxxx) 'http://localhost:8080/computer/slave02/api/json?pretty=true'

結果は省略しますが、こんな感じの結果が取得できます。

{
  "_class": "hudson.slaves.SlaveComputer",
  "actions": [],
  "assignedLabels": [
    {
      "name": "slave02"
    }
........
  "offline": false,
........

この"offline"キーで状態が取れるので、それをもとにしてオンライン/オフラインを切り替えることにした。
"offline":falseオンライン状態
"offline":true オフライン状態
(間違えそうだー)

ちなみにですが、"temporarilyOffline"というのもあります。
これはノード設定の画面から「このノードを一時的にオフラインにする」を押すと"temporarilyOffline":trueになる。

例えばなにかしらの理由でslave01が使えなくなった時を想定して、slave01のremote.jarプロセスを全部killしてみると、"offline":trueだけど"temporarilyOffline":falseになります。
なので"offline"で判断したほうがよさげ。

ジョブの設定と監視

ジョブの設定

ジョブ側の設定については「実行するノードを制限」のラベル式を「slave01 || slave02」にするだけです。

監視側

  • 定常状態をslave01:オンライン、slave02:オフラインにしておきます。
  • apiで取得できる"offline"キーを定期的に監視します。
  • slave01がオフラインになってたらslave02をオンラインに復活させます。

これでジョブをなるべく止めることなく動かすようにしてみた。

監視のスクリプトは省略。

最後に

良く調べたらtoggleOfflineだけじゃなくてdoDisconnectやlaunchSlaveAgentもあるみたいですね。
stackoverflow.com


状態にかかわらず一方的にオフライン/オンラインできるならこれの方がいいかも。


オンライン/オフラインって言葉間違えてないかな,.......