先日、社内勉強会以外の伺か(仮)で、Consul について話したときの資料がでてきたので置いておきます。
213 番煎じぐらいで真新しいものではありません。
Consul is なに?
Packer や Vagrant の HashiCorp 社が作っているオーケストレーションツール。
Docker コンテナ
Consul を試す環境として Docker で以下の環境を作ります。
- server
- 10.88.0.10
- 8080 => 8080
- node1
- 10.88.0.11
- node2
- 10.88.0.12
次の Dockerfile を使います。 最近の CentOS 7 のコンテナは systemd のサービスが普通に動くので、軽量な仮想環境として使うのに便利です。
FROM centos:7 RUN yum install -y epel-release &&\ yum install -y wget unzip bind-utils dnsmasq nginx rsync &&\ yum clean all RUN wget -q https://dl.bintray.com/mitchellh/consul/0.5.2_linux_amd64.zip &&\ unzip 0.5.2_linux_amd64.zip &&\ mv consul /usr/local/bin/consul &&\ rm -vf 0.5.2_linux_amd64.zip RUN wget https://dl.bintray.com/mitchellh/consul/0.5.2_web_ui.zip &&\ unzip 0.5.2_web_ui.zip &&\ mkdir -p /opt/consul/dist/ &&\ rsync dist/ /opt/consul/dist/ -av &&\ rm -rvf 0.5.2_web_ui.zip dist/ ENTRYPOINT /sbin/init
yum は説明不要だと思います。他の2つの RUN は、consul のコマンドとか UI とかをダウンロードしているのですが、後ほど説明します。
最後の ENTRYPOINT /sbin/init
は、コンテナの中で systemd のサービスを動かすため です。
ビルドします。
docker build -t example/consul .
コンテナを起動します。--privileged
は pipework を使うために必要です。
docker run --privileged -d --name server -h server -p 8080:8080 example/consul docker run --privileged -d --name node1 -h node1 example/consul docker run --privileged -d --name node2 -h node2 example/consul
pipework でコンテナに固定IPを付与します。
sudo pipework br1 server 10.88.0.10/24 sudo pipework br1 node1 10.88.0.11/24 sudo pipework br1 node2 10.88.0.12/24
インストール
Go 言語なのでバイナリいっこ落とすだけ。 (Dockerfile でやってるので不要です)
wget https://dl.bintray.com/mitchellh/consul/0.5.2_linux_amd64.zip unzip 0.5.2_linux_amd64.zip sudo mv consul /usr/local/bin/consul
Consul Server
server にログインします。
docker exec -it server /bin/bash
次のように consul を実行します。
consul agent -server -bootstrap-expect=1 -data-dir=/tmp/consul -node=server \ -bind=10.88.0.10 -ui-dir=/opt/consul/dist
別のターミナルでログインします。
docker exec -it server /bin/bash
メンバーの一覧を表示してみます。
consul members
まだ自分しかいません。
Node Address Status Type Build Protocol DC server 10.88.0.10:8301 alive server 0.5.2 2 dc1
consul の DNS インタフェースを dig で呼んでみます。
dig server.node.consul @127.0.0.1 -p 8600
次のように結果が返ります。
;; ANSWER SECTION: server.node.consul. 0 IN A 10.88.0.10
port 番号とかを指定するのが面倒なので dnsmasq を使います。
.consul をローカルの 8600 ポートにフォワードするように設定します。
cat <<EOS> /etc/dnsmasq.d/consul.conf server=/consul/127.0.0.1#8600 strict-order EOS
デフォルトのネームサーバをローカルにするために nameserver 127.0.0.1
を /etc/resolv.conf
の先頭に挿入します。
sed -i '1i nameserver 127.0.0.1' /etc/resolv.conf
設定を反映します。
systemctl restart dnsmasq.service
名前解決してみます。
dig server.node.consul
先ほどと同じように結果が帰ります。
;; ANSWER SECTION: server.node.consul. 0 IN A 10.88.0.10
Consul Client
node1 と node2 に nginx を入れて consul を使って DNS ラウンドロビンするようにしてみます。
node1 と node2 にそれぞれログインします。
docker exec -it node1 /bin/bash docker exec -it node2 /bin/bash
nginx を起動します。
systemctl start nginx.service systemctl status nginx.service
ドキュメントルートに、どちらのホストを見ているのか判るようにホスト名が書かれたファイルを置きます。
uname -n > /usr/share/nginx/html/consul.html curl http://127.0.0.1/consul.html
consul のサービスの設定ファイルを作成します。 この例だと web というサービス名で、curl でサービスの監視をしています。
mkdir -p /etc/consul.d/ cat <<EOS> /etc/consul.d/web.json { "service": { "name": "web", "tags": [ "nginx" ], "port": 80, "check": { "script": "curl http://127.0.0.1:80/consul.html >/dev/null 2>&1", "interval": "10s", "timeout": "5s" } } } EOS
node1 と node2 でそれぞれ consul を起動します。
consul agent -data-dir=/tmp/consul -node=$(uname -n) \ -bind=10.88.0.11 -config-dir=/etc/consul.d/ -join=10.88.0.10
consul agent -data-dir=/tmp/consul -node=$(uname -n) \ -bind=10.88.0.12 -config-dir=/etc/consul.d/ -join=10.88.0.10
server のターミナルでメンバーの一覧を表示してみます。
consul members
node1 と node2 が追加されています。
Node Address Status Type Build Protocol DC server 10.88.0.10:8301 alive server 0.5.2 2 dc1 node1 10.88.0.11:8301 alive client 0.5.2 2 dc1 node2 10.88.0.12:8301 alive client 0.5.2 2 dc1
名前解決もできます。
dig node2.node.consul a
次のようにサービス名を指定すると、node1 と node2 の両方のアドレスが返ってきます。
dig web.service.consul a
サービス名の URL を複数回表示すると、DNS ラウンドロビンされているのがわかります。 (と思ったんだけどなぜかラウンドロビンされない・・Vagrant で環境作った時にはできたんだけど・・?)
curl http://web.service.consul/consul.html
node1 の nginx を止めてみます。
docker exec node1 systemctl stop nginx.service
メンバーの一覧は特に変わりません。
consul members
サービス名で名前解決してみると、node2 のアドレスしか返らなくなっています。
dig web.service.consul a
もちろん、サービス名の URL も node2 にのみアクセスします。
curl http://web.service.consul/consul.html
node1 の nginx を再開します。
docker exec node1 systemctl start nginx.service
サービス名で node1 と node2 の両方が返るように戻ります。
dig web.service.consul a
サービス名の URL も両方に振り分けられるように戻ります。 (と思ったんだけどやっぱりラウンドロビンされない?)
curl http://web.service.consul/consul.html
クエリ
serf でやったようなクエリの機能はデフォで有効です。
次のようにすると、すべてのホストでの uname -a
の結果が得られます。
consul exec uname -a
特定のノードを指定することもできます。
consul exec -node=node2 uname -a
特定のサービスを指定することもできます。
consul exec -service=web uname -a
次のように、web サーバだけを対象に nginx を再起動させたりできます。
consul exec -service=web systemctl restart nginx.service
UI
consul には Web の UI があります。
UI のファイルをダウンロードして、適当なディレクトリ(consul agent
の --ui-dir
に指定したディレクトリ)に展開します。
(Dockerfile でやっているので不要です)
wget https://dl.bintray.com/mitchellh/consul/0.5.2_web_ui.zip unzip 0.5.2_web_ui.zip mkdir -p /opt/consul/dist/ rsync dist/ /opt/consul/dist/ -av
nginx をそれっぽく設定します。
cat <<EOS> /etc/nginx/conf.d/consul.conf; server { listen 8080 default_server; server_name server.node.consul; location / { proxy_pass http://127.0.0.1:8500; } } EOS
nginx を開始します。
systemctl start nginx.service systemctl status nginx.service
以下の URL を開くと Web 画面が見えます。
open http://localhost:8080/ui/
さいごに
このデモでは、サーバが1台と、クライアントが2台で試しましたが、高可用にするためにはサーバは3台か5台にするべきらしいです(マルチデータセンターならデータセンターごとに3台か5台)。
また、Consul サーバはそれなりに重たいので、Consul サーバ専用のホストとして構築するべきらしいです。
逆にクライアントはとても軽量なので、他のサービスと一緒に動作させて問題ありません(というかそうしなければ意味が無い)。
つまり、Consul を使ったクラスタでは、Consul サーバのためだけに最低3台の専用のホストが必要となります。
・・・ちょっとした小規模クラスタに使う感じではありませんね。