weblog of key_amb

主にIT関連の技術メモ

Consul クラスタ上で動作する S3 非依存の pull 型デプロイツール "fireap" を作った

github.com

fireap = fire + reap です。
Consul Event を発火(fire)して、受信側でそれを収穫(reap)する、という意で。

読み方は「ファイリープ」で良いかと思ってます。

どんなツール?

GitHub に上げた README.md より、かいつまんで日本語に変換しつつ説明します。

ノード数 N に対して O(log N) で動作するデプロイツールです。
が、実際にはデプロイに限らず任意のコマンドを実行できるので、README の中ではデプロイツールとは書いておらず、「高速タスクランナー」としています。

fujiwara/stretchersorah/mamiyaO(1) なので、それらが使える環境(S3 的な I/O やトラフィックの上限が非常に高いストレージがある)でデプロイを速くしたいという場合は、それらを使えばよろしいかと。

fireap では O(log N) にしかなりませんが、S3 的なものに依存してないのがウリです。 そうです。オンプレミス内で閉じた環境でも使えます。

参考として t2.micro x 100 台で GNU Parallel と比較したベンチマーク結果を README に載せています。
この例では、デプロイ時間を 47sec から 19sec に短縮できました。 ノードがもっと多かったり、並列実行数が少ない場合は、もっと差が広がるでしょう。

また、デプロイサーバ(Publisher Node)の負荷が上がらないことも、メリットとして挙げられると思います。(stretcher, mamiya もそう)

どんな仕組みなの? …というのは、README に貼った図がわかりやすいので、こちらにも貼っておきます。

f:id:key_amb:20160320120514p:plain

  1. Publisher ノードが Consul Event を発火してデプロイをキック。
  2. 第1段階は、数台の Subscriber ノードが Publisher に更新を取得しに行く。
  3. 第2段階以降は、(Publisher + 更新が完了した Subscriber)が、更新取得の対象になる。
  4. 伝播が完了するまで繰り返し。

こんな感じですね。

まだ使い方をドキュメントに起こしていないのですが、近い内に用意するつもりです。

タスクの定義方法について

ドキュメントに書くつもりですが、下のような TOML 形式でタスクを定義できます。

# "foo" アプリケーションのタスク定義
[task.apps.foo]
max_semaphores     = 5       # 1台あたりの更新フェッチの同時最大受付数
on_command_failure = "abort" # or "ignore". Default is "abort"

# Subscriber ノード側で実行されるコマンドを ERB 形式で記述できます。
# 利用可能な変数は以下:
#   - @app            ... 対象アプリケーションの名前。"foo" が入ります。
#   - @remote.name    ... Consul Cluster 内の Node.Name
#   - @remote.address ... Consul Cluster 内の Node.Address
#   - ENV             ... fireap スクリプトに渡される環境変数
# before, exec, after の 3フェーズに分けています。それぞれ複数コマンド記述できます。
before_commands = [
    "echo Task <%= @app %> Started.",
]
exec_commands = [
    "mkdir -p <%= ENV['HOME'] %>/<%= @app %>",
    "rsync -avz --delete <%= @remote.address %>:<%= ENV['HOME'] %>/<%= @app %>/ <%= ENV['HOME'] %>/<%= @app %>/",
]
after_commands = [
    "echo Task <%= @app %> Ended.",
]

余談

Consul を使ったこういうデプロイの仕組みを思いついたのは、去年の6月ぐらいでした。
社内のチャットにコンセプトを書いてみたところ、ある同僚から「Twitter の Murder *1 みたいですね」というコメントをもらいました。

それから早9ヶ月経ちましたが、最近まとまった時間が取れたので、ようやく実装することができました。

もう一つ余談ですが、今回は Ruby で作ることに挑戦してみました。
最悪 HTTP API を直接叩けばいいやと思っていましたが、WeAreFarmGeek/diplomat というライブラリがあったので、その部分は割と楽をできました。

Ruby については、最近は Chef のレシピを書いたりもしていたのですが、思ったより言語仕様レベルで色々わかっていなくてハマりました。

そんな感じなので、まだまだおかしな書き方・作りをしているところが色々あるやもしれません(汗)