consul でローカルマシンの名前解決をしたい

Docker Engine 1.9 でついにマルチホスト間の仮想ネットワーク接続がサポートされました。
これで Docker を利用したクラスタシステム開発にさらに弾みがつくものと期待します。

Docker Engine 1.9 の仮想ネットワークを構築するためには Docker Engine だけでなくクラスタ対応の分散型KVSが必要で consul/etcd/ZooKeeper が対応しているとの事です。
この機会に consul を使ってみようというのが今回の起点となります。

# consul でできること

consul は Hashicorp が提供しているデータセンタ管理ツール群 ATLAS の一部で、クラスタリングと分散KVS、オーケストレーションの基礎となる機能をいくつか提供してくれます。
それらの機能のうち今回は

- ノードのクラスタ参加とクラスタリングの管理
- WEBAPIとDNS形式でのノード情報提供

を利用したいと思います。

# 解決したい問題

ローカルネットワークに部屋内サーバーがあるのですが、VM 化したことでサーバーマシンの数が増えてきました。これらにアクセスするために IP アドレスを直打ちするのも面倒なので、ローカルの名前解決を行う bind を設置しているのですが、マシンに変化があったときに設定を書き換えるのが手間になっています。また、DHCP の場合IPアドレスも追いきれません。

そこでマシンが起動しただけでそのアドレスを管理して名前解決を行う仕組みがほしいと思いました。

avahi をインストールして mDNS も試してみましたが、いまいち解決には至りません。

そんな、変化するマシン環境を管理し名前解決を提供してくれる機構として consul を利用します。
名前解決したいマシンすべてに consul をインストール、実行する必要はありますが動的に解決できるというのは魅力です。

# 設計

consul が抱えているクラスタリングエンジンの self は Gossipプロトコルベースでノード間連携を行うので基本的にはサーバー役が居ないのですが、consul はサーバーという概念があります。最低1台のサーバー役がいることで、ノードの参加宣言先となりますし、WEB-UI を使うこともできるようになります。

consul は DNS形式でノード情報を提供しますが、この時のアクセスポートが 8600番なのでそのままでは DNS リゾルバが行えません。
dnsmasq を利用することで、マシンローカル(127.0.0.1)のリクエストを別ポートにプロキシすることができるので、それで consul の DNS を利用できるようになりますがマシン 1台内で閉じています。
幸いローカル環境には bind サーバーが既にあり、主要はローカルマシンの名前解決を行っています。なので、ここに "consul" zone の名前解決であれば 127.0.0.1:8600 に forword すれば他のマシンに対しても consul の名前解決を提供することができます。

これらの事から元々 bind が入っていた部屋内情報サーバー(我が家では 192.168.0.1)に consul をサーバーモードで起動し、他のマシンは基本このサーバーに node join を依頼することにします。
参加中の node 情報は bind を介して DNS でリゾルバできるようにし、consul をインストールしノードとして参加しているマシンはすべて (マシン名).node.consul で名前解決可能ということで目的を達成できそうです。


# サーバーマシンの設定

インストール先のサーバーマシンでは bind と nginx が既に動いています。(詳細には OpenNebura コントローラーを役目としています)

consul を Hashicorp からダウンロードすると実行ファイルが 1個だけ手に入ります。
これを ```/usr/local/sbin``` 等適当なところにコピーして起動できるようにしましょう。理由は後述しますが、consul 自体は一般ユーザーが実行できないほうが良いように思います。

initd もしくは systemd の service としてサービスとして起動できるようにしておきます。
この辺の手順は省略。(別枠で書き起こすかも)

起動オプションは以下の通り。

```
 # consul agent -server -bootstrap-expect 1 -config-dir /etc/consul.d -data-dir /var/lib/consul -ui-dir /var/lib/consul/ui -bind=192.168.0.1
```

データディレクトリやバインドIPアドレスは環境に合わせて適時読み替えてください。

UIdir には実行ファイルとは別に配布されているUIパッケージを置いておきます。

consul は基本ローカルアドレスからのアクセスしか受け付けません。
せっかくの WEB UI なのに他のマシンからアクセスできないというのももったいない話ですが、WEB UI/API ともにクラスタの全システムにアクセスできてしまうので開放はしないほうが良さそうです。
ちなみに、他のマシンからアクセスできるようにするには ```-client=0.0.0.0``` などとしてアクセス可能なインタフェースを指定します。

ローカルアドレスからのアクセスに絞ったままで外部にサービスを提供するために必要なプロトコルのみを他のサーバーで proxy します。
具体的には nginx で WEB-UI を、bind で DNS をそれぞれ forword してやります。前段の nginx で認証をつけるなり https 化するなりできるわけですね。

## nginx の設定例。

```
server {
        listen 8081;
        listen [::]:8081;

        server_name consul-ui;
        autoindex off;

        location / {
                proxy_pass http://127.0.0.1:8500/;
        }
}
```

/hoge/ui/ と /hoge/v1/ にアクセスしようとするので location に名前を付けて一つ下の階層にできませんでした。ポートで分けてあります。
もうちょっとうまいプロキシができると良いのですが。

## bind9 の設定例。

```
zone "consul" IN {
  type forward;
  forward only;
  forwarders { 127.0.0.1 port 8600; };
};
```

*.node.consul, *.service.consul といった名前になりますので、consul zone で forword します。

サーバーの設定はこれで完了。
サーバーは基本的に起動したままで、各ノードの参加を待ち受けます。


# その他ノードマシンの設定

各ノードマシンは起動時に consul を起動して、サーバーに join をリクエストします。
基本的にはこれだけです。

initd もしくは systemd の service としてサービスとして起動できるようにしておきます。
詳細は省略。

コマンドラインは以下の通り。

```
 # consul agent -config-dir=/etc/consul.d -data-dir=/var/lib/consul
```

起動した状態でサーバーに join リクエストを出してクラスタに参加します。

```
 # consul join (サーバーのIPアドレス)
```

これらを service 起動で行うわけですが、タイミング的にネットワークがまだ準備できていなくてエラーになる場面がありました。有線LANだと ```network-online.target``` の後で起動を指定することでなんとかなるのですが、無線LANだと接続＆DHCP確定が随分と後の事になるので失敗するといった状況でした。
これを含め確実に起動するためには service disable で起動時に自動起動しないようにして、 consul の起動はネットワークが確立した後になるよう network/if-up.d/ スクリプトの中で ```systemctl restart consul``` などを呼んで起動＆join するようにするとうまくいきました。

ノード名は起動時に ```-node=``` で指定できますが、デフォルトは hostname になります。
あらかじめ /etc/hostname を確認し、必要ならば編集して指定しておきましょう。
hostname コマンドでも変更できますが、再起動すると消えます。

ここまでできたらマシンを起動すると自動的にクラスタに参加し、サーバーの DNS 軽油で名前解決ができる用になります。
consul が起動しているノードマシンにおいては他のノードと KVS 共有など様々なクラスタリング連携ができるようになっています。


# ノードのメンバー確認

```
 # consul members
```

クラスタのノード一覧を表示します。


# ほかに consul が提供するもの

今回はノードの参加と、DNS 機能しか使っていませんが consul の基本は大体つかめました。
consul は他に以下のようなオーケストレーションに使える機能を提供してくれる様です。

1. クラスタ内で一意な分散型 KVS
 - 簡単なキーバリューストアですが、ノードのどこで読みだしても同じ内容になるDBです。状態フラグや設定の共有などが行えます。

2. ノードで起動しているサービスの監視
 - ノードが提供するサービス(httpとか)を設定、宣言することでそのサービス名でDNSが引けるようになるのですが、それと同時にサービスの簡単な死活監視を行うことができます。
状態が変化(サービス停止または再開、どちらかはわからない)したときに全ノードにイベントを発呼します。consul が提供するのはこのイベントの発呼までとなります。

3. ノードに対するコマンド実行
 - exec コマンドで全ノードもしくは指定ノードに対し実行するコマンドを指定できます。
クラスタに属していれば、参加ノードをリモート操作することができます。
クラスタリングに使えて便利なのですが、コマンドの実行権限は consul を起動したユーザーになります。root で起動していると強力なコマンドを発呼できることになるのでセキュリティには気を付けたほうが良いかもしれません。

4. クラスタからのイベント
 - watch コマンドでイベントを待ち受けることができます。
イベントを受けたらコマンドを実行する形でリアクションを実現できます。

実際には Docker の様にこれら機能を利用する上位のフレームワークを使う場面が多いとは思いますが、consul を使いこなすことで自前クラスタ環境を構築できるかも知れません。