ようへいの日々精進XP

よかろうもん

Consul を利用して pull 型デプロイを実現する stretcher を試してみた(2)~エラー処理(検知)について考えてみた~

前回の続き。

デプロイのエラー処理(検知)について

任意のコマンドが実行出来そうだ

stretcher にはデプロイプロセスが成功した際、失敗した際に任意のコマンドを実行することが出来るようだ。

github.com

以下は README からの抜粋。

commands:
  pre:
    - echo 'staring deploy'
  post:
    - echo 'deploy done'
  success:
    - echo 'deploy success'
  failure:
    - echo 'deploy failed!!'
    - cat >> /path/to/failure.log

任意のコマンドを実行出来るということは、上記の例のようにファイルへのログの出力や Slack 等への通知や fluentd へ JSON メッセージを転送したりすることも出来そうであるが、対象の台数が多くなると Slack 等の通知が半端ない数になってしまうなどの課題が無いわけでもない。

成功 or 失敗

成功と失敗はどのように判定されているのかはソースコードを見れば...と思ったが、残念ながらソースコードを見る能力が無いので実際に試してみる。

以下のように post で実行するコマンドを exit 1 だけにしたマニフェストを書いてデプロイを実行してみる。

src: http://consul_deploy:8000/app.tar.gz
checksum: 4b4fedd8ef18df2b42134fa81bbc75bc
dest: /tmp/service
commands:
  pre:
    - echo 'staring deploy' >> /tmp/stretcher.log
  post:
    - exit 1
  success:
    - echo 'deploy success' >> /tmp/stretcher.log
  failure:
    - echo 'deploy failed!!' >> /tmp/stretcher.log

引き続き、post で実行するコマンドを 0 に変えてデプロイを実行してみる。

src: http://consul_deploy:8000/app.tar.gz
checksum: 4b4fedd8ef18df2b42134fa81bbc75bc
dest: /tmp/service
commands:
  pre:
    - echo 'staring deploy' >> /tmp/stretcher.log
  post:
    - exit 0
  success:
    - echo 'deploy success' >> /tmp/stretcher.log
  failure:
    - echo 'deploy failed!!' >> /tmp/stretcher.log

デプロイ先では以下のようなログが記録された。

$ cat /tmp/stretcher.log
staring deploy
deploy failed!!
staring deploy
deploy success

どうやら post で定義されているコマンドの終了コードが 0 以外の場合には failure にて定義されているコマンドが実行されるようだ。(※ pre でコマンドが定義されている場合にも同じようだ。)


Consul を使ったエラー処理実装案

ということで、Consul を利用したエラーの判定について判った(つもり)ところで、デプロイ時のエラー処理について考えてみる。尚、エラー処理というと範囲が広いので今回はエラー検知方法を試してみる。

案(1)KVS + consul-kv-dashboard を利用する

イメージ

f:id:inokara:20150708164044p:plain

試す

consul-kv-dashboard については以下の記事にて。

inokara.hateblo.jp

consul-kv-dashboard を利用して KVS の情報を取得する場合には登録するキーは以下のように定義する必要があるが...

/v1/kv/{namespace}/{category}/{node}(/{key})?flags={flags}

逆を言うと、上記のキーに従えば簡単に consul-kv-dashboard を利用することが出来るので、以下のようなシェルスクリプトを用意して...

#!/bin/sh

datetime=`date +%s`
result_str=$1
result_code=$2
datetime_code=`expr ${datetime} \* 1000 + ${result_code}`
node_name=`curl -s localhost:8500/v1/agent/self | python -c "exec(\"import json,sys\\nj=json.load(sys.stdin)\\nprint j['Member']['Name']\")"`
curl -X PUT -d "{\"result\": \"${result_str}\", \"timestamp\": \"${datetime}\"}" "localhost:8500/v1/kv/dashboard/deploy/${node_name}/result?flags=${datetime_code}"

result.sh というファイル名で保存して実行権限を付与してデプロイパッケージに含めておく。 更にマニフェストcommandsuccessfailure を以下のように設定する。

src: http://consul_deploy:8000/app.tar.gz
checksum: 4b4fedd8ef18df2b42134fa81bbc75bc
dest: /tmp/service
commands:
  pre:
    - echo 'staring deploy' >> /tmp/stretcher.log
  post:
    - sed -i "s/$/`hostname -s`/g" /tmp/service/app/html/index.html
    - pkill python; pushd /tmp/service/app/html; /tmp/service/app/start.sh > /dev/null 2>&1
  success:
    - echo 'deploy success' >> /tmp/stretcher.log
    - /tmp/service/app/result.sh success 0
  failure:
    - echo 'deploy failed!!' >> /tmp/stretcher.log
    - /tmp/service/app/result.sh failure 2

実際にデプロイしてみる。

/usr/local/bin/consul event -node="consul_0" -name deploy_demo http://consul_deploy:8000/deploy.yml

以下のように結果が反映されている。

f:id:inokara:20150708165558p:plain

意図的に失敗させてみる(exit 1 してみる)。

f:id:inokara:20150708165932p:plain

素晴らしひ。

メリデメ

  • 複数のノードの結果を一つの画面でリアルタイムに確認することが出来る(メリ)
  • 複雑な開発等が不要でマニフェストを修正するだけで導入することが出来る(メリ)
  • Consul クラスタ内のノードの何れかにインストールしておくだけで良い(メリ)
  • 特にデメリットは感じない(強いていえば consul-kv-dashboard のプロセス管理が必要にある)(デメ)

案(2)KVS + Consul watch + スクリプトを利用する

イメージ

f:id:inokara:20150708164056p:plain

試す

こちらのようなスクリプトをハンドラとして consul watch を任意のノード(デプロイイベントを送るノード又は専用のノード)にて起動する。尚、consul watchtypekeyprefix を指定することで、deploy/ キーを含む全てのキーの変化を検知してハンドラを起動することが出来る。

consul watch -type keyprefix -prefix deploy/ /path/to/result_hook.py &

また、result.sh を以下のように修正する。

#!/bin/sh

datetime=`date +%s`
result_str=$1
datetime_code=`expr ${datetime} \* 1000 + ${result_code}`
node_name=`curl -s localhost:8500/v1/agent/self | python -c "exec(\"import json,sys\\nj=json.load(sys.stdin)\\nprint j['Member']['Name']\")"`
curl -X PUT -d "{\"result\": \"${result_str}\", \"timestamp\": \"${datetime}\"}" "localhost:8500/v1/kv/deploy/${node_name}/result?flags=${datetime}"

更にマニフェストも以下のように少し修正。

src: http://consul_deploy:8000/app.tar.gz
checksum: 4b4fedd8ef18df2b42134fa81bbc75bc
dest: /tmp/service
commands:
  pre:
    - echo 'staring deploy' >> /tmp/stretcher.log
  post:
    - sed -i "s/$/`hostname -s`/g" /tmp/service/app/html/index.html
    - pkill python; pushd /tmp/service/app/html; /tmp/service/app/start.sh > /dev/null 2>&1
  success:
    - echo 'deploy success' >> /tmp/stretcher.log
    - /tmp/service/app/result.sh success
  failure:
    - echo 'deploy failed!!' >> /tmp/stretcher.log
    - /tmp/service/app/result.sh failure

実際にデプロイしてみる。

# デプロイイベント発行
$ /usr/local/bin/consul event -node="consul_0" -name deploy_demo http://consul_deploy:8000/deploy.yml
Event ID: 6d29dafa-763a-8f80-3436-80239b5d5e20

# 以下のように結果が出力される
2015-07-08 08:21:56 4991 consul_01 {"result": "failure", "timestamp": "1436343716"}
2015-07-08 08:21:56 4989 consul_02 {"result": "failure", "timestamp": "1436343716"}
2015-07-08 08:21:56 4990 consul_03 {"result": "failure", "timestamp": "1436343716"}

デプロイ成功の場合でも...

# デプロイイベント発行
$ /usr/local/bin/consul event -node="consul_0" -name deploy_demo http://consul_deploy:8000/deploy.yml
Event ID: 2f62c232-ebdf-feec-6562-8875011da110

# 以下のように結果が出力される
2015-07-08 08:24:08 4996 consul_01 {"result": "success", "timestamp": "1436343848"}
2015-07-08 08:24:08 4995 consul_02 {"result": "success", "timestamp": "1436343848"}
2015-07-08 08:24:08 4994 consul_03 {"result": "success", "timestamp": "1436343848"}

一応、ログっぽいものが表示される。もちろん、ログ出力以外に通知等もハンドラスクリプトに手を加えることで対応することが出来る。

メリデメ

  • Consul の機能 + ハンドラスクリプトだけで完結出来る(メリ)
  • ハンドラスクリプトの出来次第(ちゃんと作り込めればそれなりに...)(メリ)
  • consul watch の挙動をちゃんと把握していないとツライ(デメ)
  • 画面での確認が出来ない(デメ)

ということで...

stretcher でデプロイを行った際のエラー処理(エラー検知)については consul-kv-dashboard を利用すると良いと思われる。