yield-bytes

沉淀、分享与无限进步

基于Docker单机部署ZooKeeper集群

  前面的文章部署zk服务,直接在裸机上部署,较为不便,现在很多服务如果不做docker化,无论在故障恢复、运维都增加很大困难,无法做到自动化部署,这种低效率的IT运营模式是比较难接受的,对于我们开发而已,必须是一键式优雅部署,所以本篇文章采用docker方式部署zk集群,可以从中对比裸机部署过程的不同以及优势

1、部署docker和docker-compose

参考本博客文章:链接

2、部署zookeeper集群

  拉取zk镜像,可以dockerhub上面看下目前的zk官方镜像的tag有什么版本,默认是latest,接着是3.5.5以及3.4.14,这里用的stable版本3.4.14

1
2
[root@dn2 opt]# docker pull zookeeper:3.4.14

在宿主机上新建一个存放docker集群zk服务器目录(仅为了方便管理),并在该目录下新建一个compose配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
[root@dn2 zk_docker_cluster]# pwd
/opt/zk_docker_cluster
[root@dn2 zk_docker_cluster]# vi docker-compose.yml
version: '3.3'
services:
zoo1:
# 使用zookeeper:3.4.14镜像,加上tag标签
image: zookeeper:3.4.14
restart: always
hostname: zoo1
container_name: zk1
ports:
- 2181:2181
volumes:
# 宿主机目录路径无需手工创建,docker-compose有权限进行自行创建挂载的目录路径
- /opt/zk_docker_cluster/zoo1/data:/data
- /opt/zk_docker_cluster/zoo1/datalog:/datalog
- /opt/zk_docker_cluster/zoo1/logs:/logs

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
hostname: zoo2
container_name: zk2
ports:
- 2182:2181
volumes:
- /opt/zk_docker_cluster/zoo2/data:/data
- /opt/zk_docker_cluster/zoo2/datalog:/datalog
- /opt/zk_docker_cluster/zoo2/logs:/logs
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
hostname: zoo3
container_name: zk3
ports:
- 2183:2181
volumes:
- /opt/zk_docker_cluster/zoo3/data:/data
- /opt/zk_docker_cluster/zoo3/datalog:/datalog
- /opt/zk_docker_cluster/zoo3/logs:/logs
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

配置文件需要注意的地方

在运行前,可以用docker-compose -f docker-compose.yml config检查配置文件是否正确

1)version 版本号不能随便改,例如这里改为1.0,提示不支持

1
2
[root@dn2 zk_docker_cluster]# docker-compose -f docker-compose.yml config 
ERROR: Version in "./docker-compose.yml" is unsupported. You might be seeing this error because you're using the wrong Compose file version. Either specify a supported version (e.g "2.2" or "3.3") and place your service definitions under the `services` key, or omit the `version` key and place your service definitions at the root of the file to use version 1.

2)注意yaml语法的层次表达

例如在这里,故意把zoo1放置在service同层次上,引起解析出错,所以在编排zk的配置时,要注意这些细节

1
2
3
4
ERROR: yaml.parser.ParserError: while parsing a block mapping
in "./docker-compose .yml", line 1, column 1
expected <block end>, but found '<block mapping start>'
in "./docker-compose .yml", line 17, column 3

==3) 注意到docker-compose yml跟裸机部署集群的不同==

  • 例如hostname:

在docker中,无需要指明具体的ip地址,因为docker使用其内部私网为zk服务自动分配私网ip,而且自动DNS解析主机名,因此配置文件可以直接用zoo1这样的主机名

而在裸机部署中,裸机自己的网络设置需要指定具体IP地址,如果zoo.cfg配置用了主机名代替服务器IP,那么要求裸机网卡设定的DNS需支持zk网段的主机名解析

  • 再例如设定集群的server.n:

在docker-compose里,直接主机名,server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 ,前面已经提过,docker内部其实已经对三个zk容器都分配相应的私网地址,通过以下命令查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 列出所有docker容器IP
[root@dn2 zk_docker_cluster]# docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)
/zk1 - 172.18.0.4
/zk3 - 172.18.0.2
/zk2 - 172.18.0.3
# 也可以用docker inspect zk2 查看容器内部具体的信息,这里截取一部分
[root@dn2 zk_docker_cluster]# docker inspect zk2
"HostConfig": {
"Binds": [
# datalog:rw,说明docker对宿挂载的宿主机有读写权限
"/opt/zk_docker_cluster/zoo2/datalog:/datalog:rw",
"/opt/zk_docker_cluster/zoo2/data:/data:rw"
],
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "zk_docker_cluster_default",
"PortBindings": {
# 绑定宿主机的端口号
"2181/tcp": [
{
"HostIp": "",
# zoo2容器内部的zk服务监听端口号
"HostPort": "2182"
}
]
},

.......

"Networks": {
"zk_docker_cluster_default": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"0595457ea13d",
# hostname主机名
"zoo2"
],
"NetworkID": "46f7dbb34f0eefb1181729aeaaf6a1080d64a46fdba935b21d5e37a3b1aea34e",
"EndpointID": "9dd1d2726ba6850ab851f0227fb04aa1a027c35e84d36cd308208a4d4fb42f7d",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.3",
"IPPrefixLen": 16,

docker内部的私网段可以在宿主机上ip a 命令查看到,这是个docker的网桥网络,

地址池:172.18.0.1/16

1
2
3
4
5
6
8: br-46f7dbb34f0e: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
link/ether 02:42:82:51:27:a6 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-46f7dbb34f0e
valid_lft forever preferred_lft forever
inet6 fe80::42:82ff:fe51:27a6/64 scope link
valid_lft forever preferred_lft forever

三个zk容器服务分别从这个地址池获取三个ip,网关为172.18.0.1,相当于三台独立服务器,因此在server.n设置端口都可以指定为2888:3888相同端口,也即

zoo1:2888:3888等于172.18.0.4:2888:3888
zoo2:2888:3888等于172.18.0.3:2888:3888

zoo3:2888:3888等于172.18.0.2:2888:3888

总之,docker内部出色的网络结构设计,使得管理员从相对繁琐的网络配置解放出来。

而在单台裸机部署集群的配置中,则要指明ip(若有dns或者配置主机名解析,可无须指定IP地址)以及不同的管理端口号(使用不同端口是防止在同一服务器上端口冲突):

1
2
3
server.1=192.168.4.100:42182:42183
server.2=192.168.4.100:42184:42185
server.3=192.168.4.100:42186:42187

启动docker-compose

注意:所有的操作都应该在对应的docker-compse项目下进行,这是因为命令docker-compose自动读取本目录下的docker-compose.yml配置,注意这里仅当配置文件名为默认值docker-compose.yml,运行docker-compose命令才无需传入配置文件,否则如果项目目录下,yml配置文件为其它名字,例如

zk_docker_cluster.yml,每次执行docker-compose命令都需要指定配置文件:

docker-compose -f zk_docker_cluster.yml up -d

改了默认文件名的配置文件,在执行命令没传人配置文件,docker-compose提示:

1
2
3
4
5
6
7
8
9
10
[root@dn2 zk_docker_cluster]# ls
zk_docker_cluster.yml zoo1 zoo2 zoo3
[root@dn2 zk_docker_cluster]# docker-compose ps
ERROR:
Can't find a suitable configuration file in this directory or any
parent. Are you in the right directory?

Supported filenames: docker-compose.yml, docker-compose.yaml
# 它这里提示在当前目录或者docker-compose的默认目录,都没有砸到配合文件,
# 支持两种使用默认值命名的文件:docker-compose.yml, docker-compose.yaml

执行相关命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@dn2 zk_docker_cluster]# docker-compose  up -d
Starting zk2 ... done
Creating zk1 ... done
Creating zk3 ... done

[root@dn2 zk_docker_cluster]# docker-compose stop
Stopping zk1 ... done
Stopping zk3 ... done
Stopping zk2 ... done

# 查看zk_docker_cluster目录结构,目录路径由docker根据compose配置自动创建,无需手工预先创建
[root@dn2 zk_docker_cluster]# tree
.
├── docker-compose.yml
├── zoo1
│ ├── data
│ │ ├── myid
│ │ └── version-2
│ │ ├── acceptedEpoch
│ │ ├── currentEpoch
│ │ ├── snapshot.0
│ │ └── snapshot.400000000
│ ├── datalog
│ │ └── version-2
│ └── logs
├── zoo2
│ ├── data
│ │ ├── myid
│ │ └── version-2
│ │ ├── acceptedEpoch
│ │ ├── currentEpoch
│ │ ├── snapshot.0
│ │ └── snapshot.400000000
│ ├── datalog
│ │ └── version-2
│ └── logs
└── zoo3
├── data
│ ├── myid
│ └── version-2
│ ├── acceptedEpoch
│ ├── currentEpoch
│ ├── snapshot.0
│ └── snapshot.400000000
├── datalog
│ └── version-2
└── logs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 可以直接进入容器内部查看
[root@dn2 ~]# docker exec -it zk1 /bin/bash
root@zoo1:/zookeeper-3.4.14# ./bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Mode: follower

# 在zk容器内部使用Cli登录并创建节点
[zk: localhost:2181(CONNECTED) 3] create /foo 1
Created /foo
[zk: localhost:2181(CONNECTED) 3] create /app_conf 1
Created /app_conf

[zk: localhost:2181(CONNECTED) 5] ls /
[zookeeper,app_conf,foo]
# 创建临时顺序节点
[zk: localhost:2181(CONNECTED) 6] create -e -s /foo 1
Created /foo0000000001
[zk: localhost:2181(CONNECTED) 7] create -e -s /foo 1
Created /foo0000000002
[zk: localhost:2181(CONNECTED) 8] create -e -s /foo 1
Created /foo0000000003

在zk的docker容器内部,指定zk容器ip进入相应的服务

在前面已经给出三个zk服务在docker内部分配的私网IP,若想进入指定的zk容器,则需要用到这些私网网IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 三个zk服务器在docker内部分配到的IP
(docker ps -aq)
/zk3 - 172.18.0.3
/zk1 - 172.18.0.4
/zk2 - 172.18.0.2

# 先选一个容器进入其内部,例如其内部zk client环境连接到其他zk服务实例,
[root@dn2 bin]# docker exec -it zk1 /bin/bash
root@zoo1:/zookeeper-3.4.14#

# 连接zk1服务
root@zoo1:/zookeeper-3.4.14# ./bin/zkCli.sh -server 172.18.0.4
[zk: 172.18.0.4:2181(CONNECTED) 1] ls /
[zookeeper, app_conf, foo]

# 连接zk2服务
root@zoo1:/zookeeper-3.4.14# ./bin/zkCli.sh -server 172.18.0.2
[zk: 172.18.0.2:2181(CONNECTED) 1] ls /
[zookeeper, app_conf, foo]

# 连接zk1服务
root@zoo1:/zookeeper-3.4.14# ./bin/zkCli.sh -server 172.18.0.3
[zk: 172.18.0.3:2181(CONNECTED) 1] ls /
[zookeeper, app_conf, foo]

3、用zk的四字命令查看zk集群状态

  通过进入容器查看zk状态显然不优雅,zk中有快捷的命令可以查看服务器的运行状态,它们的长度为4个英文字母缩写,又叫“四字命令”,需要结合nc命令,服务器安装nmap-ncat.x86_64(可通过yum install nc)

state

stat命令用于获取zk的运行时状态信息,包括基本的zk版本、打包信息、运行时角色、集群数据节点个数等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 详细信息
[root@dn2 zk_docker_cluster]# echo stat | nc 127.0.0.1 2181
Zookeeper version: 3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT
Clients:
/172.18.0.1:38660[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 2/18/49
Received: 7
Sent: 6
Connections: 1
Outstanding: 0
Zxid: 0x300000002
Mode: follower
Node count: 4

# 每个zk实例的角色
[root@dn2 zk_docker_cluster]# echo stat | nc 127.0.0.1 2181|grep Mode
Mode: follower
[root@dn2 zk_docker_cluster]# echo stat | nc 127.0.0.1 2182|grep Mode
Mode: follower
[root@dn2 zk_docker_cluster]# echo stat | nc 127.0.0.1 2183|grep Mode
Mode: leader

conf

conf命令用于输出ZooKeeper服务器运行时使用的基本配置信息,包括clientPort、dataDir和tickTime等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@dn2 zk_docker_cluster]# echo conf | nc 127.0.0.1 2181
clientPort=2181
dataDir=/data/version-2
dataLogDir=/datalog/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=5
syncLimit=2
electionAlg=3
electionPort=3888
quorumPort=2888
peerType=0

mntr

mntr命令用于输出比stat命令更为详尽的服务器统计信息,包括请求处理的延迟情况、服务器内存数据库大小和集群的数据同步情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@dn2 zk_docker_cluster]# echo mntr | nc 127.0.0.1 2181    
zk_version 3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT
zk_avg_latency 18
zk_max_latency 49
zk_min_latency 2
zk_packets_received 8
zk_packets_sent 7
zk_num_alive_connections 1
zk_outstanding_requests 0
zk_server_state follower
zk_znode_count 4
zk_watch_count 0
zk_ephemerals_count 0
zk_approximate_data_size 27
zk_open_file_descriptor_count 31
zk_max_file_descriptor_count 1048576
zk_fsync_threshold_exceed_count 0

crst

crst命令是一个功能性命令(client reset),用于重置所有的客户端连接统计信息

1
2
[root@dn2 zk_docker_cluster]# echo crst | nc 127.0.0.1 2181    
Connection stats reset.

srvr

srvr命令和stat命令的功能一致,唯一的区别是srvr不会将客户端的连接情况输出,仅仅输出服务器的自身信息

1
2
3
4
5
6
7
8
9
10
imok[root@dn2 zk_docker_cluster]# echo srvr | nc 127.0.0.1 2181    
Zookeeper version: 3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT
Latency min/avg/max: 0/0/49
Received: 327
Sent: 326
Connections: 2
Outstanding: 0
Zxid: 0x300000004
Mode: follower
Node count: 5

dump

dump命令用于输出当前集群的所有会话信息,包括这些会话的会话ID,以及每个会话创建的临时节点等信息。

1
2
3
4
5
[root@dn2 zk_docker_cluster]# echo dump | nc 127.0.0.1 2181
SessionTracker dump:
org.apache.zookeeper.server.quorum.LearnerSessionTracker@77315813
ephemeral nodes dump:
Sessions with Ephemerals (0):

envi

envi命令用于输出ZooKeeper所在服务器环境以及一些runtime环境,包括os.version、java.version和user.home等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@dn2 zk_docker_cluster]# echo envi | nc 127.0.0.1 2181    
Environment:
zookeeper.version=3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT
host.name=zoo1
java.version=1.8.0_222
java.vendor=Oracle Corporation
java.home=/usr/local/openjdk-8
java.class.path=/zookeeper-3.4.14/bin/../zookeeper-server/target/classes:/zookeeper-3.4.14/bin/../build/classes:/zookeeper-3.4.14/bin/../zookeeper-server/target/lib/*.jar:/zookeeper-3.4.14/bin/../build/lib/*.jar:/zookeeper-3.4.14/bin/../lib/slf4j-log4j12-1.7.25.jar:/zookeeper-3.4.14/bin/../lib/slf4j-api-1.7.25.jar:/zookeeper-3.4.14/bin/../lib/netty-3.10.6.Final.jar:/zookeeper-3.4.14/bin/../lib/log4j-1.2.17.jar:/zookeeper-3.4.14/bin/../lib/jline-0.9.94.jar:/zookeeper-3.4.14/bin/../lib/audience-annotations-0.5.0.jar:/zookeeper-3.4.14/bin/../zookeeper-3.4.14.jar:/zookeeper-3.4.14/bin/../zookeeper-server/src/main/resources/lib/*.jar:/conf:
java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.io.tmpdir=/tmp
java.compiler=<NA>
os.name=Linux
os.arch=amd64
os.version=3.10.0-957.27.2.el7.x86_64
user.name=zookeeper
user.home=/home/zookeeper
user.dir=/zookeeper-3.4.14

ruok

ruok命令用于输出当前ZooKeeper服务器是否正在运行,“Are you ok”的缩写?如果当前ZooKeeper服务器正在运行,那么返回“imok”(I am ok),否则没有任何响应输出。

1
2
[root@dn2 zk_docker_cluster]# echo ruok | nc 127.0.0.1 2181    
imok

wchs

wchs命令用于输出当前服务器上管理的Watcher的概要信息

1
2
3
[root@dn2 zk_docker_cluster]# echo wchs | nc 127.0.0.1 2181     
connections watching 0 paths
Total watches:0

wchc

wchc命令用于输出当前服务器上管理的Watcher的详细信息,以会话为单位进行归组,同时列出被该会话注册了Watcher的节点路径

wchp

wchp命令和wchc命令非常类似,也是用于输出当前服务器上管理的Watcher的详细信息,不同点在于wchp命令的输出信息以节点路径为单位进行归组。