最近因爲業務需求,測試各類組件的高可用性。因爲咱們的環境在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中。