サービス・ディスカバリのためのConsul入門

2015年8月24日(月)
前佛 雅人(ぜんぶつ まさひと)

Serfの足りない機能を補うConsul

前回の記事の復習になりますが、Serf(https://serfdom.io)の主要な役割は、オーケストレーション(自動的に複数のサーバなどのインフラ上で処理すること)と、クラスタ上のメンバ管理です。

Serfを単純なツールとして見た場合は、オーケストレーションを簡単に行える便利なツールと言えるでしょう。しかし、実際の運用シーンを考えますと、次のような弱点が浮かび上がります。

  • メンバ管理はSerfエージェント間の通信正常性が基準であり、アプリケーションやミドルウェアの状況に応じた自動処理が行えない(サーバは稼働しているが、ウェブサーバやデータベースが停止するような状況)
  • 何らかの状態を保持する仕組みがないので、冗長化の仕組みは自分で考慮する必要がある
  • Serfクラスタとの通信には独自のCLIかAPIを使う必要があり、他のツールとの親和性が低い

これらの問題を解決すべく登場したのが、Consul(https://consul.io/)です。

Consulの登場背景

Consulも、Serfと同じくHashiCorp(https://hashicorp.com)により開発されています。他のツール同様、ソースコードはオープンソースとして公開されており、開発体制もGitHub(https://github.com/hashicorp/consul)を通して開かれています。

ここでもう少し、Consul登場の背景について見てみましょう。かつてのインフラでは、納品時におけるネットワークやリソースに関する環境設定は固定されがちでした。一度ホスト名やIPアドレスが割り振られると、拡張やサービスの見直しの機会がなければ、多くの場合はそのまま据え置かれることになります。

ところがクラウドや仮想化に加え、最近ではコンテナも使われるようになり、状況が変わってきます。環境は固定ではなく、動的に変わることも普通になってきました。例えば、クラウド環境におけるスケールアウトやスケールインは、トラフィック量や処理の負荷に応じて増減できるため、上手く使いこなすことは、サービスや事業における競争力となり始めています。

それと同時に動的に変わる環境に適合するように、監視や運用手法にも変化が求められています。クラウドによって、スケールイン・スケールアウトする環境においては、IPアドレスやシステムリソースが固定ではないケースがあるためです。そのため、旧来の手法では手間が増えてしまうことになります。

そこで出てきたのが、「サービス・ディスカバリ(Service Discovery)」と呼ばれる手法です。インフラ側の視点ではなく、アプリケーション側のサービス、つまり、特定のポートが正常な応答をしているかどうか、サーバ内で特定のプロセスが正常に動作しているかどうかを発見します。そして、その結果をサービス単位でIPアドレスやポート番号などのリスト化する手法が求められています。

Consulの構成とサービス・ディスカバリ機能

Consulは多くの機能を備えていますが、ここでは中心的な機能であるサービス・ディスカバリを見ていきましょう。なおサービス・ディスカバリ(Service Discovery)とは、日本語では「サービスの発見」や「検出」という意味あいで使われています。

サービス・ディスカバリ機能によって、私達はConsulを通して、どのサーバ上でどのようなサービス(アプリケーション)が動作しているかに加え、そのポート番号、IPアドレス、ホスト名などをリアルタイムで知ることができるようになります。この情報を集めるのが「Consulノード」の役割です。Consulノードは、各々のホスト上でサービス状態の監視を、ヘルスチェック機能を通して行います。

Consulノードはサービス状況に変化が発生すると、「Consulサーバ」に対してデータを送信します。受け取ったデータはConsulサーバ上のキーバリュー・ストア(KVS)に保管されます。そしてConsulは、その情報をHTTPインターフェース(GUI)、DNSインターフェース、コマンドラインという3つのインターフェースを通して提供しています(図1)。

Consulの機能と一般的な構成

図1:Consulの機能と一般的な構成

Consulは監視ツールなの?

一見すると、Consulは監視ツールのように見えるかもしれません。確かにヘルスチェックを備えていますし、GUIを通してサービスの正常性の確認もできます。しかし、過去の時系列データに関する保存は行わないという点で、一般的な統合監視ツールとは決定的に異なります。あくまでもConsulは「現在の状態」しか保持しません。

これは、そもそもの「監視」に対する考え方が違うためです。これまでの監視ツールであれば、状態変化を検出し、アラートを送信し、それを受け取った人間が対応するという流れでした。人間が状況を判断するためには、アラートが出る前の情報も参照して、比較する必要もあったからです。一方のConsulであれば、状況変化時に人間を介さず機械的に処理を行えます。

また通常の監視ツールは、サーバがエージェントに対して通信して情報を収集するのに対し、Consulはノード(エージェント)が能動的にサーバに対してデータを送るという違いもあります。

Consulの動作環境を準備

それでは、実際にConsulの動作環境を整えてみましょう。ここではConsulの動作を理解するために、最小構成として1つのサーバ上にConsulをセットアップします(図2)。

Consulの最小構成

図2:Consulの最小構成

Consulのセットアップは非常にシンプルです。バイナリファイルが配布されているので、それをダウンロードし、起動します。以下はLinuxの64bit版バイナリをセットアップする例です。なお、ダウンロード用のページ(https://consul.io/downloads.html)では、Mac OS XやWindows用のバイナリも配布されています。

ダウンロードと展開後に、「consul version」コマンドでバージョン番号が確認できれば準備完了です。

$ wget -O 0.5.2_linux_amd64.zip  https://dl.bintray.com/mitchellh/consul/0.5.2_linux_amd64.zip
$ unzip 0.5.2_linux_amd64.zip
$ sudo cp ./consul /usr/local/bin/consul
$ consul version
Consul v0.5.2
Consul Protocol: 2 (Understands back to: 1)

Consulエージェントの起動

エージェントの初回起動時は、サーバモードとして動作するための「-server」オプションを指定します。今回は1台しか使わないため「-bootstrap-expect 1」を指定していますが、実環境ではデータ保全性向上のために3台以上での運用が推奨されています。そして、「-data-dir」でクラスタ用のデータ保管ディレクトリを指定します。最後の「-bind」は必須ではありませんが、クラウドや仮想環境などで複数のネットワーク・インターフェースを持つ環境における明示のためによく用いられます。

$ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -bind <IPアドレス>

起動すると画面上に様々なメッセージが表示され、「consul」を通して多様なコマンドが利用できるようになります。consulのクラスタ状態を確認するには「consul members」コマンドを実行します。どのようなホスト名・IPアドレスを持つ環境が、クラスタに所属しているかが分かります。

$ consul members
Node            Address              Status  Type    Build  Protocol  DC
docker2.zem.jp  128.199.84.221:8301  alive   server  0.5.2  2         dc1

consulコマンドを使わなくても、メンバ一覧などの情報を参照することもできます。curlコマンドを使って、HTTPインターフェースにアクセスしてみます。HTTPインターフェースは、デフォルトではTCPポート8500番です。「/catalog/nodes」からノード情報を取得できます。

$ curl http://127.0.0.1:8500/v1/catalog/nodes
[{"Node":"docker2.zem.jp","Address":"128.199.84.221"}]

このようにコマンドラインと同様の結果を得られます。もう1つのDNSインターフェースを試してみましょう。DNSインターフェースは、UDPポート8600番です。これにより、正常なホスト名やサービスが稼働しているかどうかを知ることができます。例えば、特定のホスト名のIPアドレスを知りたい場合は「.node.consul」で名前解決を行います。

$ dig @127.0.0.1 -p 8600 docker2.zem.jp.node.consul

;; QUESTION SECTION:
;docker2.zem.jp.node.consul.    IN      A

;; ANSWER SECTION:
docker2.zem.jp.node.consul. 0   IN      A       128.199.84.221

この例では「docker2.zem.jp」というホスト名のIPアドレスが「128.199.84.221」であることが分かります。仮にこのホストのIPアドレスが変わったとしても、Consulを通せば同じホスト名でアクセスできます。そのため、クラウドのように動的にIPアドレスが変わったとしても一意にアクセスできるわけで、負荷分散や障害時の切り替えなどを、アプリケーション側には手を加えずに行えるようになります。

Consulノードの構成と、オーケストレーション機能

続いて、いよいよConsulを使ったサービス・ディスカバリを試してみましょう。別のサーバ環境を用意し、そこでConsulノードをセットアップしてクラスタを構成します。各々のサーバ上では、先ほど同様にバイナリをセットアップしておきます。

Consulサーバとノードでサービス検出

図3:Consulサーバとノードでサービス検出

クライアントとしてConsulノードを起動する場合も、サーバの時とほぼ同様です。異なるのは「-join」オプションで、Consulサーバを指定する必要があります。

$ consul agent -data-dir /tmp/consul -bind <IPアドレス> -join <ConsulサーバのIPアドレス>

正常に通信ができると、自動的にクラスタが構成されます。「consul members」コマンドを実行すると、次のように複数のホスト情報が確認できます。

 consul members
Node            Address               Status  Type    Build  Protocol  DC
docker2.zem.jp  128.199.84.221:8301   alive   server  0.5.2  2         dc1
node1.zem.jp    128.199.130.243:8301  alive   client  0.5.2  2         dc1
node2.zem.jp    128.199.137.114:8301  alive   client  0.5.2  2         dc1

この状態で、簡単なオーケストレーション機能を試すことができます。コマンドラインでの指定方法は、「consul exec 」のような書式となります。例えば各サーバでuptimeを実行したいときは「consul exec uptime」と実行します。

$ consul exec uptime
    docker2.zem.jp:  23:42:51 up 2 days, 23:09,  4 users,  load average: 0.00, 0.01, 0.05
    docker2.zem.jp:
==> docker2.zem.jp: finished with exit code 0
    node1.zem.jp:  23:42:58 up 40 min,  1 user,  load average: 0.00, 0.00, 0.00
    node1.zem.jp:
==> node1.zem.jp: finished with exit code 0
    node2.zem.jp:  23:42:58 up 12 min,  1 user,  load average: 0.00, 0.01, 0.00
    node2.zem.jp:
==> node2.zem.jp: finished with exit code 0
3 / 3 node(s) completed / acknowledged

このように、各サーバ上でuptimeを実行した結果をまとめて参照できます。この機能を応用すると、アプリケーションの一括セットアップや、バージョンの確認、プロセスの起動停止などもConsulを通して行えるようになります。

このconsul execは、リモートからSSHでログインするのではなく、Consulエージェントを通してSerfのように一斉実行している点がポイントです。Parallel SSHやFabricを用いても似たような機能を実現できますが、ConsulはSSHサーバの稼働していないコンテナの中でも実行できる点がユニークです。Consulには多くの機能がありますが、このオーケストレーション機能を使うだけでも、試してみる価値はあるのではないでしょうか。

サービス・ディスカバリ機能を試す

それでは、次にサービス・ディスカバリ機能を見ていくために、ウェブサーバのDNSラウンドロビンを試してみましょう。各クライアントのサーバ上ではApacheのセットアップを行い、接続先を識別するためにホスト名をドキュメント・ルートに置きます。

$ sudo yum -y install httpd
$ sudo echo "node1" > /var/www/html/index.html
$ sudo service httpd start
$ curl http://127.0.0.1
node1

次にConsulの設定です。エージェントが起動していますので、コンソール画面を開いたままの場合は「ctrl+C」で中断します。それ以外の場合は、killコマンドで対象PIDを停止します。

それから、次のようにファイルを作成し、webという名称のサービスを定義します。

$ sudo mkdir /etc/consul.d
$ sudo vi /etc/consul.d/web.json

web.jsonファイルの内容は次の通りにします。

web.json

{
  "service": {
    "name": "web",  # サービス名「web」を定義
    "port": 80,               # ポート番号「80」
    "check": {                # ヘルスチェックはcurlでローカルのポートにアクセス
      "script": "curl localhost:80 >/dev/null 2>&1",
      "interval": "15s"     # チェック間隔は「15秒」
    }
  }
}

それから、consulエージェントを再起動します。先ほどと違うのは「-config-dir /etc/consul.d」を指定する点です。これは、指定したディレクトリ内にある拡張子.jsonのファイルを、Consul起動時に設定ファイルとして自動的に読み込ませるための指定です。

# consul agent -data-dir /tmp/consul  -bind <IPアドレス> -join <ConsulサーバのIPアドレス> -config-dir /etc/consul.d

エージェントが起動すると、コンソール画面上に「service:web」という名称でサービスが認識されたことが分かります。

    2015/06/26 00:14:39 [INFO] agent: Synced check 'service:web'

複数のサーバがある場合は、それぞれ作業を繰り返します。

さて、登録されたサービスを確認するために、再びdigコマンドで確認してみましょう。今度は「.service.consul」という名称で名前解決を試みます。

# dig @127.0.0.1 -p 8600 web.service.consul

;; QUESTION SECTION:
;web.service.consul.            IN      A

;; ANSWER SECTION:
web.service.consul.     0       IN      A       128.199.137.114
web.service.consul.     0       IN      A       128.199.130.243

結果として表示されるのは、ウェブサーバが正常起動しているサーバの情報です。SRVレコードで確認すると、さらに詳細なポート番号などの確認ができます。

 dig @127.0.0.1 -p 8600 web.service.consul SRV

;; QUESTION SECTION:
;web.service.consul.            IN      SRV

;; ANSWER SECTION:
web.service.consul.     0       IN      SRV     1 1 80 node2.zem.jp.node.dc1.consul.
web.service.consul.     0       IN      SRV     1 1 80 node1.zem.jp.node.dc1.consul.

;; ADDITIONAL SECTION:
node2.zem.jp.node.dc1.consul. 0 IN      A       128.199.137.114
node1.zem.jp.node.dc1.consul. 0 IN      A       128.199.130.243

dnsmasqを使った名前解決とラウンドロビン

これまで見てきた方法は、都度digコマンドでポート8600を指定する必要がありました。これはdnsmasqを使うことで、ローカルでConsulのサービスと連携した名前解決が簡単に行えます。以下は、CentOSにおけるdnsmasqの設定手順です。まずパッケージをセットアップします。

# yum -y install dnsmasq

それから、設定ファイルにConsulサーバのIPアドレスとポート番号の情報を追記します。次の例はConsulサーバ上で指定しています。

# 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」の記述を追加します。これは、dnsmasqを使ってConsulで名前解決を行うための指定です。最後に、dnsmasqを起動します。

# service dnsmasq start
dnsdomainname: Unknown host
Starting dnsmasq:                                          [  OK  ]

これでdig使用時にポート番号を指定せずに名前解決ができるようになります。

ウェブサービスがDNSラウンドロビンされていることを確認してみましょう。curlコマンドを使い、ホスト名「web.service.consul」にアクセスすると、アクセス先のホスト名が都度変わることが確認出来ます。

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

ここではウェブサーバの例を扱いましたが、キャッシュサーバやデータベース、アプリケーションのエンドポイントなどにも応用することができます。

まとめ

Consulが必要とされているのは、動的に変わる環境においてサービス・ディスカバリを行えるためです。この機能があるので、動的に変化するインフラ上でも、作業をスムーズに行ったり、様々な処理の自動化にも応用したりすることができます。

次回はこれまでのまとめとして、開発から運用に至るまでの一貫した流れを、HashiCorpが提供するAtlas(https;//atlas.hashicorp.com)を使って見ていきます。

※本稿は、2015年8月現在のConsul v0.5.2に対して確認を行っています。

【参考文献】

Consul(アクセス:2015/08)

https://consul.io/

Consul Documentation(アクセス:2015/08)

https://consul.io/docs/index.html

著者
前佛 雅人(ぜんぶつ まさひと)
クリエーションライン株式会社

Technology Evangelist
ホスティングサービスで運用保守サポートに携わった後、現職へ。サポート業務や新技術検証や開発業務を行う傍ら、実家で農作業のため東京と富山を往復する日々。趣味で監視や自動化に関するOSS検証や翻訳を行う。とりわけ運用・監視の省力化・最適化に興味。辛口の日本酒が大好き。
Twitter: @zembutsu (https://twitter.com/zembutsu)

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています