分布式架构——第8篇:使用docker-compose构建ZooKeeper分布式集群

本文采用Docker技术,重新构建ZooKeeper集群。在实现时,采用docker-compose技术简化了集群配置的复杂度。因为,docker-compose它允许用户通过一个单独的docker-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器为一个项目。此前,《分布式架构——第4篇:构建ZooKeeper伪分布式集群》描述了Zookeeper伪分布式集群的构建。

下载ZooKeeper Docker镜像

这里直接下载官方镜像。

$ sudo docker pull zookeeper

新建docker-compose.yml文件

这个名字为docker-compose默认yml名,类似于zoo.cfg。这里文件存放位置随意。
e.g.

$ mkdir -p ~/apps/docker/zookeeper/
$ cd ~/apps/docker/zookeeper/
$ vim docker-compose.yml

docker-compose.yml这个配置文件会告诉Docker分别运行三个zookeeper镜像, 并分别将本地的2181, 2182, 2183端口绑定到对应的容器的2181端口上。

ZOO_MY_IDZOO_SERVERS是搭建ZooKeeper集群需要设置的两个环境变量, 其中:

  • ZOO_MY_ID 表示服务的id, 它是1-255之间的整数, 必须在集群中唯一。
  • ZOO_SERVERS 是集群的主机列表。
version: '2'
services:
zoo1:
image: zookeeper
restart: always
container_name: zoo1
ports:
- "2181:2181"
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

zoo2:
image: zookeeper
restart: always
container_name: zoo2
ports:
- "2182:2181"
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

zoo3:
image: zookeeper
restart: always
container_name: zoo3
ports:
- "2183:2181"
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

启动ZooKeeper集群

$ sudo docker-compose -f docker-compose.yml -p zookeeper_cluster up -d
Creating network "zookeeper_cluster_default" with the default driver
Creating zoo1 ... done
Creating zoo3 ... done
Creating zoo2 ... done

这里各参数的含义:

  • -f 指定docker-composeyml配置文件(缺省为docker-compose.yml,类似于zoo.cfg);
  • -p 用来指定集群的名称,如缺少该参数,则默认为当前父目录名称;
  • -d 指定为后台运行。

外部访问集群

我有两台物理主机,服务器安装的是ubuntu server(没有显示屏),开发机是windows 10,所有操作都是开发机上ssh进行的。现实中,我将自己的两台电脑连在了一个路由器上,固定分配服务器的IP地址为192.168.1.102

从上文中的docker-compose.yml可以知道,三个ZooKeeper程序对外映射为21812182以及2183端口。(但是都运行在192.168.1.102这台服务器的Docker中)

$ ~/zookeeper-3.4.12/bin/zkCli.sh -server 192.168.1.102:2181,192.168.1.102:2182,192.168.1.102:2183

[zk: 192.168.1.102:2181,192.168.1.102:2182,192.168.1.102:2183(CONNECTED) 0] ls /
[zookeeper]
[zk: 192.168.1.102:2181,192.168.1.102:2182,192.168.1.102:2183(CONNECTED) 1] ls /zookeeper
[quota]
[zk: 192.168.1.102:2181,192.168.1.102:2182,192.168.1.102:2183(CONNECTED) 2] ls /zookeeper/quota
[]

当然,这里也可以直接使用Docker命令行,在本地访问
e.g.

$ sudo docker run -it --rm --net zookeeper_cluster_default zookeeper zkCli.sh -server zoo1:2181,zoo2:2181,zoo3:2181

这里各参数的含义:

  • -it -i以交互模式运行容器,-t为容器重新分配一个伪输入终端。通常连用为-it
  • --rm 可以在容器启动时设置–rm选项,这样在容器退出时就能够自动清理容器内部的文件系统(不能与-d同时使用);
  • --net 默认值为--net=bridge,指连接到默认的网桥。--net=container:NAME_or_ID (可以用container_name_default代替)让Docker将新建容器的进程放到一个已存在容器的网络栈中,添加了--net才能识别zoo1zoo2zoo3

查看集群的运行状态

使用nc命令。通过nc命令连接到指定的ZooKeeper服务器, 然后发送stat可以查看服务的状态。

$ echo stat | nc 192.168.1.102 2181

Zookeeper version: 3.4.13-2d71af4dbe22557fda74f9a9b4309b15a7487f03, built on 06/29/2018 04:05 GMT
Clients:
/192.168.1.102:48174[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x100000002
Mode: follower
Node count: 4

$ echo stat | nc 192.168.1.102 2182

Zookeeper version: 3.4.13-2d71af4dbe22557fda74f9a9b4309b15a7487f03, built on 06/29/2018 04:05 GMT
Clients:
/192.168.1.102:39304[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/3/63
Received: 23
Sent: 22
Connections: 1
Outstanding: 0
Zxid: 0x100000002
Mode: follower
Node count: 4
$ echo stat | nc 192.168.1.102 2183

Zookeeper version: 3.4.13-2d71af4dbe22557fda74f9a9b4309b15a7487f03, built on 06/29/2018 04:05 GMT
Clients:
/192.168.1.102:57988[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x100000002
Mode: leader
Node count: 4
Proposal sizes last/min/max: 32/32/36

通过上面的输出,我们可以看到,zoo1zoo2 都是follower,而zoo3leader,因此证明了我们的ZooKeeper集群确实是搭建起来了。

关闭集群

$ sudo docker-compose -f docker-compose.yml -p zookeeper_cluster down

Stopping zoo2 ... done
Stopping zoo3 ... done
Stopping zoo1 ... done
Removing zoo2 ... done
Removing zoo3 ... done
Removing zoo1 ... done
Removing network zookeeper_cluster_default

References:
[1] https://segmentfault.com/a/1190000006907443
[2] https://blog.csdn.net/halcyonbaby/article/details/42112141
[3] https://yeasy.gitbooks.io/docker_practice/compose/commands.html
[4] http://www.runoob.com/docker/docker-run-command.html
[5] https://yeasy.gitbooks.io/docker_practice/underly/network.html
[6] http://www.voidcn.com/article/p-pqmqlniy-yc.html