Fork me on GitHub

Docker下Zookeeper集群搭建

前言

自己了解一些Docker容器技术,想在自己机器上搭建一个zookeeper集群,以前在虚拟机上有过实践经验,这次就想着在Docker下也搞一个Zookeeper集群。Docker的相关介绍这里就不多讲了,大家可以通过搜索引擎查阅相关资料。

zookeeper简介

Apache ZooKeeper是Apache的一个软件项目,它提供了一个开放源码的分布式配置服务、同步服务和大型分布式系统的命名注册服务。ZooKeeper是Hadoop的一个子项目,但目前是它本身就是一个顶级项目。

安装包下载、镜像下载

http://archive.apache.org/dist/zookeeper/zookeeper-3.4.11/zookeeper-3.4.11.tar.gz

docker pull zookeeper

到写文章时,pull下来zookeeper的版本也是3.4.11,后续pull下来可能不是3.4.11了,需注意,后续新版本有配置变更也请留意

Zookeeper集群及容灾

集群

Zookeeper集群部署并非一定需要奇数台服务器,任意台Zookeeper服务器都能部署且能够正常运行。

一个Zookeeper集群如果要对外提供可用的服务,那么集群中必须要有过半的机器正常工作并且彼此之间能够正常通信。基于这个特性,如果想搭建一个能够允许N台机器挂掉的集群,那么就需要部署一个由2XN+1台服务器构成的Zookeeper集群。因此,一个由5台机器构成的Zookeeper集群,能够在挂掉2台机器后依然正常工作,而如果是一个由6台服务器构成的Zookeeper集群,同样只能够挂掉2台机器,因为如果挂掉3台,剩下的机器就无法实现过半了。

因此,从上面的讲解中,我们可以看出,对于一个由6台服务器组成的Zookeeper集群来说,和一个由5台服务器构成的Zookeeper集群相比,其在容灾能力上没有任何明显的提升。基于这个原因,Zookeeper集群通常设计部署成奇数台服务器即可。

容灾

容灾主要是两个个方面,一个是单点问题,Zookeeper的单点问题可以通过搭建集群来解决。二是Zookeeper需要保证集群内服务器过半存活问题,可以考虑三机房部署(N1=N总/3,N2=N总/3, N3=N总-N1-N2,其中N代表机器数量)。对于双机房部署,可以将过半的Zookeeper部署在主机房。

Zookeeper官方镜像提供的环境变量

注意跟系统属性配置方式区分开来,这个启动容器时可制定的环境变量与系统属性配置没有关系

启动容器时可指定的环境变量名 默认值
ZOO_TICK_TIME 2000
ZOO_INIT_LIMIT 5
ZOO_SYNC_LIMIT 2
ZOO_MAX_CLIENT_CNXNS 60
ZOO_STANDALONE_ENABLED false
ZOO_MY_ID 如果是在复制模式下该参数是必须的,要么在启动容器时,指定此环境变量。要么在数据目录的myid文件里指定
ZOO_SERVERS 如果是在复制模式下该参数是必须的,要么在启动容器时,指定此环境变量。要么在zoo.cfg配置文件里指定 。This variable allows you to specify a list of machines of the Zookeeper ensemble. Each entry has the form of server.id=host:port:port. Entries are separated with space. Do note that this variable will not have any effect if you start the container with a /conf directory that already contains the zoo.cfg file.

Zookeeper简单部署之使用Docker方式

zookeeper官方镜像,数据目录是/data,日志目录是/datalog,配置目录是/conf。这里官方文档里给出了解释,如果你映射了配置目录/conf

如果没有映射配置文件目录或映射配置文件也没关系,zookeeper官方镜像提供了以下启动容器时的环境变量参数

启动一个zookeeper容器

1
$ docker run --name some-zookeeper --restart always -d zookeeper

启动一个应用的容器,这个应用是需要用到zookeeper的,可以通过–link some-zookeeper:zookeeper连接到上一步启动的zookeeper容器。some-zookeeper冒号后的zookeeper是别名

1
$ docker run --name some-app --link some-zookeeper:zookeeper -d application-that-uses-zookeeper

通过命令行客户端连接zookeeper,由于这里加了–rm参数,使得退出容器,容器马上会被删掉

1
2
3
4
5
$ docker run -it --rm --link some-zookeeper:zookeeper zookeeper zkCli.sh -server zookeeper
# 可以直接指定ip:port
$ docker run -it --rm --link some-zookeeper:zookeeper zookeeper zkCli.sh -server 192.168.56.101:2181
# 连接集群
$ docker run -it --rm --link some-zookeeper:zookeeper zookeeper zkCli.sh -server 192.168.56.101:2181,192.168.56.101:2182,192.168.56.101:2183

Zookeeper集群部署之Use Docker Compose

stack.yml定义如下:

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
version: '3.1'

services:
zoo1:
image: zookeeper
restart: always
hostname: zoo1
ports:
- 2181:2181
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

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

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

端口2181、2182、2183将会暴露出来

1
docker-compose -f stack.yml up

这种方式是一种伪集群方式,只适合在开发、测试环境中部署;生产环境是强烈不建议的,生产环境建议每个zookeeper实例部署在不同的物理机上。如果zookeeper实例部署在在同一物理机上的不同虚拟机,仍然不是严格意义的完全冗余。

Zookeeper集群部署之普通Docker部署方式

zk容器规划

通过表格形式来规划下各个zookeeper实例所需的myid,端口号,以及需要在宿主机上的对应的配置文件

容器名称 ip myid 端口1 端口2 端口3 宿主机配置文件 宿主机上的data目录
zk1 ip1 1 2181 2881 3881 /opt/data/zk1/conf/zoo.cfg /opt/data/zk1/data
zk2 ip2 2 2182 2882 3882 /opt/data/zk2/conf/zoo.cfg /opt/data/zk2/data
zk3 ip3 3 2183 2883 3883 /opt/data/zk3/conf/zoo.cfg /opt/data/zk3/data

上面表格显示有点问题,还是提供一张图片清楚一些,那么看图吧

端口1用于对client端提供服务,端口2用于选举leader,端口3用于集群内通讯使用(Leader会监听此端口),zk实例数应为大于等于3的奇数,如3、5、7。。。不宜太多。由于我是在同一台机器上启动的Zookeeper容器,所以ip1,ip2,ip3都是一样的。在同一台机器上启的话,是伪集群。

宿主机上创建相应目录

1
2
3
4
5
6
7
8
9
10
mkdir -p /opt/data/zk1/data
mkdir -p /opt/data/zk2/data
mkdir -p /opt/data/zk3/data

mkdir -p /opt/data/zk1/conf
mkdir -p /opt/data/zk2/conf
mkdir -p /opt/data/zk3/conf

将zookeeper-3.4.11.tar.gz,解压出zoo_sample.cfg,并重命名为zoo.cfg
将zoo.cfg传到/opt/data/zk1/conf,/opt/data/zk1/conf,/opt/data/zk1/conf目录下

我实验的时候,是在同一台机器上操作的,如果是在不同的机器上,请分别在不同机器上创建目录

创建myid文件

分别在/opt/data/zk1/data,/opt/data/zk2/data,/opt/data/zk3/data目录下创建myid文件,并写入相应的id

name myid file server id
zk1 /opt/data/zk1/data/myid 1
zk2 /opt/data/zk2/data/myid 2
zk3 /opt/data/zk3/data/myid 3
1
2
3
4
5
cd /opt/data/zk1/data/ && echo 1 |tee myid

cd /opt/data/zk2/data/ && echo 2 |tee myid

cd /opt/data/zk3/data/ && echo 3 |tee myid

zoo.cfg配置

以下配置只有clientPort是不同的,因为我是在同一台机器上跑的容器,端口一样会有冲突;如果在不同机器上跑,那clientPort可以是一样的。生产环境建议是在不同的机器上跑。分别修改/opt/data/zk1/conf/zoo.cfg,/opt/data/zk2/conf/zoo.cfg,/opt/data/zk3/conf/zoo.cfg

zk1的配置zoo.cfg

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

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/data
dataLogDir=/datalog
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

server.1=192.168.56.101:2881:3881
server.2=192.168.56.101:2882:3882
server.3=192.168.56.101:2883:3883

zk2的配置zoo.cfg

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
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/data
dataLogDir=/datalog
# the port at which the clients will connect
clientPort=2182
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.56.101:2881:3881
server.2=192.168.56.101:2882:3882
server.3=192.168.56.101:2883:3883

zk3的配置zoo.cfg

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
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/data
dataLogDir=/datalog
# the port at which the clients will connect
clientPort=2183
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=192.168.56.101:2881:3881
server.2=192.168.56.101:2882:3882
server.3=192.168.56.101:2883:3883

启动容器

接下来就可以启动容器了,我是在同一台机器上启动的,用的docker的host网络。生产环境建议在不同的机器上启动zookeeper容器。

1
2
3
4
5
docker run --name zk1 --net host --restart always -d -v/opt/data/zk1/data:/data -v /opt/data/zk1/conf/zoo.cfg:/conf/zoo.cfg -p 2181:2181 -p 2881:2881 -p 3881:3881 zookeeper

docker run --name zk2 --net host --restart always -d -v/opt/data/zk2/data:/data -v /opt/data/zk2/conf/zoo.cfg:/conf/zoo.cfg -p 2182:2182 -p 2882:2882 -p 3882:3882 zookeeper

docker run --name zk3 --net host --restart always -d -v/opt/data/zk3/data:/data -v /opt/data/zk3/conf/zoo.cfg:/conf/zoo.cfg -p 2183:2183 -p 2883:2883 -p 3883:3883 zookeeper

Zookeeper配置详解

以下有些默认值在新版本中可能已经改成其他值了,这个是需要注意的。这里还未去验证确切的默认值

基础配置

配置项 解释
clientPort 该参数无默认值,必须配置,不支持系统属性方式配置。参数clientPort用于配置当前服务器对外的服务器端口,客户端会通过该端口和Zookeeper服务器创建连接,一般设置为2181。每台Zookeeper服务器都可以配置任意可用的端口,同时,集群中的所有服务器不需要保持clientPort端口一致。
dataDir 该参数无默认值,必须配置,不支持系统属性方式配置。参数dataDir用于配置Zookeeper服务器存储快照文件的目录。默认情况下,如果没有配置参数dataLogDir,那么事务日志也会存储在这个目录中。考虑到事务日志的写性能直接影响Zookeeper整体的服务能力,因此建议同时通过参数dataLogDir来配置Zookeeper事务日志的存储目录。
tickTime 该参数有默认值:3000,单位是毫秒(ms),可以不配置,不支持系统属性方式配置。参数tickTime用于配置Zookeeper中最小事件单元的长度,很多运行时的时间间隔都是使用tickTime的倍数来表示的。例如,Zookeeper中会话的最小超时时间默认是2*tickTime.

高级配置

系统属性方式配置:即在Java中,可以通过在启动的命令行参数中添加-D参数来达到配置系统属性的目的

例如 -Djava.library.path=/home/admin/jdk/lib, 就是通过系统属性来配置java.library.path的。

配置项 解释
dataLogDir 该参数有默认值:dataDir,可以不配置,不支持系统属性方式配置。参数dataLogDir用于配置Zookeeper服务器存储事务日志文件的目录。默认情况下,Zookeeper会将事务日志文件和快照数据存储在同一个目录中,使用者应尽量将这两者的目录区分开来。另外,如果条件允许,可以将事务日志的存储配置在一个单独的磁盘上。事务日志记录对于磁盘的性能要求非常高,为了保证数据的一致性,Zookeeper在返回客户端事务请求响应之前,必须将本次请求对应的事务日志写入到磁盘中。因此事务日志写入的性能直接决定了Zookeeper在处理事务请求时的吞吐。针对同一块磁盘的其他并发读写操作(例如Zookeeper运行时日志输出和操作系统自身的读写等),尤其是数据快照操作,会极大的影响事务日志的写性能。因此尽量给事务日志的输出配置一个单独的磁盘或是挂载点,将极大地提升Zookeeper的整体性能
initLimit 该参数有默认值:10,即表示是参数tickTime值的10倍,必须配置,且需要配置一个正整数,不支持系统属性方式配置。该参数用于配置Leader服务器等待Follower启动,并完成数据同步的时间。Follower服务器在启动过程中,会与Leader建立连接并完成对数据的同步,从而确定自己对外提供服务的起始状态。通常情况下,运维人员不用太在意这个参数的配置,使用期默认值即可。但如果随着Zookeeper集群管理的数据量增大,Follower服务器在启动的时候,从Leader上进行同步数据的时间也会相应变长,于是无法在较短的时间完成数据同步。因此,在这种情况下,有必要适当调大这个参数。
syncLimit 该参数有默认值:5,即表示是参数tickTime值的5倍,必须配置,且需要配置一个正整数,不支持系统属性当时配置。该参数用于配置Leader服务器和Follower之间进行心跳检测的最大延时事件。在Zookeeper集群运行过程中,Leader服务器会与所有的Follower进行心跳检测来确定该服务器是否存活。如果Leader服务器在syncLimit时间内无法获取到Follower的心跳检测响应,那么Leader就会认为该Follower已经脱离了和自己的同步。通常情况下,运维人员使用该参数的默认值即可,但如果部署Zookeeper集群的网络环境质量较低(例如网络延时较大或丢包严重),那么可以适当调大这个参数。
snapCount 该参数有默认值:100000,可以不配置,仅支持系统属性方式配置:zookeeper.snapCount。参数snapCount用于配置相邻两次数据快照之间的事务操作次数,即Zookeeper会在snapCount次事务操作之后进行一次数据快照。
preAllocSize 该参数有默认值:65536,单位是KB,即64MB,可以不配置,仅支持系统属性方式配置:zookeeper.preAllocSize。参数preAlloSize用于配置Zookeeper事务日志文件预分配的磁盘空间大小。通常情况下,我们使用Zookeeper的默认配置65536KB即可,但是如果我们将参数snapCount设置得比默认值更小或更大,那么preAllocSize参数也要随之做出变更。举例来说:如果我们将snapCount的值设置为500,同时预估每次事务操作的数据量大小至多1KB,那么参数preAllocSize设置为500就足够了。
minSessionTimeout/maxSessionTimeout 这两个参数有默认值,分别是参数tickTime值的2倍和20倍,即默认的会话超时时间在2tickTime~20tickTime范围内,单位毫秒,可以不配置,不支持系统属性方式配置。这两个参数用于服务端对客户端会话的超时时间进行限制,如果客户端设置的超时时间不在该范围内,那么会被服务端强制设置为最大或最小超时时间。
maxClientCnxns 该参数有默认值:60,可以不配置,不支持系统属性方式配置。从Socket层面限制单个客户端与单台服务器之间的并发连接数,即以IP地址粒度来进行连接数的限制。如果将该参数设置为0,则表示对连接数不做任何限制。需要注意该连接数限制选项的适用范围,其仅仅是对单台客户端机器与单台Zookeeper服务器之间的连接数限制,并不能控制所有客户端的连接数总和。另外,在3.4.0版本以前该参数的默认值都是10,从3.4.0版本开始变成了60,因此运维人员尤其需要注意这个变化,以防Zookeeper版本变化带来服务器连接数限制变化的隐患。
jute.maxbuffer 该参数有默认值:1048575,单位是字节,可以不配置,仅支持系统属性方式配置:jute.maxbuffer。该参数用于配置单个数据节点(ZNode)上可以存储的最大数据量大小。通常情况下,运维人员不需要改动该参数,同时考虑到Zookeeper上不适宜存储太多的数据,往往还需要将该参数设置得更小。需要注意的是,在变更该参数的时候,需要在Zookeeper集群的所有机器以及所有的客户端上均设置才能生效。
clientPortAddress 该参数没有默认值:可以不配置,不支持系统属性方式配置。针对那些多网卡的机器,该参数允许为每个IP地址制定不同的监听端口。
server.id=host:port:port 该参数没有默认值,在单机模式下可以不配置,不支持系统属性方式配置。该参数用于配置组成Zookeeper集群的及其列表,其中id即为ServerID,与每台服务器myid文件中的数字相对应。同时,在该参数中,会配置两个端口:第一个端口用于指定Follower服务器与Leader进行运行时通信和数据同步时所使用的端口,第二个端口则专门用于进行Leader选举过程中的投票通信。在Zookeeper服务器启动的时候,其会根据myid文件中配置的ServerID来确定自己是哪台服务器,并使用对应配置的端口来进行启动。如果在实际使用过程中,需要在同一台服务器上部署多个Zookeeper实例来构成伪集群的话,那么这些端口都需要配置成不同。例如 server.1=192.168.0.1:2777:3777, server.1=192.168.0.1:2888:3888, server.1=192.168.0.1:2999:3999 不过这个参数在3.5.0版本开始,有所改变了,具体查看zookeeper官方文档
autopurge.snapRetainCount 该参数有默认值:3,可以不配置,不支持系统属性方式配置。从3.4.0版本开始,Zookeeper提供了对历史事务日志和快照数据自动清理的支持。参数autopurge.snapRetainCount用于配置Zookeeper在自动清理的时候需要保留的快照数据文件数量和对应的事务日志万能键。需要注意的是,并不是磁盘上的所有事务日志和快照数据文件都可以被清理掉–那样的话将无法恢复数据。因此参数autopurge.snapRetainCount的最小值是3,如果配置的autopurge.snapRetainCount值小于3的话,那么会被自动调整到3,即至少需要保留3个快照数据文件和对应的事务日志文件
autopurge.purgeInteval 该参数有默认值:0,单位是小时,可以不配置,不支持系统属性方式配置。参数autopurge.purgeInterval和参数autopurge.snapRetainCount配套使用,用于配置Zookeeper进行历史文件自动清理的频率。如果配置该值为0或者负数,那么就表明不需要开启定时清理功能。Zookeeper默认不开启这项功能。
fsync.warningthresholdms 该参数有默认值:1000,单位是毫秒,可以不配置,仅支持系统属性方式配置:fsync.warningthresholdms。参数fsync.warningthresholdms用于配置Zookeeper进行事务日志fsync操作时消耗时间的报警阈值。一旦进行一个fsync操作消耗的事件大于参数fsync.warningthresholdms指定的值,那么就在日志中打印出报警日志。
forceSync 该参数有默认值:yes,可以不配置,可选配置项为yesno,仅支持系统属性方式配置:zookeeper.forceSync。该参数用于配置Zookeeper服务器是否在事务提交的时候,将日志写入操作强制刷入磁盘(即调用java.nio.channels.FileChannel.force接口),默认情况下是yes,即每次事务日志写入操作都会实时刷入磁盘。如果将其设置为no。则能一定程度的提高Zookeeper的写性能,但同时也会存在类似于机器断电这样的安全风险。
globalOutstandingLimit 该参数有默认值:1000,可以不配置,仅支持系统属性方式配置:zookeeper.globalOutstandingLimit。参数globalOutstandingLimit用于配置Zookeeper服务器最大请求堆积数量。在Zookeeper服务器运行的过程中,客户端会源源不断的将请求发送到服务端,为了防止服务端资源(包括CPU,内存和网络等)耗尽,服务端必须限制同时处理的请求数,即最大请求堆积数量。
leaderServes 该参数有默认值:yes,可以不配置,可选配置项为yesno,仅支持系统属性方式配置:zookeeper.leaderServes。该参数用于配置Leader服务器是否能够接受客户端的连接,即是否允许Leader向客户端提供服务,默认情况下,Leader服务器能够接受并处理客户端的所有读写请求。在Zookeeper的架构设计中,Leader服务器主要用来进行对事务更新请求的协调以及集群本身的运行时协调,因此,可以设置让Leader服务器不接受客户端的连接,以使其专注于进行分布式协调。
skipAcl 该参数有默认值:no,可以不配置,可选配置项为yesno,仅支持系统属性方式配置:zookeeper.skipACL。该参数用于配置Zookeeper服务器是否跳过ACL权限检查,默认情况下是no,即会对每一个客户端请求进行权限检查。如果将其设置为yes,则能一定程度的提高Zookeeper的读写性能,但同时也会向所有客户端开放Zookeeper的数据,包括那些之前设置过ACL权限的数据节点,也将不再接收权限控制。
xncTimeout 该参数有默认值:5000,单位是毫秒,可以不配置,仅支持系统属性方式配置:zookeeper.cnxTimeout。该参数用于配置在Leader选举过程中,各服务器之间进行TCP连接创建的超时时间。
electionAlg 在之前的版本中,可以使用该参数来配置选择Zookeeper进行Leader选举时所使用的算法,但从3.4.0版本开始,Zookeeper废弃了其他选举算法,只留下了FastLeaderElection算法,因此该参数目前看来没有用了。

参考

https://hub.docker.com/_/zookeeper/

http://zookeeper.apache.org/doc/r3.4.11/zookeeperAdmin.html

https://www.hifreud.com/2017/01/09/zookeeper-06-common-configurations/

http://www.cnblogs.com/sunddenly/articles/4071730.html

https://my.oschina.net/xiaotian120/blog/194820

https://yq.aliyun.com/articles/30328