この投稿 (Docker Swarm Cluster without docker-machine)を書いている時に思った。SwarmプロセスをHAで複数立ち上げても、この構成ではConsulプロセスが一つしかないからConsulが死んだらサービス停止だと。Consul自体は複数ノードで運用できる (詳しくはこちら)ので、今回は複数ノードで立ち上がっているConsulとSwarmをどのように組み合わせるか実験してみた。
結論から言ってしまうと、Swarmプロセスを起動時に紐付けるConsulノードは一つしか記述できないようだ。Docker Swarm Discoveryの例にEtcdはswarm join --advertise=<node_ip:2375> etcd://<etcd_addr1>,<etcd_addr2>/<optional path prefix>
とあるのに対し、Consulではswarm join --advertise=<node_ip:2375> consul://<consul_addr>/<optional path prefix>
とあり、いかにも複数ノード記述できなそう。実際にdocker/libkv/store/consul/consul.goに次のような記述がある。
var (
// ErrMultipleEndpointsUnsupported is thrown when there are
// multiple endpoints specified for Consul
ErrMultipleEndpointsUnsupported = errors.New("consul does not support multiple endpoints")
// ErrSessionRenew is thrown when the session can't be
// renewed because the Consul version does not support sessions
ErrSessionRenew = errors.New("cannot set or renew session for ttl, unable to operate on sessions")
)
<snip>
func New(endpoints []string, options *store.Config) (store.Store, error) {
if len(endpoints) > 1 {
return nil, ErrMultipleEndpointsUnsupported
}
ということで、Swarm起動時に紐付けたConsulノードが行方不明になった場合、そのノードを利用していたSwarmプロセスはService Discoveryを出来なくなってしまう。これでは話が終わってしまうので、ConsulノードをServerとClientの組合せとし、ConsulのClientノードをproxyとして見立ててSwarmプロセスと紐付けることを試してみた。proxyが落ちれば、そのproxyと紐付けられたSwarmももちろんサービス停止となる。構成図は次の通り。
7つのVMを利用する。Consul用に10.0.1.0/24のアドレスを割り当てた。一覧は次の通り。
VM name | IP address | IP address for consul |
---|---|---|
consul-server1 | 192.168.209.168 | 10.0.1.129 |
consul-server2 | 192.168.209.169 | 10.0.1.130 |
consul-server3 | 192.168.209.171 | 10.0.1.134 |
consul-client | 192.168.209.170 | 10.0.1.131 |
swarm-mgr | 192.168.209.138 | 10.0.1.128 |
swarm-node1 | 192.168.209.166 | 10.0.1.132 |
swarm-node2 | 192.168.209.167 | 10.0.1.133 |
(ああ、めんどくさい。)
では、早速Consulクラスターを立ち上げる。
$ docker run --rm progrium/consul cmd:run 10.0.1.129 -d
eval docker run --name consul -h $HOSTNAME -p 10.0.1.129:8300:8300 \
-p 10.0.1.129:8301:8301 -p 10.0.1.129:8301:8301/udp -p 10.0.1.129:8302:8302 \
-p 10.0.1.129:8302:8302/udp -p 10.0.1.129:8400:8400 -p 10.0.1.129:8500:8500 \
-p 172.17.0.1:53:53 -p 172.17.0.1:53:53/udp -d progrium/consul \
-server -advertise 10.0.1.129 -bootstrap-expect 3
progrium/consul cmd:run
でこのようにevalな形で実行形式の文字列を返してくれる。以下を実際には実行する。
$ docker run --name consul1 -p 10.0.1.129:8300:8300 \
-p 10.0.1.129:8301:8301 \
-p 10.0.1.129:8301:8301/udp \
-p 10.0.1.129:8302:8302 \
-p 10.0.1.129:8302:8302/udp \
-p 10.0.1.129:8400:8400 \
-p 10.0.1.129:8500:8500 \
-p 172.17.0.1:53:53 \
-p 172.17.0.1:53:53/udp \
-d progrium/consul -server -advertise 10.0.1.129 \
-bootstrap-expect 3
consul-server2
とconsul-server3
をConsulクラスターにjoinさせる。
$ docker run --name consul2 -p 10.0.1.130:8300:8300 \
-p 10.0.1.130:8301:8301 \
-p 10.0.1.130:8301:8301/udp \
-p 10.0.1.130:8302:8302 \
-p 10.0.1.130:8302:8302/udp \
-p 10.0.1.130:8400:8400 \
-p 10.0.1.130:8500:8500 \
-p 172.17.0.1:53:53 \
-p 172.17.0.1:53:53/udp \
-d progrium/consul -server -advertise 10.0.1.130 \
-join 10.0.1.129
$ docker run --name consul3 -p 10.0.1.134:8300:8300 \
-p 10.0.1.134:8301:8301 \
-p 10.0.1.134:8301:8301/udp \
-p 10.0.1.134:8302:8302 \
-p 10.0.1.134:8302:8302/udp \
-p 10.0.1.134:8400:8400 \
-p 10.0.1.134:8500:8500 \
-p 172.17.0.1:53:53 \
-p 172.17.0.1:53:53/udp \
-d progrium/consul -server -advertise 10.0.1.134 \
-join 10.0.1.129
最後にconsul-clinet
をクライアントとしてjoinさせる。-server
の指定がなくなっただけ。
$ docker run --name consul-cli -p 10.0.1.131:8300:8300 \
-p 10.0.1.131:8301:8301 \
-p 10.0.1.131:8301:8301/udp \
-p 10.0.1.131:8302:8302 \
-p 10.0.1.131:8302:8302/udp \
-p 10.0.1.131:8400:8400 \
-p 10.0.1.131:8500:8500 \
-p 172.17.0.1:53:53 \
-p 172.17.0.1:53:53/udp \
-d progrium/consul -advertise 10.0.1.131 \
-join 10.0.1.129
Consulクラスターのmemberを確認してみる。大丈夫そう。
$ docker exec consul1 consul members
Node Address Status Type Build Protocol DC
1011d34dc1c5 10.0.1.129:8301 alive server 0.5.2 2 dc1
b0d93c5dd007 10.0.1.130:8301 alive server 0.5.2 2 dc1
c277ab2f8165 10.0.1.134:8301 alive server 0.5.2 2 dc1
375a31f7fb52 10.0.1.131:8301 alive client 0.5.2 2 dc1
ではいよいよSwarm Managerを立ち上げる。Consulのbindingにはconsul-client (10.0.1.131)
を指定する。
$ docker run --restart=unless-stopped -d -p 3375:2375 swarm manage \
--replication --advertise 192.168.209.138:3375 consul://10.0.1.131:8500
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6461f48b1b04 swarm "/swarm manage --repl" 4 seconds ago Up 3 seconds 0.0.0.0:3375->2375/tcp tiny_raman
最後にswarm nodeを追加する。docker info
の結果からきちんと追加されたことが分かる。
$ docker -H=tcp://192.168.209.166:2375 run -d swarm join \
--advertise=192.168.209.166:2375 consul://10.0.1.131:8500
$ docker -H=tcp://192.168.209.167:2375 run -d swarm join \
--advertise=192.168.209.167:2375 consul://10.0.1.131:8500
$ DOCKER_HOST=192.168.209.138:3375 docker info
Containers: 27
Running: 2
Paused: 0
Stopped: 25
Images: 14
Server Version: swarm/1.2.0
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 2
ubu-swarm-node1: 192.168.209.166:2375
└ Status: Healthy
└ Containers: 15
└ Reserved CPUs: 0 / 2
└ Reserved Memory: 1 GiB / 4.047 GiB
└ Labels: executiondriver=, kernelversion=3.13.0-85-generic, operatingsystem=Ubuntu 14.04.4 LTS, storagedriver=aufs
└ Error: (none)
└ UpdatedAt: 2016-05-02T14:49:12Z
└ ServerVersion: 1.11.0
ubu-swarm-node2: 192.168.209.167:2375
└ Status: Healthy
└ Containers: 12
└ Reserved CPUs: 0 / 2
└ Reserved Memory: 1 GiB / 4.047 GiB
└ Labels: executiondriver=, kernelversion=3.13.0-85-generic, operatingsystem=Ubuntu 14.04.4 LTS, storagedriver=aufs
└ Error: (none)
└ UpdatedAt: 2016-05-02T14:48:49Z
└ ServerVersion: 1.11.0
Plugins:
Volume:
Network:
Kernel Version: 3.13.0-85-generic
Operating System: linux
Architecture: amd64
CPUs: 4
Total Memory: 8.094 GiB
Name: 6461f48b1b04
Docker Root Dir:
Debug mode (client): false
Debug mode (server): false
WARNING: No kernel memory limit support
適当にコンテナを起動してみる。
$ DOCKER_HOST=192.168.209.138:3375 docker run -dit ubuntu /bin/bash
$ DOCKER_HOST=192.168.209.138:3375 docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9bd2e4eccc01 ubuntu "/bin/bash" 5 seconds ago Up 4 seconds ubu-swarm-node2/hungry_archimedes
$ DOCKER_HOST=192.168.209.138:3375 docker exec -it ubu-swarm-node2/hungry_archimedes bash
root@9bd2e4eccc01:/#
consul-server1
のconsulを落としてみる。
$ docker stop consul1
次のように、落とした直後はErrorが返ってくるが、leader electionが実行されると処理を継続することができた。ちなみにConsulサーバーが2台構成の場合、1つを落とすとleader electionが出来ずswarmの処理は停止してしまう。3台以上のサーバーが必要らしい。
$ DOCKER_HOST=192.168.209.138:3375 docker ps
Error response from daemon: No elected primary cluster manager
$ DOCKER_HOST=192.168.209.138:3375 docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66a76e873e51 ubuntu "/bin/bash" 56 seconds ago Up 55 seconds ubu-swarm-node2/drunk_galileo
consul-cli
のconsulを落としてみる。
$ docker stop consul-cli
冒頭に述べたようにこの場合はswarmの処理を継続できない。
$ DOCKER_HOST=192.168.209.138:3375 docker ps
Error response from daemon: No elected primary cluster manager
おしまい。