Zookeeper 运维管理

郭武
ZooKeeper简介

  Apache ZooKeeper是Apache软件基金会的一个软件项目,为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。ZooKeeper的架构通过冗余服务实现高可用性。因此,如果第一次无应答,客户端就可以询问另一台ZooKeeper主机。ZooKeeper节点将它们的数据存储于一个分层的命名空间,非常类似于一个文件系统或一个前缀树结构。客户端可以在节点读写,从而以这种方式拥有一个共享的配置服务,更新是全序的。
  ZooKeeper曾经是Hadoop的一个子项目,但现在是一个独立的顶级项目。使用ZooKeeper的公司包括Rackspace、雅虎和eBay,以及类似于像Solr这样的开源企业级搜索系统。[摘自维基百科]

快速启动

  快速启动旨在提供使用方最简单的步骤启动一个zk服务,甚至一个zk集群。 下载最新zookeeper代码,配置zoo.cfg 根据部署机器信息修改conf/zoo.cfg文件。
主要参数及其说明如下:
tickTime=2000: ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的超时时间是2*tickTime~20 *tickTime。

initLimit=10: Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许F在initLimit时间内完成这个工作。

syncLimit=5: 在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题。

dataDir=/home/xxx/data: 存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能。

dataLogDir=/home/xxx/logs: 事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能。
clientPort=2181: 客户端连接server的端口,即对外服务端口,一般设置为2181。

autopurge.purgeInterval=24: 3.4.0及之后版本,ZK提供了自动清理事务日志和快照文件的功能,这个参数指定了清理频率,单位是小时,需要配置一个1或更大的整数,默认是0,表示不开启自动清理功能。
autopurge.snapRetainCount=500: 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个。

server.id=host:port1:port2 这里server后面的数字,与myid文件中的id是一致的,我们称之为Server ID。右边可以配置两个端口,第一个端口用于Follower和Leader之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信。(单机模式则不需要配置此参数)

配置myid(单机模式不需要配置) 在dataDir目录下touch myid文件。myid文件中只有一个数字,即一个Server ID。例如,server.1 的myid文件内容就是“1”。注意,请确保每个server的myid文件中id数字不同,并且和server.id=host:port:port中的id一致。另外,id的范围是1~255。启动zookeeper服务 执行bin目录下的zkServer.sh来启动zookeeper服务
sh zkServer.sh start
检验检查zookeeper服务:
  A. 使用zkCli.sh来访问zookeeper 执行
sh zkCli.sh -server 127.0.0.1:2181
  B. 使用4字命令查看服务器状态 echo srvr|nc localhost 2181

部署管理
1. 单机维度

  对于zookeeper来说,如果在运行过程中,需要和其它应用程序来竞争磁盘,CPU,网络或是内存资源的话,那么整体性能将会大打折扣。 首先来看看磁盘对于ZK性能的影响。客户端对zookeeper的更新操作都是永久的,不可回退的,也就是说,一旦客户端收到一个来自server操作成功的响应,那么这个变更就永久生效了。为做到这点,ZK会将每次更新操作以事务日志的形式写入磁盘,写入成功后才会给予客户端响应。明白这点之后,你就会明白磁盘的吞吐性能对于ZK的影响了,磁盘写入速度制约着ZK每个更新操作的响应。为了尽量减少zookeeper在读写磁盘上的性能损失,不仿试试下面说的几点:
 a. 使用单独的磁盘作为事务日志的输出(比如我们这里的ZK集群,使用单独的挂载点用于事务日志的输出)。事务日志的写性能确实对zooKeeper性能,尤其是更新操作的性能影响很大,所以想办法搞到一个单独的磁盘吧!ZK的事务日志输出是一个顺序写文件的过程,本身性能是很高的,所以尽量保证不要和其它随机写的应用程序共享一块磁盘,尽量避免对磁盘的竞争。
 b. 尽量避免内存与磁盘空间的交换。如果希望ZK能够提供完全实时的服务的话,那么基本是不允许操作系统触发此类swap的。因此在分配JVM堆大小的时候一定要非常小心

2. 集群纬度

  为了确保zookeeper服务的稳定与可靠性,通常是搭建成一个ZK集群来对外提供服务。关于zookeeper,需要明确一个很重要的特性:集群中只要有过半的机器是正常工作的,那么整个集群对外就是可用的。正是基于这个特性,建议是将zookeeper集群的机器数量控制为奇数较为合适。为什么选择奇数台机器,我们可以来看一下,假如是4台机器构成的zookeeper集群,那么只能够允许集群中有一个机器down掉,因为如果down掉2台,那么只剩下2台机器,显然没有过半。而如果是5台机器的集群,那么就能够对2台机器down掉的情况进行容灾了。

日常运维
1. 清理数据

  dataDir目录指定了ZK的数据目录,用于存储ZK的快照文件(snapshot)。另外,默认情况下,ZK的事务日志也会存储在这个目录中。在完成若干次事务日志之后(在zookeeper中,凡是对数据有更新的操作,比如创建节点,删除节点或是对节点数据内容进行更新等,都会记录事务日志),ZK会触发一次快照(snapshot),将当前server上所有节点的状态以快照文件的形式dump到磁盘上去,即snapshot文件。这里的若干次事务日志是可以配置的,默认是100000。考虑到ZK运行环境的差异性,以及对于这些历史文件,不同的管理员可能有自己的用途(例如作为数据备份),因此默认ZK是不会自动清理快照和事务日志,需要交给管理员自己来处理。 从3.4.0版本开始,zookeeper提供了自己清理历史文件的功能,相关的配置参数是autopurge.snapRetainCount和autopurge.purgeInterval。在git仓库中默认提供的清理参数为autopurge.purgeInterval=24和autopurge.snapRetainCount=500,代表每24小时清理一次数据,包含快照文件以及事务日志,并最多保留500份。

2.日志规范

  zookeeper使用log4j作为日志系统,conf目录中有一份默认的log4j配置文件。我们可以根据自己的需要开启rollingfile文件输出,并将日志根据日志级别分别打印出info、warn、error日志文件。如需要修改日志格式的,修改log4j配置文件即可。

3.监控

  鉴于zookeeper的重要程度,对zookeeper的监控变得必不可少,主要包括两方面:
 a. 机器的硬件监控 机器CPU/MEM/LOAD、zookeeper日志目录所在磁盘空间监控
 b. zookeeper的服务监控 zookeeper运行时信息展示,单机连接以及连接数峰值报警,单机Watcher以及Watcher数的峰值报警

4.数据文件管理

  zookeeper中主要的两种文件分别是数据快照文件以及事务日志。
a.数据快照
  zookeeper在进行数据快照过程中,会生成snapshot文件,存储在dataDir目录中。文件后缀是zxid,也就是事务id。(这个zxid代表了zk触发快照那个瞬间,提交的最后一个事务id)。注意,一个快照文件中的数据内容和提交第zxid个事务时内存中数据近似相同。仅管如此,由于更新操作的幂等性,ZK还是能够从快照文件中恢复数据。数据恢复过程中,将事务日志和快照文件中的数据对应起来,就能够恢复最后一次更新后的数据了。

b.事务日志
  正常运行过程中,针对所有更新操作,在返回客户端“更新成功”的响应前,ZK会确保已经将本次更新操作的事务日志写到磁盘上,只有这样,整个更新操作才会生效。每触发一次数据快照,就会生成一个新的事务日志。事务日志的文件名是log.zxid是写入这个文件的第一个事务id。

c. 查看快照以及日志命令
  java -cp ../../zookeeper-3.4.9/zookeeper-3.4.9.jar:../../zookeeper-3.4.9/lib/slf4j-api-1.6.1.jar org.apache.zookeeper.server.SnapshotFormatter snapshot.6081b4445

  java -cp ../../zookeeper-3.4.9/lib/slf4j-api-1.6.1.jar:../../zookeeper-3.4.9/zookeeper-3.4.9.jar org.apache.zookeeper.server.LogFormatter log.6081ab380

运维操作
1.扩容

有节点 A, B, C 处于服务状态 server.1=192.168.12.1:2888:3888 server.2=192.168.12.2:2888:3888 server.3=192.168.12.3:2888:3888
加入节点 D,配置如下:
server.1=192.168.12.1:2888:3888 server.2=192.168.12.2:2888:3888 server.3=192.168.12.3:2888:3888 server.4=192.168.12.4:2888:3888 server.5=192.168.12.5:2888:3888

用4字命令检查,保证该节点同步完毕集群数据,处于Follower状态:
Zookeeper version: 3.4.9-1757313, built on 08/23/2016 06:50 GMT
Latency min/avg/max: 0/0/78
Received: 755753
Sent: 758304
Connections: 39
Outstanding: 0
Zxid: 0x30002ac33
Mode: follower
Node count: 5318
注意:这一步加入的节点的 id,必须大于集群中原有的节点的 id,例如 6 > 3,4,5 同上一步,加入节点E 更新A、B、C的配置如D、E,并依次重启

2.缩容

例如服务一共F、E、D、C、B(、A)五(六)台机器。需要将C、B(、A)下掉。更新F、E、D的集群配置,将配置中的C、B(、A)三台机器的配置移除,并逐个重启。 此时C、B(、A)不可用,下掉C、B(、A)机器

server.4=192.168.12.4:2888:3888 server.5=192.168.12.5:2888:3888 server.6=192.168.12.6:2888:3888

注意:下掉的机器中不能含有leader机器,如果leader在下掉的机器中,需要先通过重启将leader转移至其他机器。

注意事项
  1. 保持Server地址列表一致
    客户端使用的server地址列表必须和集群所有server的地址列表一致。(如果客户端配置了集群机器列表的子集的话,也是没有问题的,只是少了客户端的容灾。) 集群中每个server的zoo.cfg中配置机器列表必须一致。
  2. 独立的事务日志输出
    对于每个更新操作,ZK都会在确保事务日志已经落盘后,才会返回客户端响应。因此事务日志的输出性能在很大程度上影响ZK的整体吞吐性能。强烈建议是给事务日志的输出分配一个单独的磁盘。
  3. 配置合理的JVM堆大小
    确保设置一个合理的JVM堆大小,如果设置太大,会让内存与磁盘进行交换,这将使ZK的性能大打折扣。例如一个4G内存的机器,如果你把JVM的堆大小设置为4G或更大,那么会使频繁发生内存与磁盘空间的交换,通常设置成3G就可以了。