CLOVER🍀

That was when it all began.

WildFly Swarm+Consul(+Dnsmasq)でService Discovery

WildFly Swarmには、Service Discoveryのための仕組みとしてTopologyがありますが、その実装手段としていくつかの
方法を提供しています。

Topology

今回は、Consulを試してみようかなと思います。

Topology using Hashicorp Consul

Consul?

Consulというのは、HashiCoprの提供するService Discoveryの仕組みです。

Consul

Introduction To Consul

主に以下のような機能を持ちます。

  • Service Discovery
  • Health Check
  • KVS

Consul自体はAgentとして各サーバーで動作させるものですが、動作タイプにServerとClientがあり、通常は
Serverは(データセンターあたり)3台または5台での構成を推奨しています。

Bootstrapping a Datacenter

ConsulにはDNSの機能もあり、名前解決を行うことができます。

DNS Interface

とはいえ、ConsulのDNSは8600ポートで提供されるので、他のDNS製品と合わせて通常の名前解決の仕組みに乗せる例がこちらに記載されています。

DNS Forwarding

今回は、このドキュメントに習いDnsmasqを使ってみたいと思います。

Dnsmasq - network services for small networks.

WildFly Swarm+Consul

続いて、WildFly SwarmとConsulの統合機能について。

Topology using Hashicorp Consul

Consulは、NodeまたはServiceで名前解決を行うことができます。

DNS Interface

WildFly SwarmのConsul向けの統合機能を使うと、WildFly Swarmの起動時にConsul Agentに対してServiceとして
自分自身を登録してくれるようになります。

お題

で、今回はWildFly SwarmとConsulを使って、こんなお題で試してみようと思います。

  • Consul Agent(Server)3台でServer構成
  • Consul Agent(Client)2台に、WildFly Swarmで作ったサンプルJAX-RSアプリケーションを載せてService構成
  • Consul Agent(Client)にDnsmasqをインストールし、curlでService名を解決してアクセス

つまり、こんな(クラスタ)構成です。

ConsulとWildFly Swarmについては、以下の条件で。

  • Consul Agent(Server) … 172.17.0.2 〜172.17.0.4(ホスト名:consulserver1 〜 consulserver3)
  • Consul Agent(Client - WildFly Swarm) … 172.17.0.5 〜 172.17.0.6(ホスト名:consulswarmclient1 〜 consulswarmclient2)
  • Consul Agent(Client - curl) … 172.17.0.1(ホスト名:consulcurlclient)

Consulのインストール

全Serverで行うので、共通的に1度紹介。

Consulのダウンロードページから、Consulをダウンロードします。

Download Consul

もしくは、wgetなどで。

$ wget https://releases.hashicorp.com/consul/0.7.2/consul_0.7.2_linux_amd64.zip

展開します。

$ unzip consul_0.7.2_linux_amd64.zip 
Archive:  consul_0.7.2_linux_amd64.zip
  inflating: consul

「consul」というファイルが現れるので、このファイルを起動オプションを付けて実行します。

$ ./consul agent option...

また、Consul Agentの起動に必須となるのはData Directory(-data-dirで指定)なので、こちらは
今回はとりあえずカレントディレクトリに「data」というディレクトリを作って、こちらを指定
するものとします。

$ ./consul agent -data-dir=data option...

オプションについては、Agentの種類で異なるので都度紹介します。

では、だいぶ前置きが長くなりましたがはじめていきましょう。

Consul Agent(Server)のクラスタ構成

では、まずConsul Agent(Server)を起動して、クラスタを構成します。

追記
Consulの起動時のオプション、クラスタ構成についてあいまいだったので、別エントリで確認しました。
Consulでクラスタを構成、Service設定をしてみる - CLOVER

各サーバーで微妙にオプションが違うので、全部記載。

## consulserver1(Leader)
$ ./consul agent -server -data-dir=data -client=172.17.0.2 -bootstrap-expect=1 -ui

## consulserver2
$ ./consul agent -server -data-dir=data -join=172.17.0.2 -client=172.17.0.3 -ui

## consulserver3
$ ./consul agent -server -data-dir=data -join=172.17.0.2 -client=172.17.0.4 -ui

オプションを説明します。

  • agent … Agentとして起動することを表します
  • -server … Serverとして起動することを表します
  • -data-dir … Consulが内部で状態保存に使用するData Directoryを指定します
  • -client … Clientから使用する際のIPアドレスを指定します(デフォルトは127.0.0.1となっているため、外部から接続できません)
  • -ui … 付けておくと、ConsouのWebインターフェースを見ることができます
  • -join … クラスタに参加する先のIPアドレスを指定します(最初のNode以外に付けています)
  • -bootstrap-expect … Consulが起動した際に、期待するConsulインスタンスの数。これを下回っているとリーダーを選出できません。今回は1に設定しています。

「-client」でバインドしたIPアドレスで公開されるのは、HTTP、HTTPSDNS、RPCなので、他のサーバーから
Webインターフェースを見る時に困ったりすると思います。DNSは、ローカルにConsul Agentを動かして
いることが多そうな気がするので、そんなに困らないかも…?

この状態で、今回の構成であれば「http://172.17.0.2:8500/ui/」にアクセスすると、Consulの状態を
確認することができます。
※1度、左の「consul」と書かれたラベルをクリックすると、左に登録してあるNodeが展開されます

デフォルトは画面上部の「SERVICES」が選ばれた状態となっているので、「NODES」を選ぶと登録してある
Nodeの一覧が見れます。

3つのNodeがいることが確認できます。

また、consulのコマンドで確認してもOKです。

$ ./consul members -rpc-addr=172.17.0.2:8400
Node           Address          Status  Type    Build  Protocol  DC
consulserver1  172.17.0.2:8301  alive   server  0.7.2  2         dc1
consulserver2  172.17.0.3:8301  alive   server  0.7.2  2         dc1
consulserver3  172.17.0.4:8301  alive   server  0.7.2  2         dc1

Consul Agent(Client - WildFly Swarm)をクラスタに参加させる

続いて、WildFly Swarmを含めたConsul Agentをクラスタに参加させます。

まずは、Agentの起動。

## consulswarmclient1
$ ./consul agent -data-dir=data -join=172.17.0.2

## consulswarmclient2
$ ./consul agent -data-dir=data -join=172.17.0.2

今回は、UIは外部から見れなくてもいいかなと思ったので、「-client」と「-ui」は付けていません。

ちなみに、この時点ではUI上の「SERVICES」には変化がありませんが、

「NODES」には起動したNodeが追加されます。

続いて、WildFly Swarmのアプリケーションを作成しましょう。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.littlewings</groupId>
    <artifactId>consul-integration</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <scala.major.version>2.12</scala.major.version>
        <scala.version>${scala.major.version}.1</scala.version>
        <scala.maven.plugin.version>3.2.2</scala.maven.plugin.version>

        <failOnMissingWebXml>false</failOnMissingWebXml>

        <wildfly.swarm.version>2017.1.1</wildfly.swarm.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.wildfly.swarm</groupId>
                <artifactId>bom</artifactId>
                <version>${wildfly.swarm.version}</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.wildfly.swarm</groupId>
            <artifactId>jaxrs</artifactId>
        </dependency>
        <dependency>
          <groupId>org.wildfly.swarm</groupId>
          <artifactId>topology-consul</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>consul-integration</finalName>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>${scala.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <scalaVersion>${scala.version}</scalaVersion>
                    <args>
                        <arg>-Xlint</arg>
                        <arg>-unchecked</arg>
                        <arg>-deprecation</arg>
                        <arg>-feature</arg>
                    </args>
                    <recompileMode>incremental</recompileMode>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.wildfly.swarm</groupId>
                <artifactId>wildfly-swarm-plugin</artifactId>
                <version>${wildfly.swarm.version}</version>
                <configuration>
                    <mainClass>org.littlewings.wildflyswarm.consul.App</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

特徴的なのは、「topology-consul」への依存関係を加えたことですね。

        <dependency>
          <groupId>org.wildfly.swarm</groupId>
          <artifactId>topology-consul</artifactId>
        </dependency>

個人的には珍しくmainクラスを指定しています。

                <configuration>
                    <mainClass>org.littlewings.wildflyswarm.consul.App</mainClass>
                </configuration>

テスト用のJAX-RSリソースクラス。単純にホスト名を返す実装とします。
src/main/scala/org/littlewings/wildflyswarm/consul/HostNameResource.scala

package org.littlewings.wildflyswarm.consul

import java.net.InetAddress
import javax.ws.rs.core.MediaType
import javax.ws.rs.{GET, Path, Produces}

@Path("hostname")
class HostNameResource {
  @GET
  @Produces(Array(MediaType.TEXT_PLAIN))
  def get: String = InetAddress.getLocalHost.getHostName
}

続いて、mainクラス。
src/main/scala/org/littlewings/wildflyswarm/consul/App.scala

package org.littlewings.wildflyswarm.consul

import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.spec.WebArchive
import org.wildfly.swarm.Swarm
import org.wildfly.swarm.jaxrs.JAXRSArchive
import org.wildfly.swarm.topology.TopologyArchive

object App {
  def main(args: Array[String]): Unit = {
    val swarm = new Swarm(args: _*)
    val deployment = ShrinkWrap.create(classOf[JAXRSArchive])
    //deployment.as(classOf[TopologyArchive]).advertise()  // AdvertiseがWARの名前になる
    deployment.as(classOf[TopologyArchive]).advertise("swarm")

    deployment.addResource(classOf[HostNameResource])

    swarm.start().deploy(deployment)
  }
}

WildFly SwarmのTopologyのドキュメントに沿って、TopologyArchive#advertiseすればいいのですが、ここでの
advertiseの値がConsulでのService名になります。

ふつうに実行してしまうと、内部のWARの名前ってこんなことになって

2017-01-14 12:37:26,160 INFO  [org.jboss.as.server] (main) WFLYSRV0010: Deployed "c166ab56-4056-475a-a81a-a8d0cf63e12a.war" (runtime-name : "c166ab56-4056-475a-a81a-a8d0cf63e12a.war")

指定するにはとても困るので、今回はService名を「swarm」としました。

あとはパッケージングします。

$ mvn package

で、起動。

## consulswarmclient1
$ java -Dswarm.bind.address=172.17.0.5 -jar /path/to/consul-integration-swarm.jar

## consulswarmclient2
$ java -Dswarm.bind.address=172.17.0.6 -jar /path/to/consul-integration-swarm.jar

なんで「-Dswarm.bind.address」を付けたかは、あとで説明します。

Webインターフェース上の「SERVICES」では、登録されたWildFly SwarmのServiceを見ることができます。

左に、「swarm」というサービスが増えていますね。これが、TopologyArchive#advertiseで指定した名前です。

あと、よーく見ると右上部の「TAGS」に「http」が増えていますね。こちらもWildFly Swarmが登録したものですが、
名前使われます。

とりあえず、簡単に動作確認。

$ curl http://172.17.0.5:8080/hostname
consulswarmclient1

$ curl http://172.17.0.6:8080/hostname
consulswarmclient2

OKですね。

Consul Agent(Client - curl)をクラスタに参加させる

起動したWildFly SwarmのConsulを使った確認に移る前に、先にcurlを使って確認する側のConsul Agentを起動しておきます。
※これは書き手の都合上です

$ ./consul agent -data-dir=data -join=172.17.0.2

これで、Consulクラスタのひととおりのメンバーが揃いました。

動作確認など

ここからは、Consul Agent(Client - curl)側から確認してみます。

digで動作確認

さて、名前解決の動作確認をしてみましょう。

Consulでは、NodeとServiceの名前解決を行うことができます。

DNS Interface

Nodeの名前解決を行う場合の書式は

<node>.node[.datacenter].<domain>

で、Serviceの名前解決を行う場合の書式は

[tag.]<service>.service[.datacenter].<domain>

となります。

まずはNode名から。ConsulのNode名はデフォルトはホスト名ですが、「-node」オプションで任意の名前に変更することもできます。

digで確認。digの向き先は、ローカルで動いているConsul AgentのDNSポート(8600)となります。bindでアドレスを変えたりしている
場合は、その内容に合わせてください。

Nodeについては、「Node名」.node.consulで確認することができます。Datacenter名を入れてもいいのですが、今回は省略します。

## consulserver1
$ dig @localhost -p 8600 consulserver1.node.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 consulserver1.node.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41638
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;consulserver1.node.consul.	IN	ANY

;; ANSWER SECTION:
consulserver1.node.consul. 0	IN	A	172.17.0.2

;; Query time: 60 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 21:43:24 JST 2017
;; MSG SIZE  rcvd: 59


## consulswarmclient1
$ dig @localhost -p 8600 consulswarmclient1.node.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 consulswarmclient1.node.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54267
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;consulswarmclient1.node.consul.	IN	ANY

;; ANSWER SECTION:
consulswarmclient1.node.consul.	0 IN	A	172.17.0.5

;; Query time: 5 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 21:43:38 JST 2017
;; MSG SIZE  rcvd: 64


## consulcurlclient
$ dig @localhost -p 8600 consulcurlclient.node.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 consulcurlclient.node.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2666
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;consulcurlclient.node.consul.	IN	ANY

;; ANSWER SECTION:
consulcurlclient.node.consul. 0	IN	A	172.17.0.1

;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 21:44:39 JST 2017
;; MSG SIZE  rcvd: 62

続いてServiceで確認。「swarm」というServiceが登録されているので、こちらを使います。

Serviceの場合は、「Service名」.service.consul、もしくは「タグ名」.「Service名」.service.consulとなります。

## タグを入れない場合
$ dig @localhost -p 8600 swarm.service.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 swarm.service.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23476
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;swarm.service.consul.		IN	ANY

;; ANSWER SECTION:
swarm.service.consul.	0	IN	A	172.17.0.5
swarm.service.consul.	0	IN	A	172.17.0.6

;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 21:45:38 JST 2017
;; MSG SIZE  rcvd: 70


## タグを入れた場合
$ dig @localhost -p 8600 http.swarm.service.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 http.swarm.service.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11862
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;http.swarm.service.consul.	IN	ANY

;; ANSWER SECTION:
http.swarm.service.consul. 0	IN	A	172.17.0.6
http.swarm.service.consul. 0	IN	A	172.17.0.5

;; Query time: 18 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 21:46:46 JST 2017
;; MSG SIZE  rcvd: 75

名前解決できていますね。

HTTP APIで確認

登録されているServiceやNodeなどを、HTTP APIで見ることもできます。

HTTP API

ここから、Serviceを登録したりすることも可能です。

今回は、NodeおよびServiceの一覧を見てみましょう。

Node一覧。

$ curl http://localhost:8500/v1/agent/members
[{"Name":"consulswarmclient1","Addr":"172.17.0.5","Port":8301,"Tags":{"build":"0.7.2:'a9afa0c","dc":"dc1","role":"node","vsn":"2","vsn_max":"3","vsn_min":"2"},"Status":1,"ProtocolMin":1,"ProtocolMax":5,"ProtocolCur":2,"DelegateMin":2,"DelegateMax":4,"DelegateCur":4},{"Name":"consulserver2","Addr":"172.17.0.3","Port":8301,"Tags":{"build":"0.7.2:'a9afa0c","dc":"dc1","port":"8300","role":"consul","vsn":"2","vsn_max":"3","vsn_min":"2"},"Status":1,"ProtocolMin":1,"ProtocolMax":5,"ProtocolCur":2,"DelegateMin":2,"DelegateMax":4,"DelegateCur":4},{"Name":"consulserver1","Addr":"172.17.0.2","Port":8301,"Tags":{"bootstrap":"1","build":"0.7.2:'a9afa0c","dc":"dc1","port":"8300","role":"consul","vsn":"2","vsn_max":"3","vsn_min":"2"},"Status":1,"ProtocolMin":1,"ProtocolMax":5,"ProtocolCur":2,"DelegateMin":2,"DelegateMax":4,"DelegateCur":4},{"Name":"consulswarmclient2","Addr":"172.17.0.6","Port":8301,"Tags":{"build":"0.7.2:'a9afa0c","dc":"dc1","role":"node","vsn":"2","vsn_max":"3","vsn_min":"2"},"Status":1,"ProtocolMin":1,"ProtocolMax":5,"ProtocolCur":2,"DelegateMin":2,"DelegateMax":4,"DelegateCur":4},{"Name":"consulcurlclient","Addr":"172.17.0.1","Port":8301,"Tags":{"build":"0.7.2:'a9afa0c","dc":"dc1","role":"node","vsn":"2","vsn_max":"3","vsn_min":"2"},"Status":1,"ProtocolMin":1,"ProtocolMax":5,"ProtocolCur":2,"DelegateMin":2,"DelegateMax":4,"DelegateCur":4},{"Name":"consulserver3","Addr":"172.17.0.4","Port":8301,"Tags":{"build":"0.7.2:'a9afa0c","dc":"dc1","port":"8300","role":"consul","vsn":"2","vsn_max":"3","vsn_min":"2"},"Status":1,"ProtocolMin":1,"ProtocolMax":5,"ProtocolCur":2,"DelegateMin":2,"DelegateMax":4,"DelegateCur":4}]

Service一覧。ただ、ServiceはローカルのAgentが管理しているものしか見えないみたいなので、こちらのコマンドはWildFly Swarmを
動かしているホストで実行しました。

$  curl http://localhost:8500/v1/agent/services
{"swarm:172.17.0.6:8080":{"ID":"swarm:172.17.0.6:8080","Service":"swarm","Tags":["http"],"Address":"172.17.0.6","Port":8080,"EnableTagOverride":false,"CreateIndex":0,"ModifyIndex":0}}

地味にportとか分かっているみたいです。

Consul Command(CLI

HTTP API以外にも、consul実行ファイルに対してコマンドを実行することもできます。

Consul Command(CLI)

とりあえず、Node一覧を見てみましょう。

$ ./consul members
Node                Address          Status  Type    Build  Protocol  DC
consulcurlclient    172.17.0.1:8301  alive   client  0.7.2  2         dc1
consulserver1       172.17.0.2:8301  alive   server  0.7.2  2         dc1
consulserver2       172.17.0.3:8301  alive   server  0.7.2  2         dc1
consulserver3       172.17.0.4:8301  alive   server  0.7.2  2         dc1
consulswarmclient1  172.17.0.5:8301  alive   client  0.7.2  2         dc1
consulswarmclient2  172.17.0.6:8301  alive   client  0.7.2  2         dc1

Dnsmasqをインストールしてcurlで動作確認

名前解決できたりNodeやServiceの一覧が見れたのはいいのですが、このままだとcurlで名前解決ができません。

ここは、ドキュメントに習ってDnsmasqを使ってConsulでの名前解決を統合してみます。
DNS Forwarding

Dnsmasqインストール。

$ sudo apt-get install dnsmasq

ドキュメントに習い、Dnsmasqの設定をしていきます。

$ sudo vim /etc/dnsmasq.conf

dnsmasq.confの最後に、「consul」で終わるエントリはローカルのDNSサーバー(Consul)に解決するようにします。

server=/consul/127.0.0.1#8600

設定したら、Dnsmasqを再起動します。

$ sudo service dnsmasq restart

確認。Serviceで確認するので、今回は「swarm.service.consul」でアクセスします。「http.swarm.service.consul」でもOKです。

$ curl http://swarm.service.consul:8080/hostname
consulswarmclient2
$ curl http://swarm.service.consul:8080/hostname
consulswarmclient1
$ curl http://swarm.service.consul:8080/hostname
consulswarmclient1
$ curl http://swarm.service.consul:8080/hostname
consulswarmclient2

ちょっと不規則気味ですが、WildFly Swarmが動作しているServerに振り分けられていることがわかります。

また、メンバー(この場合はWildFly Swarmのアプリケーション)を停止したりすると、切り離されます。

$ curl http://swarm.service.consul:8080/hostname
consulswarmclient1
$ curl http://swarm.service.consul:8080/hostname
consulswarmclient1

もちろん、再度クラスタに参加させればまた名前解決されるメンバーに復帰します。

動作確認としてはOKそうですね。

オマケ

「swarm.bind.address」について

WildFly Swarmのアプリケーションを起動する時に、わざわざ「-Dswarm.bind.address」を指定していました。

## consulswarmclient1
$ java -Dswarm.bind.address=172.17.0.5 -jar /path/to/consul-integration-swarm.jar

## consulswarmclient2
$ java -Dswarm.bind.address=172.17.0.6 -jar /path/to/consul-integration-swarm.jar

これ、なんで付けてたかというと、何も付けずに起動すると名前解決の結果がこうなるからです。

## タグなし
$ dig @localhost -p 8600 swarm.service.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 swarm.service.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51890
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;swarm.service.consul.		IN	ANY

;; ANSWER SECTION:
swarm.service.consul.	0	IN	AAAA	::

;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 22:25:03 JST 2017
;; MSG SIZE  rcvd: 66


## タグあり
$ dig @localhost -p 8600 http.swarm.service.consul ANY

; <<>> DiG 9.9.5-3ubuntu0.11-Ubuntu <<>> @localhost -p 8600 http.swarm.service.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53860
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;http.swarm.service.consul.	IN	ANY

;; ANSWER SECTION:
http.swarm.service.consul. 0	IN	AAAA	::

;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Sat Jan 14 22:25:05 JST 2017
;; MSG SIZE  rcvd: 71

ひとつしか返ってきませんが…?

;; ANSWER SECTION:
swarm.service.consul.	0	IN	AAAA	::

;; ANSWER SECTION:
http.swarm.service.consul. 0	IN	AAAA	::

ただ、Consulの「swarm」Service上は確かに2つ登録されています。

これ、WildFly SwarmのConsul統合部分の起動時のログを見ていたらピンとくるんですけど、こんなのが出力されます。

2017-01-14 13:24:35,447 INFO  [org.wildfly.swarm.topology.consul.runtime.Advertiser] (MSC service thread 1-7) Registered service swarm:0:0:0:0:0:0:0:0:8080

「-Dswarm.bind.address」を指定すると、こうなります。

2017-01-14 13:27:17,940 INFO  [org.wildfly.swarm.topology.consul.runtime.Advertiser] (MSC service thread 1-7) Registered service swarm:172.17.0.5:8080

つまり、何も指定しないとローカルアドレスが登録されるということで…。

WildFly Swarmは、どこのConsulにアクセスしている?

これはConsulTopologyFractionを見ると分かります。
https://github.com/wildfly-swarm/wildfly-swarm/blob/2017.1.1/fractions/topology-consul/src/main/java/org/wildfly/swarm/topology/consul/ConsulTopologyFraction.java#L100

デフォルトでは、「http://localhost:8500」にアクセスしに行こうとします。つまり、ローカルにConsul Agentがいることが前提に
なっていますね。

    static {
        URL tmp = null;
        try {
            tmp = new URL("http://localhost:8500");
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
        DEFAULT_URL = tmp;
    }

もちろん、ConsulTopologyFractionを使用すればカスタマイズはできそうです。

アクセス先が切り替わらない

実はこの構成ですが、ほとんどDockerでやっています。で、Docker内(Ubuntuイメージ)でDnsmasqをインストールして確認して
みたのですが、ひとつのホストにアクセスし続けていてけっこう困りました。

どうもこれが原因っぽいです。
Consul DNS round robin works for host but not for containers - General - Docker Forums

今回は、curlとDnsmasqはホスト側にインストールして実行しました。

「/etc/resolv.conf」について

「/etc/resolv.conf」ファイルは、今回編集しませんでした。
/etc/resolv.conf

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.0.1
search localdomain

nameserverが他に登録されている場合は、ローカルのnameserverを先頭に持ってくるようにしましょう。

場合によっては、「/etc/dnsmasq.conf 」ファイルに「strict-order」を書いた方がいい時もあるようです。

【Consul】dnsmasqで名前解決を行う方法を試してみた | Pocketstudio.jp log3

まとめ

WildFly SwarmのConsul統合機能を使って、Service Discovery、名前解決の仕組みとして使ってみました。

ほとんどConsulについての調べ物が多かった気がするのですが、とりあえず確認したいところまで動かせたので
満足です。

名前解決できるのはいいのですが、DNSという関係上、ポートはDNSに頼っている限りは解決できないなぁとちょっと
思いましたが、そのあたりはまあ、と…。

今回作成したコード(WildFly Swarmアプリケーションのみ)は、こちらに置いています。
https://github.com/kazuhira-r/wildfly-swarm-scala-examples/tree/master/consul-integration