Serf/Consulで管理を自動化! ~実践的な手法を紹介~

第6回Consulのクラスタ構成とサービス検出

サービス検出を使った自動化を行うために、Consulを使って具体的にクライアント・サーバ構成を構築しましょう。複数のサーバ上でサービスを定義し、NginxのDNSラウンドロビンを行う方法を見ていきます。

サービスの可用性を高めるConsul

複数台のサーバ構成によって、Webサーバであれば通信量やアクセスの負荷分散を図ることができます。あるいは、データベースやキャッシュ用のサーバを冗長化構成にすることで、障害発生時におけるサービスの継続性(稼動時間)を高めることもできます。

クラウド技術の普及により、インフラであるサーバ環境は簡単に増減できるようになりました。その一方で、負荷分散や冗長化は、手動で行う設定に頼りがち。動的に変わるサーバ環境に応じて設定を変更するためには、何らかの手間なり工夫が求められています。

このような課題を簡単に解決すべく登場したのがConsulです。Consulが持つ機能の1つにサービス検出(service discovery)があります。Consulが認識するサービスとは、対象となるWebサーバやデータベースのデーモンが存在しているかどうかだけでに限りません。指定のポート番号に対するアクセスの正常性も判別することができます。

また、Consulは独自にDNSを持ちます。そのため、ローカル環境やプライベートなネットワーク空間内でも利用可能です。活用できるのは、ホスト名からIPアドレスの名前解決を自動的に行えるだけでなく、サービス検出に応じた動的な名前解決もできます。これを使えば、クライアント側はホスト名を定義しておくだけで、正常に稼働しているキャッシュ用サーバやデータベースのIPアドレスを知るような応用が可能です。

Consulは、そのサービス状況をConsulのDNSやHTTPのインターフェースから参照できるだけではありません。サービスが正常か異常か、それらの変化が発生したタイミングで、任意の処理を自動的に行うこともできます。

このConsulの機能を使えば、Webサーバの負荷分散の自動化も行えます。Webサーバ上でApacheやNginxなどのサービスが応答する間、負荷分散の対象となるだけでなく、正常に応答できないときは自動的に対象から外すことも容易になります。

Nginxの負荷分散にConsulを使うには

それでは、実際にConsulを使って、サービス検出の流れや挙動を見ていきましょう。ここでは、ローカル環境でNginxのDNSラウンドロビンを行います。

同一ネットワーク上で、3台のサーバ(インスタンスや仮想サーバ等)を用意します。それぞれのサーバ上でconsulエージェントを稼働し、Nginxの負荷分散環境を構築します図1⁠。

  • Consulサーバ:HTTP・DNSインターフェースを持つ。1台(192.168.39.3)のみ。
  • Consulクライアント:サービスが稼働する環境を想定。2台(192.168.39.11、192.168.39.12)で構成。
図1 ConsulでNginxの負荷分散イメージ
図1 ConsulでNginxの負荷分散イメージ

Consulサーバのセットアップ

Consulのセットアップの他に、DNSの名前解決用にBINDとdnsmasqをセットアップします。

まず、ConsulサーバではConsulエージェントを⁠server⁠モードとして起動します。

$ wget -O 0.5.0_linux_amd64.zip https://dl.bintray.com/mitchellh/consul/0.5.0_linux_amd64.zip
$ unzip 0.5.0_linux_amd64.zip
$ sudo cp consul /usr/bin/consul

セットアップ後は、Consulサーバを次のように実行します。nohupを使ってバックグラウンドでConsulサーバを起動するのは、ログアウト後もエージェントを起動し続けるためです。

$ nohup consul agent -server -bootstrap-expect=1 -data-dir=/tmp/consul -bind=192.168.39.3 -node=server -ui-dir=/opt/consul/dist &

正常に起動したかどうかは、consul membersコマンドでも確認できます。⁠Type」「server」となっていることに注意します。

# consul members
Node    Address            Status  Type    Build  Protocol
server  192.168.39.3:8301  alive   server  0.5.0  2

次にdnsmasqの設定を行います。ConsulのDNSインターフェースはポート8600を使用するため、アプリケーションやdig等のコマンドでConsulの名前解決を行う場合は、都度明示しなくてはいけません。ポートを指定しなくても、アプリケーションやcurlping等のツールで名前解決を手軽に行うのがdnsmasqです。

以下はCentOSにおけるセットアップ例です。

# yum -y install dnsmasq
# cp -p /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
# echo "server=/consul/127.0.0.1#8600" >> /etc/dnsmasq.conf
# echo "strict-order" >> /etc/dnsmasq.conf

必要に応じて/etc/resolv.confも書き換えます。自分自身で名前解決を行うためnameserver 127.0.0.1を既存の指定よりも前の行に記述します。

nameserver 127.0.0.1
nameserver 8.8.8.8

それからdnsmasqを起動します。

# chkconfig dnsmasq on
# service dnsmasq start

正常に名前解決を行えるか確認します。hostコマンドを使い、serverというノード名称を持つIPアドレスを確認します。

$ host server.node.consul
server.node.consul has address 192.168.39.3

名前解決の確認ができれば、同様にpingなどコマンドライン上から利用できることがわかります。

$ ping -c 3 server.node.consul
PING server.node.consul (192.168.39.3) 56(84) bytes of data.
64 bytes from consul-server (192.168.39.3): icmp_seq=1 ttl=64 time=0.008 ms
64 bytes from consul-server (192.168.39.3): icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from consul-server (192.168.39.3): icmp_seq=3 ttl=64 time=0.035 ms

--- server.node.consul ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.008/0.028/0.042/0.015 ms

これでConsulサーバー側の準備が完了しました。

Consulクライアントのセットアップ

次は、Consulノード側のセットアップです。Consulエージェントを⁠client⁠モードとして稼働し、サーバ上のNginxを⁠web⁠という名称のサービスとしてConsulに登録します。1台目のNginxサーバの名前をnode12台目をnode2と設定することにします。

まず始めに、Nginxのセットアップを行います。

# yum -y install nginx
# service nginx start
# echo 'node1' > /usr/share/nginx/html/consul.html

curlを使って動作確認をします。正常に設定が終わっていれば、次のようにnode1という文字列が画面に表示されます。これはホスト名を確認用するためでなく、サービス正常性を確認するConsulのヘルスチェック用としても使います。

$ curl http://127.0.0.1/consul.html
node1

次にConsulエージェントのセットアップです。先ほどのConsulサーバと同様に行います。

$ wget -O 0.5.0_linux_amd64.zip https://dl.bintray.com/mitchellh/consul/0.5.0_linux_amd64.zip
$ unzip 0.5.0_linux_amd64.zip
$ sudo cp consul /usr/bin/consul

以上の動作を2台目のサーバでも繰り返します。このとき、ホスト名のnode1の箇所はnonde2に置き換えての実行をお願いします。

Consulへのサービス登録準備

それではConsulにサービスを登録します。サービス登録には、Consulエージェントを起動し、Consulサーバに接続する必要があります。

あらかじめ、設定ファイルを置くためのディレクトリを作成します。さらに、サービス定義用のファイルを作成します。ファイルの書式はJSON形式です。

# mkdir /etc/consul.d/
# cat << EOF > /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"
    }
  }
}
EOF

ここでは⁠web⁠という名称のサービスを定義します。タグにはnginxを指定しています。重要なのはcheckで囲まれたブロックです。script⁠web⁠サービスが正常(passed)とみなす条件を指定しています。内容はcurlコマンドでconsu.htmlが取得可能かで判断します。またintervalは、このscriptを実行する間隔です。10sは10秒ごとにという指定です(任意の秒数を指定できます⁠⁠。さらにtimeoutでタイムアウト秒数を指定しました。

今回はcurlを使いましたが、pingでも任意のコマンドを指定できます。Consulは終了コードによってサービスの状態を区別します。終了コード0であれば正常(passing)と見なされます。1の場合は警告(warning)であり、終了コードが無い場合は障害(failing)となります。

Consulエージェントの起動とサービス登録

準備が整ったら次のようにConsulを起動します。

$ consul agent -data-dir=/tmp/consul -node=node1 -bind=192.168.39.11 -config-dir=/etc/consul.d/ -join=192.168.39.3

ここで指定したオプションは、それぞれ次のような意味があります。

-data-dir
Consul用のデータ保存に使うディレクトリです。
-node
このConsulノードの名称であり、Consulクラスタ内で重複できません。
-bind
使用するIPアドレスを明示しています。
-config-dir
設定ファイル用のディレクトリを指定し、対象以下の.jsonファイルを読み込みます。
-join
ConsulサーバのIPアドレスを指定します。この例では192.168.39.3です。

正常に実行すると、次のようにメッセージが画面に表示され、この時点でConsulクライアントとして登録されただけでなく、webサービスが認識されたことがわかります。

==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Joining cluster...
    Join completed. Synced with 1 initial agents
(省略)
    2015/04/18 23:15:20 [INFO] consul: adding server server (Addr: 192.168.39.3:8300) (DC: dc1)
    2015/04/18 23:15:21 [INFO] agent: Synced service 'web'
    2015/04/18 23:15:25 [INFO] agent: Synced check 'service:web'

consul membersコマンドを実行すると、Consulクラスタとしてのメンバ情報も確認できます。

$ consul members
Node    Address             Status  Type    Build  Protocol
server  192.168.39.3:8301   alive   server  0.5.0  2
node1   192.168.39.11:8301  alive   client  0.5.0

動作確認として、Consulサーバ上(192.168.39.3)で名前解決の確認を行いましょう。Consulはノード(サーバ、クライアントどちらも含みます)のIPアドレスをノード名称で名前解決できます。

# dig node1.node.consul a
(省略)
;; QUESTION SECTION:
;node1.node.consul.             IN      A

;; ANSWER SECTION:
node1.node.consul.      0       IN      A       192.168.39.11

この機能を使えば、クラウドのように動的にIPアドレスが変わる環境でも、Consulエージェントでクラスタを構成する限り、同じホスト名称でアクセスし続けることができます。

更にwebサービスの登録状況を確認します。Consulは登録したサービス名で名前解決が可能です。先ほど登録したサービスwebの情報を持つIPアドレスを調べるには、web.service.consulという名前で名前解決を試みます。正常に処理されると、次のようにIPアドレスを確認できます。

$ dig web.service.consul a
(省略)
;; ANSWER SECTION:
web.service.consul.     0       IN      A       192.168.39.11

同様にcurlを使って動作確認してみましょう。ホスト名をweb.service.consulと指定することで、Consulとdnsmasqが自動的に名前解決をしてくれます。

$ curl http://web.service.consul/consul.html
node1

2台目のサービスを登録

それでは、もう1台のConsulクライアントを起動します。先ほどと設定は同じですが、-nodeのノード名称と-bindのIPアドレスが違うだけです。

$ consul agent -data-dir=/tmp/consul -node=node2 -bind=192.168.39.12 -config-file=/etc/consul.d/ -join=192.168.39.3

この状態でconsul membersコマンドを実行すると、新しくConsulクライアントが追加されたことがわかります。

$ consul members
Node    Address             Status  Type    Build  Protocol
node1   192.168.39.11:8301  alive   client  0.5.0  2
node2   192.168.39.12:8301  alive   client  0.5.0  2
server  192.168.39.3:8301   alive   server  0.5.0  2

また、サービスwebが登録されているかどうかも、再びdigコマンドで確認します。すると、次のように2つのIPアドレスが表示されることがわかります。

# dig web.service.consul a
(省略)
;; QUESTION SECTION:
;web.service.consul.            IN      A

;; ANSWER SECTION:
web.service.consul.     0       IN      A       192.168.39.11
web.service.consul.     0       IN      A       192.168.39.12

この状態でwebサービスに対するDNSラウンドロビンが行えるようになります。curlコマンドを何度か実行すると、2台に交互にアクセスしていることが確認できます。

$ curl http://web.service.consul/consul.html
node1
$ curl http://web.service.consul/consul.html
node2
$ curl http://web.service.consul/consul.html
node1
$ curl http://web.service.consul/consul.html
node2

サービス障害と名前解決

引き続きConsulのサービス検出機能を見ていきます。例えば、障害によってNginxが停止したらどうなるでしょうか。node1サーバにログインし、nginxを停止します。

# service nginx stop
Stopping nginx:                                            [  OK  ]

Consulサーバ上でdigを実行すると、停止したnode1のIPアドレスは表示されず、node2のIPアドレスのみ表示されていることがわかります。

dig web.service.consul a
(省略)
;; QUESTION SECTION:
;web.service.consul.            IN      A

;; ANSWER SECTION:
web.service.consul.     0       IN      A       192.168.39.12

まとめ

今回はNginxのサービス検出をDNSで行う方法を見てきました。Nginxに限らず、Consulはスクリプトで識別できるものであれば、プロセスの存在状況や、APIの処理結果など、任意のものをサービスとして定義できます。

次回はこのサービス検出をトリガとして、自動処理に応用したり、複数台のサーバー上でコマンドを実行する方法を見ていきます。

参考情報

おすすめ記事

記事・ニュース一覧