最近因为业务需求,测试各类组件的高可用性。因为咱们的环境在AWS 北京部署。只有两个Aviable Zone(可用区)。php
注释:有两个数据中心,相互须要作容灾的需求,和本文测试的状况是相同的。html
而Zookeeper须要3个以上的单数节点同时工做,而且,必须保证半数以上的节点存活,还能正常提供服务。java
那么,针对只有两个AZ的状况,无论怎么规划,都有几率遇到存在半数以上的AZ挂掉,致使整个Zookeeper不可用的状况。node
因此,咱们能作的就是,在这个AZ挂掉以后,咱们怎么尽快处理,并恢复环境。apache
咱们准备两个软件安装好,参数配置好的机器。在可用区1彻底挂掉以后,能够手动启动两个备用节点。将可用区2的Zookeeper数量增长过半数。就能够在可用区2恢复Zookeeper的服务。api
参考下图:服务器
以上的设想,是否能实现呢?session
那咱们今天就来测试一下。socket
1. 一共准备了5台机器,做为测试ide
2. Zookeeper的下载与安装。
2.1 Zookeeper官方下载地址
https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/
2.2 下载软件
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
2.3 详细Zookeeper安装步骤,请参考:
https://blog.51cto.com/hsbxxl/1971241
2.4 zoo.cfg的配置 #cat zoo.cfg
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/log clientPort=2181 autopurge.snapRetainCount=3 autopurge.purgeInterval=6 server.1=172.31.9.73:2888:3888 server.2=172.31.20.233:2888:3888 server.3=172.31.26.111:2888:3888 server.4=172.31.17.68:2888:3888 server.5=172.31.16.33:2888:3888
2.5 根据zoo.cfg建立data和log两个文件夹
mkdir -p /data/zookeeper/data mkdir -p /data/zookeeper/log
2.6 根据节点号码,修改文件
echo 1 > /data/zookeeper/data/myid
3. 一共准备了5台EC2进行测试,而且都已经安装好Zookeeper
可是只启动三台,另两个机器做为standby
下图能够看到,已经有三台启动zookeeper,
注意,在Zookeeper启动的过程当中,必须保证三台及以上,zookeeper集群才能正常工做
4. 接下来,我开始逐个机器关机,看zookeeper的状态
当前leader在zk3上,咱们先关闭zk1,再关闭zk3,看Leader会不会飘到zk2上
4.1 在zk1上执行kill,杀掉进程
[root@ip-172-31-9-73 ~]# jps 12438 Jps 7545 QuorumPeerMain [root@ip-172-31-9-73 ~]# zkServer.sh status ZooKeeper JMX enabled by default Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg Mode: follower [root@ip-172-31-9-73 ~]# kill -9 7545
4.2 在zk5上经过zkCli连接zk3,并能够查询数据。
在zk1上kill掉进程以后,理论上,还有zk2和zk3存活,可是zkCli的链接显示已经报错。
[root@ip-172-31-16-33 bin]# ./zkCli.sh -server 172.31.26.111:2181 Connecting to 172.31.26.111:2181 ...... [zk: 172.31.26.111:2181(CONNECTED) 0] ls / [zk-permanent, zookeeper, test] [zk: 172.31.26.111:2181(CONNECTED) 1] 2019-06-23 07:28:06,581 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1158] - Unable to read additional data from server sessionid 0x30000c504530000, likely server has closed socket, closing socket connection and attempting reconnect ...... 2019-06-23 07:28:09,822 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1025] - Opening socket connection to server ip-172-31-26-111.cn-north-1.compute.internal/172.31.26.111:2181. Will not attempt to authenticate using SASL (unknown error) 2019-06-23 07:28:09,824 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@879] - Socket connection established to ip-172-31-26-111.cn-north-1.compute.internal/172.31.26.111:2181, initiating session 2019-06-23 07:28:09,825 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1158] - Unable to read additional data from server sessionid 0x30000c504530000, likely server has closed socket, closing socket connection and attempting reconnect
4.3 咱们继续 kill掉zk3上的进程,只保留zk2上的进程。可是咱们已经没法确认zk2是Leader仍是Follow,或者说,他是否还保留有数据。
[root@ip-172-31-26-111 bin]# jps 4183 QuorumPeerMain 4648 Jps [root@ip-172-31-26-111 bin]# kill -9 4183 [root@ip-172-31-26-111 bin]# jps 4658 Jps
4.4 zk3上进程kill掉以后,连接就不仅是上面的报错了,而是直接链接拒绝
[root@ip-172-31-16-33 bin]# ./zkCli.sh -server 172.31.26.111:2181 Connecting to 172.31.26.111:2181 ...... Welcome to ZooKeeper! 2019-06-23 07:35:18,411 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1025] - Opening socket connection to server ip-172-31-26-111.cn-north-1.compute.internal/172.31.26.111:2181. Will not attempt to authenticate using SASL (unknown error) JLine support is enabled 2019-06-23 07:35:18,533 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1162] - Socket error occurred: ip-172-31-26-111.cn-north-1.compute.internal/172.31.26.111:2181: Connection refused [zk: 172.31.26.111:2181(CONNECTING) 0] 2019-06-23 07:35:19,639 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1025] - Opening socket connection to server ip-172-31-26-111.cn-north-1.compute.internal/172.31.26.111:2181. Will not attempt to authenticate using SASL (unknown error) 2019-06-23 07:35:19,640 [myid:] - INFO [main-SendThread(ip-172-31-26-111.cn-north-1.compute.internal:2181):ClientCnxn$SendThread@1162] - Socket error occurred: ip-172-31-26-111.cn-north-1.compute.internal/172.31.26.111:2181: Connection refused
4.5 能够看到zk2上的进程还在,
# jps 5155 QuorumPeerMain 5211 Jps
4.6 而且经过下面命令,能够检查到zk2 的2181端口还在提供服务
# echo ruok | nc localhost 2181 imok
4.7 可是其余命令是没有正常输出的,只有echo ruok | nc localhost 2181输出ok。
# echo ruok | nc 172.31.16.33 2181 imok[root@ip-172-31-16-33 bin]# echo conf | nc 172.31.16.33 2181 This ZooKeeper instance is not currently serving requests # echo dump | nc 172.31.16.33 2181 This ZooKeeper instance is not currently serving requests
4.8 ZooKeeper 四字命令
ZooKeeper 四字命令 |
功能描述 |
conf |
输出相关服务配置的详细信息。 |
cons |
列出全部链接到服务器的客户端的彻底的链接 / 会话的详细信息。包括“接受 / 发送”的包数量、会话 id 、操做延迟、最后的操做执行等等信息。 |
dump |
列出未经处理的会话和临时节点。 |
envi |
输出关于服务环境的详细信息(区别于 conf 命令)。 |
reqs |
列出未经处理的请求 |
ruok |
测试服务是否处于正确状态。若是确实如此,那么服务返回“imok ”,不然不作任何相应。 |
stat |
输出关于性能和链接的客户端的列表。 |
wchs |
列出服务器 watch 的详细信息。 |
wchc |
经过 session 列出服务器 watch 的详细信息,它的输出是一个与watch 相关的会话的列表。 |
wchp |
4.9 正常状况下,以上命令能够输出:
# echo dump | nc 172.31.20.233 2181
SessionTracker dump: org.apache.zookeeper.server.quorum.LearnerSessionTracker@77714302 ephemeral nodes dump: Sessions with Ephemerals (0):
# echo conf | nc 172.31.20.233 2181
clientPort=2181 dataDir=/data/zookeeper/data/version-2 dataLogDir=/data/zookeeper/log/version-2 tickTime=2000 maxClientCnxns=60 minSessionTimeout=4000 maxSessionTimeout=40000 serverId=2 initLimit=10 syncLimit=5 electionAlg=3 electionPort=3888 quorumPort=2888 peerType=0
# echo envi| nc 172.31.20.233 2181
Environment: zookeeper.version=3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT host.name=ip-172-31-20-233.cn-north-1.compute.internal java.version=1.8.0_212 java.vendor=Oracle Corporation java.home=/usr/java/jdk1.8.0_212-amd64/jre java.class.path=/root/zookeeper-3.4.14/bin/../zookeeper-server/target/classes:/root/zookeeper-3.4.14/bin/../build/classes:/root/zookeeper-3.4.14/bin/../zookeeper-server/target/lib/*.jar:/root/zookeeper-3.4.14/bin/../build/lib/*.jar:/root/zookeeper-3.4.14/bin/../lib/slf4j-log4j12-1.7.25.jar:/root/zookeeper-3.4.14/bin/../lib/slf4j-api-1.7.25.jar:/root/zookeeper-3.4.14/bin/../lib/netty-3.10.6.Final.jar:/root/zookeeper-3.4.14/bin/../lib/log4j-1.2.17.jar:/root/zookeeper-3.4.14/bin/../lib/jline-0.9.94.jar:/root/zookeeper-3.4.14/bin/../lib/audience-annotations-0.5.0.jar:/root/zookeeper-3.4.14/bin/../zookeeper-3.4.14.jar:/root/zookeeper-3.4.14/bin/../zookeeper-server/src/main/resources/lib/*.jar:/root/zookeeper-3.4.14/bin/../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=4.14.123-86.109.amzn1.x86_64 user.name=root user.home=/root user.dir=/root/zookeeper-3.4.14/bin
5. 这个时候,我去启动另外两个备用节点,zk4,zk5.这个两个节点都是第一次启动。
6. 再次链接到zookeeper上,能够看到,至少数据仍是没有丢失的
[root@ip-172-31-16-33 bin]# ./zkCli.sh -server 172.31.16.33:2181 Connecting to 172.31.16.33:2181 ...... [zk: 172.31.16.33:2181(CONNECTED) 0] ls / [zk-permanent, zookeeper, test]
7. 经过以上测试,彷佛是达到咱们预期的结果。惟一的一点小问题,就是:咱们有3个节点,为何关闭1个,剩余两个,就不能正常运行了呢?
其实,这里是有个“想固然”的小问题。
咱们觉得,只启动三个. 其实,Zookeeper集群,识别的是5个, 为何呢?
Zookeeper靠什么去识别集群中有几个节点呢?固然不是靠“想固然”。必定是有配置文件告诉它。Zookeeper,只有两个配置文件zoo.cfg和myid。
那就只有zoo.cfg会影响到它了。
8. 我将zoo.cfg作以下修改以后。只开启3个节点,在关闭一个节点以后,仍是能够正常运行的。
注释掉server2和server5
# cat zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/log clientPort=2181 autopurge.snapRetainCount=3 autopurge.purgeInterval=6 server.1=172.31.9.73:2888:3888 #server.2=172.31.20.233:2888:3888 server.3=172.31.26.111:2888:3888 server.4=172.31.17.68:2888:3888 #server.5=172.31.16.33:2888:3888
9. 关闭server4以后,还有server2和server3活着。
[root@ip-172-31-26-111 ~]# zkServer.sh status ZooKeeper JMX enabled by default Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg Mode: leader [root@ip-172-31-9-73 ~]# zkServer.sh status ZooKeeper JMX enabled by default Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg Mode: follower
10. 总结,若是考虑两个AZ的状况下,zookeeper节点数多的AZ出现灾难状况,咱们如何快速恢复?
(假设Server1/Server2在1AZ,Server3/Server4/Server5在2AZ)
10.1. 在Zookeeper节点少的AZ,多准备2台配置好zookeeper的EC2,并关机待使用。Server4/Server5具体zoo.cfg配置以下
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/log clientPort=2181 autopurge.snapRetainCount=3 autopurge.purgeInterval=6 server.3=172.31.26.111:2888:3888 server.4=172.31.17.68:2888:3888 server.5=172.31.16.33:2888:3888
10.2. Server1/Server2/Server3,是正常运行的节点,配置以下:
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/log clientPort=2181 autopurge.snapRetainCount=3 autopurge.purgeInterval=6 server.1=172.31.9.73:2888:3888 server.2=172.31.20.233:2888:3888 server.3=172.31.26.111:2888:3888
10.3. 灾难发生,Server1/Server2所在的1AZ挂掉的状况下,须要人工介入,将Server3的配置更改成以下配置,并重启Server3的zookeeper服务,而后启动Server4/Server5,必定要先启动Server3,注意顺序。
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/log clientPort=2181 autopurge.snapRetainCount=3 autopurge.purgeInterval=6 server.3=172.31.26.111:2888:3888 server.4=172.31.17.68:2888:3888 server.5=172.31.16.33:2888:3888
10.4 平常运行状态
10.5 检查已经建立的znode信息
./zkCli.sh -server 172.31.16.33:2181 ls / Connecting to 172.31.16.33:2181 [zk-permanent, zookeeper, test]
10.6 关闭Server1/Server2,注意顺序,先关闭follow,若是先关闭leader,会发生切换。咱们指望的是Server3最后以follow的身份存活。
11. 最终能够看到测试结果,一切都是按照咱们“想固然”的方向发展。
12. 最后验证zookeeper中的znode数据,仍是都存在的。
./zkCli.sh -server 172.31.16.33:2181 ls / Connecting to 172.31.16.33:2181 [zk-permanent, zookeeper, test]
13. 其实数据一直是在这个路径下,只要有一个节点还保留,就会保存下去。
# ls /data/zookeeper/data/ myid version-2 zookeeper_server.pid
注意:必定要保证Server4/Server5的下面两个路径是空的,否则会出现,Server4/Server5识别的是以前的陈旧信息。
/data/zookeeper/data/version-2 /data/zookeeper/log/version-2
14. 说到这里,咱们能够理解到,Zookeeper的所有数据,都是存放在下面两个路径中。若是须要作备份,能够直接在OS层面,作cp备份便可。
dataDir=/data/zookeeper/data dataLogDir=/data/zookeeper/log
衍生一个想法,就是若是想作跨Region,北京(主环境)到宁夏(容灾环境)的zookeeper的高可用怎么作呢?
咱们能够考虑将北京的zookeeper的数据文件按期备份,并导入到宁夏的环境。
具体步骤:
<1. 在宁夏启动一个Zookeeper集群,并配置好,而后关闭zookeeper服务,清空掉数据文件夹。
<2. 在北京,经过脚本按期检查zookeeper各个节点状态,从一个运行健康的节点,按期备份数据到S3的一个bucket,为每一个文件加上时间戳。
<3. 经过S3的Cross Region Replication,同步到宁夏。
<4. 而后在宁夏,从S3读取备份文件,并还原到灾备的zookeeper中。