log.fstn

技術よりなことをざっくばらんにアウトプットします。

Consulクラスタ内でファイル共有する Fileconsul というツールをつくってみた

追記(2014/09/23) しました。

はじめに

Consul使ってますか?
最近は 0.4 がリリースされ、大幅な機能追加によってサービスディスカバリとしてでなく、オーケストレーションツールとして注目を集めて来ています。 また、プロダクションでの使用事例やSoftware Designに掲載されるなど(http://gihyo.jp/magazine/SD)、国内でも勢いをみせています。

個人的にはオーケストレーションツールとして利用したいと思っていますが、また検証段階といったところです(個人で遊んでいるレベル)。

Consul + Capistrano でオーケストレーションさせてみた - log.fstn

オーケストレーションツールとしてのConsulの使い方 - Qiita

そんなConsulですが、利用にあたって下記のようなファイルを各ノードに設置する必要があります。

ファイルの配布方法に関する考察

Consulに必要なファイルは何かしらの方法で各ノードに配布する必要があります。 また、これらのファイルは運用中にアップデートされることが容易に考えられるので、クラスタの各ノードにファイルを配布するような仕組みも必要です。

既存のツールを利用する場合は、Capistranoなどのデプロイツールを利用して各ノードにファイルを配布する方法が考えられますが、配布用のタスクを書く必要がありますし、ノード数が多いと配布時間も気になってきます。

なお先日のRubyKaigi 2014 で Serf(http://www.serfdom.io/) を利用したデプロイツール Mamiya (https://github.com/sorah/mamiya) の紹介がありました。

RubyKaigi 2014 | Scalable deployments - How we deploy Rails app to 100+ hosts in a minute

配布対象のノードが多い場合は、Capistranoのようにデプロイするためのマシンを用意してそこから配布するのではなく、オーケストレーションツールでイベントを発行して、各ノードがそれぞれダウンロードするような仕組みの方が効率が良さそうです。

Fileconsul の紹介

既存のツールを利用して各種ファイルを配布するのもいいのですが、どうせなら

  • Consulの機能を利用したい
  • 配布対象を意識したくない(Capistarnoのように対象を指定して配布するようなことはしたくない)
  • 複雑な設定やCapistranoのタスクのようなものを作成したくない
  • ノードが増えても配布時間がなるべくかからないようにしたい
  • 対象ノードの環境は変えたくない(特定の言語のバージョンやパッケージに依存しないようにしたい)

など考えて、Fileconsul というものを作りました。

foostan/fileconsul · GitHub

FileconsulはConsulクラスタ内でファイル共有するためのツールであり、以下の様な特徴があります

  • ConsulのK/V Storeを用いてファイルを共有する(ファイルのデータをvalueとして保持する)
  • 共有できるファイルは1つあたり512KBの制限がある(ConsulのK/V Storeの限界値)
  • cliでファイルのアップロード(pushコマンド)、ダウンロード(pullコマンド)が簡単に行える
  • アップロードされたファイルは即座にクラスタ内で共有される
  • Goで出来ているためバイナリひとつで動き、対象ノードの環境に左右されない

ファイルの容量制限があるため、あまり大きなファイルは共有できませんが、512KBなら設定ファイルや簡単なスクリプト程度なら十分許容できると思います。 またK/V Storeを利用することでConsulの恩恵が受けられます。

Fileconsul の使い方

ほぼREADME.md(https://github.com/foostan/fileconsul/blob/master/README.md) の再掲ですが使い方を紹介します。

Demo環境の用意

Fileconsulのレポジトリにデモ環境用のDockerfileがありますので、これを利用します。

$ git clone https://github.com/foostan/fileconsul.git
$ cd fileconsul/demo
$ docker build -t fileconsul .

Dockerの環境がない場合は Docker Documentation を参照して下さい。 Mac/Windows なら boot2docker で簡単に環境が整います。

コンテナとConsul agentの起動

Consulではクラスタ内でserver agentが必要なのでデモ環境でもserverとclientを起動しています。 ですが、Fileconsulの挙動はserver側もclient側も特に変わりません。

Consul server 側

$ docker run -h server -i -t fileconsul
root@server:/# consul agent -data-dir=/tmp/consul -server -bootstrap-expect 1 &
==> WARNING: BootstrapExpect Mode is specified as 1; this is the same as Bootstrap mode.
==> WARNING: Bootstrap mode enabled! Do not enable unless necessary
==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Consul agent running!
         Node name: 'server'
        Datacenter: 'dc1'
            Server: true (bootstrap: true)
       Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
      Cluster Addr: 172.17.0.6 (LAN: 8301, WAN: 8302)
    Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

--- omitted below ---

Consul client 側

$ docker run -h client -i -t fileconsul
root@client:/# consul agent -data-dir=/tmp/consul -join 172.17.0.6 &
==> WARNING: It is highly recommended to set GOMAXPROCS higher than 1
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Joining cluster...
    Join completed. Synced with 1 initial agents
==> Consul agent running!
         Node name: 'client'
        Datacenter: 'dc1'
            Server: false (bootstrap: false)
       Client Addr: 127.0.0.1 (HTTP: 8500, DNS: 8600, RPC: 8400)
      Cluster Addr: 172.17.0.5 (LAN: 8301, WAN: 8302)
    Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false

--- omitted below ---

Fileconsul を使ってみる

サーバ側でstatusコマンドとpushコマンドを使用する

fileconsul status で local file(手元のファイル)とremote file(K/V Store上のファイル)との差分を表示します。
Consul clusterの初回構築時はK/V Storeが空なので、fileconsul pushでファイルをアップロード(K/V Storeに保存)します。

root@server:/# cd /consul/share/
root@server:/consul/share# fileconsul status
Changes to be pushed:
  (use "fileconsul push [command options]" to synchronize local files)
    add remote file:    bin/ntp
    add remote file:    bin/apache2
    add remote file:    bin/loadavg
    add remote file:    config/service/apache2.json
    add remote file:    config/service/ntp.json
    add remote file:    config/template/server/check_loadavg.json
    add remote file:    config/template/server/service_apache2.json
    add remote file:    config/template/server/agent_server.json
    add remote file:    config/template/server/service_ntp.json
    add remote file:    config/template/client/check_loadavg.json
    add remote file:    config/template/client/agent_client.json
    add remote file:    config/template/client/service_ntp.json
    add remote file:    config/check/loadavg.json
root@server:/consul/share# fileconsul push
push new file:  bin/ntp
push new file:  bin/apache2
push new file:  bin/loadavg
push new file:  config/service/apache2.json
push new file:  config/service/ntp.json
push new file:  config/template/server/check_loadavg.json
push new file:  config/template/server/service_apache2.json
push new file:  config/template/server/agent_server.json
push new file:  config/template/server/service_ntp.json
push new file:  config/template/client/check_loadavg.json
push new file:  config/template/client/agent_client.json
push new file:  config/template/client/service_ntp.json
push new file:  config/check/loadavg.json

client側でファイルを編集してpushする

共有されているファイルを編集してfileconsul statusすると、編集されているファイルが確認できます。 この状態でfileconsul pushすると編集されたファイルの内容がK/V Storeに反映されます。

root@server:/# cd /consul/share/
root@client:/consul/share# vi bin/apache2   # edit a file
root@client:/consul/share# fileconsul status
Changes to be pushed:
  (use "fileconsul push [command options]" to synchronize local files)
    modify remote file: bin/apache2
Changes to be pulled:
  (use "fileconsul pull [command options]" to synchronize remote files)
    modify local file:  bin/apache2
root@client:/consul/share# fileconsul push
push modified file: bin/apache2

server側でpullする

client側で編集したファイルをpushしたため、server側でfileconsul statusすると、編集されたファイルが確認できます。 この状態でfileconsul pullすると、編集されたファイルが同期されます。

root@server:/consul/share# fileconsul status
Changes to be pushed:
  (use "fileconsul push [command options]" to synchronize local files)
    modify remote file: bin/apache2
Changes to be pulled:
  (use "fileconsul pull [command options]" to synchronize remote files)
    modify local file:  bin/apache2
root@server:/consul/share# fileconsul pull
Synchronize remote files:
    modified:   bin/apache2
Already up-to-date.

さいごに

Fileconsulを利用することでConsulクラスタ内でファイルを共有することができます。

Consulを利用していると様々なファイルを各ノードで共有したくなりますが、Fileconsulを利用すると既存のツールに比べて簡単に共有できるようになると思います。 また、Consul 0.4 で追加されたwatch機能を利用すれば、いちいち fileconsul pull をすることなくファイルの同期が出来るようになります。

今後はFileconsulを含めたConsulの有益な使い方を考えていければと思います。
ちなみにFileconsulはGoの勉強がてら作り始めたものなので、この書き方おかしいよとか、こんな風に書くとGoっぽいよ、といったご指摘も頂けるとありがたいです。

追記(2014/09/23)

Consul Google Group にて Fileconsul を紹介したところ、早速フィードバックを頂けたので追記します。
やりとりの内容はこちら Consul Google Group: Released the tool of sharing small files in a Consul cluster

@armon からのフィードバック

armon (Armon Dadgar) · GitHub

似たようなプロジェクトがあるから見てみてとのこと。

ryanbreen/fsconsul · GitHub

fsconsulはファイルをダウンロードすることしか出来ないらしく、その点Fileconsulはアップロードも出来る点が有益とのこと。

メインのコミッターからカジュアルに意見を頂けるコミュニティがあるのはありがたいし、フィードバックもらえて素直にうれしいです。
あとConsulの中の人はfsconsulのような小さなツールまで把握してるんですね。フィードバックも10分程度で貰えたし、スピード感あります。

Alvaro からの質問

Alvaro Miranda Aguilera - 基本情報 - Google+

各コマンドの終了ステータスを教えてほしいとのこと。 終了ステータスを見て次のアクションに繋げたいらしいので、この辺りも実装でフォローしていくべきなのかな?
実際、終了ステータスを明確に返すツールってどれぐらいあるんだろうか。