Consul クラスタ上で動作する S3 非依存の pull 型デプロイツール "fireap" を作った
fireap = fire + reap です。
Consul Event を発火(fire)して、受信側でそれを収穫(reap)する、という意で。
読み方は「ファイリープ」で良いかと思ってます。
どんなツール?
GitHub に上げた README.md より、かいつまんで日本語に変換しつつ説明します。
ノード数 N に対して O(log N)
で動作するデプロイツールです。
が、実際にはデプロイに限らず任意のコマンドを実行できるので、README の中ではデプロイツールとは書いておらず、「高速タスクランナー」としています。
fujiwara/stretcher や sorah/mamiya は O(1)
なので、それらが使える環境(S3 的な I/O やトラフィックの上限が非常に高いストレージがある)でデプロイを速くしたいという場合は、それらを使えばよろしいかと。
fireap では O(log N)
にしかなりませんが、S3 的なものに依存してないのがウリです。
そうです。オンプレミス内で閉じた環境でも使えます。
参考として t2.micro x 100 台で GNU Parallel と比較したベンチマーク結果を README に載せています。
この例では、デプロイ時間を 47sec から 19sec に短縮できました。
ノードがもっと多かったり、並列実行数が少ない場合は、もっと差が広がるでしょう。
また、デプロイサーバ(Publisher Node)の負荷が上がらないことも、メリットとして挙げられると思います。(stretcher, mamiya もそう)
どんな仕組みなの? …というのは、README に貼った図がわかりやすいので、こちらにも貼っておきます。
- Publisher ノードが Consul Event を発火してデプロイをキック。
- 第1段階は、数台の Subscriber ノードが Publisher に更新を取得しに行く。
- 第2段階以降は、(Publisher + 更新が完了した Subscriber)が、更新取得の対象になる。
- 伝播が完了するまで繰り返し。
こんな感じですね。
まだ使い方をドキュメントに起こしていないのですが、近い内に用意するつもりです。
タスクの定義方法について
ドキュメントに書くつもりですが、下のような 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 のレシピを書いたりもしていたのですが、思ったより言語仕様レベルで色々わかっていなくてハマりました。
そんな感じなので、まだまだおかしな書き方・作りをしているところが色々あるやもしれません(汗)