一文运维zookeeper
文章目錄
- 1. zookeeper生產環境的安裝配置
- 1.1 軟件配置
- 1.2 硬件配置
- 1.3 日志配置文件
- 1.4 配置三節點的zookeeper集群
 
- 2. zookeeper的監控方法
- 2.1 four letters命令
- 2.2 JMX 監控方式
 
- 3. 通過zookeeper observer實現跨地域部署
- 3.1 什么是observer
- 3.2 observer 提升 寫性能
- 3.3 observer 實現跨數據中心部署
 
- 4. zookeeper 不中斷服務的集群成員變更方法
- 5. zookeeper數據存儲文件:事務文件和快照文件
- 6. 總結
 
關于分布式協調服務zookeeper,之前已經對整個框架以及基礎使用場景和環境配置做個一個總體的介紹
 一文入門zookeeper。
ps: 以下實驗是在mac上進行的,有一些mac特有的配置會特別說明
本文中使用的zookeeper版本是3.5.8
本節將簡單介紹zookeeper的基本運維操作,主要包括:
- zookeeper 生產環境的安裝配置
- zookeeper 的監控方法
- 通過zookeeper observer 實現跨地域部署
- 通過動態配置實現不中斷服務的集群成員變更
- 如何查看zookeeper的數據存儲文件:事務文件和快照文件
通過熟悉以上運維操作,能夠更進一步的了解整個zookeeper的架構和實現方式,從而更好得使用它。
1. zookeeper生產環境的安裝配置
1.1 軟件配置
ZooKeeper 的配置項在 zoo.cfg 配置文件中配置, 另外有些配置項可以通過 Java 系統屬性來 進行配置。
- clientPort : ZooKeeper 和客戶端通信的端口號。
- dataDir :來保存快照文件的目錄。如果沒有設置 dataLogDir ,事務日志文件也會保存到這
 個目錄。
- dataLogDir :用來保存事務日志文件的目錄。因為 ZooKeeper 在提交一個事務之前,需要保 證事務日志記錄的落盤,所以需要為 dataLogDir 分配一個獨占的存儲設備。
基本配置文件內容如下,這里是在mac本地進行演示,并沒有增加dataLogDir,默認會和dataDir在一個目錄,如果有足夠的測試服務器可以為該配置分配獨立的目錄:
# 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=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
1.2 硬件配置
建議為zookeeper分配獨立的服務器,同時要給zookeeper的事務日志(dataLogDir)分配獨立的存儲設備或者分區
- 內存:ZooKeeper 需要在內存中保存 data tree 。對于一般的 ZooKeeper 應用場景,8G 的內存足夠了。
- CPU:ZooKeeper 對 CPU 的消耗不高,只要保證 ZooKeeper 能夠有一個獨占的 CPU 核 即可
- 存儲:因為存儲設備的寫延遲會直接影響事務提交的效率,建議為 dataLogDir 分配一個獨占的 SSD 盤
1.3 日志配置文件
日志配置文件默認在 conf/log4j
 基本配置內容如下:
# 日志級別
zookeeper.root.logger=INFO, CONSOLE
# 對于appender來說,也是Info 或者比info 級別更嚴重的log會被打出來
zookeeper.console.threshold=INFOzookeeper.log.dir=.
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=INFO
zookeeper.log.maxfilesize=256MB
zookeeper.log.maxbackupindex=20zookeeper.tracelog.dir=${zookeeper.log.dir}
zookeeper.tracelog.file=zookeeper_trace.loglog4j.rootLogger=${zookeeper.root.logger}#
# console
# Add "console" to rootlogger above if you want to use this 
#
# console appender 的實現類
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
# console 日志輸出的格式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 指定詳細的日志格式
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n#
# Add ROLLINGFILE to rootLogger to get log file output
#
# 一般會使用appender.ROLLINGFILE作為日志追加方式
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold}
log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
log4j.appender.ROLLINGFILE.MaxFileSize=${zookeeper.log.maxfilesize}
log4j.appender.ROLLINGFILE.MaxBackupIndex=${zookeeper.log.maxbackupindex}
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
1.4 配置三節點的zookeeper集群
在服務器充足的情況下,每一個服務區基本的配置步驟如下:
- 申請 ZooKeeper 節點服務器。每個 ZooKeeper 節點有兩個掛載盤。
- 每個節點安裝 JDK 8 。
- 在每個節點為 dataDir 初始化一個獨立的文件系統 /data ,編輯 myid,表示server的編號(每個 server代表一個數字,可以echo 1 > /data/myid 即可) 。
- 各個節點之間配置基于 public key 的 SSH 登錄。
- 在一個節點上下載解壓 apache-zookeeper-3.5.5-bin.tar.gz ,配置 zoo.cfg 。使用 rsync 把解壓的目錄同步到其他節點。
到此即完成基本的環境配置,我們只需要使用zookeeper提供的cli命令進行運維層面的配置即可。
因為我只有一個mac機器,那只能在一個mac機器中啟動三個server進程來模擬zookeeper的集群,這里不需要執行第四步了,對應的第三步通過指定不同的配置目錄即可。
模擬的三個節點的配置如下:
 需要注意的是其中的server.配置,127.0.0.1表示節點ip或者主機(hostname)名稱,7777表示后續zookeeper 集群的qurom通信端口號,7778表示leader選舉的端口號。
同時需要打開選項4lw.commands.whitelist=*表示開啟zookeeper提供的四字母監控命令的白名單。
- zoo-node1.cfgtickTime=2000 initLimit=10 syncLimit=5 dataDir=/tmp/zookeeper1 clientPort=2181 server.1=127.0.0.1:7777:7778 server.2=127.0.0.1:6666:6667 server.3=127.0.0.1:5555:5556 4lw.commands.whitelist=* #開啟監控命令的白名單,否則后續的監控命令無法使用
- zoo-node2.cfgtickTime=2000 initLimit=10 syncLimit=5 dataDir=/tmp/zookeeper2 clientPort=2181 server.1=127.0.0.1:7777:7778 server.2=127.0.0.1:6666:6667 server.3=127.0.0.1:5555:5556 4lw.commands.whitelist=*
- zoo-node3.cfgtickTime=2000 initLimit=10 syncLimit=5 dataDir=/tmp/zookeeper3 # 如果使用服務器可以指定相同的目錄 clientPort=2181 server.1=127.0.0.1:7777:7778 server.2=127.0.0.1:6666:6667 server.3=127.0.0.1:5555:5556 4lw.commands.whitelist=*
啟動zookeeper集群:
 zkServer.h start conf/zoo-node1.cfg
 zkServer.h start conf/zoo-node2.cfg
 zkServer.h start conf/zoo-node3.cfg
啟動成功之后,檢查集群狀態是否有異常
 echo srvr | nc localhost 2181, nc是ncat命令的縮寫是一個網絡工具,mac上可以通過brew install nmap安裝,安裝完成需要將/usr/local/Cellar目錄導入$PATH,否則無法直接使用命令。
└> echo srvr | nc localhost 2181
Zookeeper version: 3.5.8-f439ca583e70862c3068a1f2a7d4d068eec33315, built on 11/15/2020 03:27 GMT
Latency min/avg/max: 0/0/0
Received: 7
Sent: 6
Connections: 1
Outstanding: 0
Zxid: 0x100000006
Mode: follower
Node count: 5
如果執行以上命令出現
 srvr is not executed because it is not in the whitelist.問題,則需要修改cfg配置,加入4lw.commands.whitelist=* 配置,打開四個字母的白名單。記得修改完配置重啟zkServer.sh ,配置才能生效。
2. zookeeper的監控方法
zookeeper 雖然僅僅提供分布式的協調服務,但是仍然有自己的監控系統(一組監控命令),來讓自己的運維系統更加完善。
 接下來主要描述兩種zookeeper原生支持的監控方式,第一種是four letters命令;第二種是JMX方式,這種方式后面的演示在mac上會更方便一點。
2.1 four letters命令
其中four letters 命令由四個字母組成,可以通過 telnet 或 ncat 使用客戶端端口向 ZooKeeper 發出命令。
- ruok , 向zookeeper 集群的一個節點詢問are you ok?
 echo ruok | nc localhost 2181,向指定節點的client通信端口發送信息
 如果回復imok%,則該節點正常,否則返回失敗。這個狀態并不是代表集群狀態,僅僅是集群的某一個節點狀態。 
- conf , 查看節點配置項
 echo conf | nc localhost 2181,輸出如下clientPort=2181 secureClientPort=-1 dataDir=/tmp/zookeeper1/version-2 dataDirSize=67109438 dataLogDir=/tmp/zookeeper1/version-2 dataLogSize=67109438 tickTime=2000 maxClientCnxns=60 minSessionTimeout=4000 maxSessionTimeout=40000 serverId=1 initLimit=10 syncLimit=5 electionAlg=3 electionPort=7778 quorumPort=7777 peerType=0 membership: server.1=127.0.0.1:7777:7778:participant server.2=127.0.0.1:6666:6667:participant server.3=127.0.0.1:5555:5556:participant
- stat, 返回當前server節點和客戶端鏈接信息
 echo stat | nc localhost 2181Zookeeper version: 3.5.8-f439ca583e70862c3068a1f2a7d4d068eec33315, built on 11/15/2020 03:27 GMT Clients:/127.0.0.1:53629[0](queued=0,recved=1,sent=0)Latency min/avg/max: 0/0/0 Received: 3 Sent: 2 Connections: 1 Outstanding: 0 Zxid: 0x100000006 Mode: follower Node count: 5
- dump, 查看zookeeper 中臨時節點的信息
 echo dump | nc localhost 2181SessionTracker dump: Global Sessions(1): 0x100077484570000 30000ms ephemeral nodes dump: Sessions with Ephemerals (1): 0x100077484570000:/worker Connections dump: Connections Sets (3)/(2): 0 expire at Sat Dec 12 12:43:33 CST 2020: 1 expire at Sat Dec 12 12:43:43 CST 2020:ip: /127.0.0.1:54091 sessionId: 0x0 1 expire at Sat Dec 12 12:43:53 CST 2020:ip: /127.0.0.1:54021 sessionId: 0x100077484570000
- wchc, 查看 watch狀態信息└> echo wchc | nc localhost 2181 0x100077484570000/worker
更多的四字命令如下:
conf: 打印ZooKeeper的配置信息
cons: 列出所有的客戶端會話鏈接
crst: 重置所有的客戶端連接
dump: 打印集群的所有會話信息,包括ID,以及臨時節點等信息。用在Leader節點上才有效果。
envi: 列出所有的環境參數
ruok: "諧音為Are you ok"。檢查當前服務器是否正在運行。
stat: 獲取ZooKeeper服務器運行時的狀態信息,包括版本,運行時角色,集群節點個數等信息。
srst: 重置服務器統計信息
srvr: 和stat輸出信息一樣,只不過少了客戶端連接信息。
wchs: 輸出當前服務器上管理的Watcher概要信息
wchc: 輸出當前服務器上管理的Watcher的詳細信息,以session為單位進行歸組
wchp: 和wchc非常相似,但是以節點路徑進行歸組
mntr: 輸出比stat更為詳細的服務器統計信息
2.2 JMX 監控方式
接下來說一下JMX,ZooKeeper 很好的支持了 JMX ,大量的監控和管理工作多可以通過 JMX 來做。
 而且能夠將zookeeper的JMX數據集成到Prometheus,利用其來進行zookeeper的監控。
安裝了JDK環境之后,JMX默認已經安裝,只需要在終端運行jconsole命令(mac)即可啟動
 
 我的環境中已經有三個zkServer,所以會有三個server.quorum,任選一個即可。
 直接點擊連接,會彈出一個不安全連接的彈窗(因為我們并沒有配置集群的ssl安全連接配置),所以這里直接選擇 “不安全連接“即可
 
 能夠看到當前節點的 zookeeper server的資源消耗概覽:
 
 更加詳細的節點細節數據可以通過進入MBean頁面查看。
 
 比如我使用zookeeper客戶端做了如下操作:
 
 原本在jconsole看到的節點個數的信息是
 刷新后可以看到有變更:
 
以上的監控訪問都是通過本地訪問,現在jmx也能夠支持遠程訪問,在被訪問機器配置環境變量:
JMXPORT=8081
重啟zookeeper server以及jconsole
然后在當前節點的jconsole連接窗口輸入如下內容,仍然使用不安全的 連接方式即可訪問遠程節點的zookeeper數據。
 
到此zookeeper的原生監控已經描述完畢,可以看到zookeeper的運維系統還是非常完善的,更加方便的監控就是結合jmx數據和premethus 完成更加完善全面的監控。
3. 通過zookeeper observer實現跨地域部署
3.1 什么是observer
就像是字面含義中所說的 觀察者,不參與,不決策,萬物皆可動,唯我心永恒。
 它作為zookeeper集群中的一個角色而存在,和集群中其他節點的唯一交互就是接受來自leader的inform信息,更新到自己的本地存儲,不參與集群中的事務提交,leader選舉等過程。
下圖為zookeeper寫請求的時序圖:
 
 上圖中:節點2: leader, 節點1和節點3都是follower
- 客戶端提交寫請求到其所連接的節點1
- 節點1 將請求轉發給節點2(節點2是leader),只有leader可寫
- 節點2 發送提案 給集群所有節點
- 節點2等待,只有有超過半數的節點回復,即可進行commit
- 節點1 收到commit消息,即可向客戶端返回成功。
3.2 observer 提升 寫性能
將節點1 設置為observer,這樣能夠減少網絡rpc(一次propose和accept),減少磁盤I/O(只需要等待leader的通知返回客戶端即可)
 
3.3 observer 實現跨數據中心部署
 如上場景,整個集群包括leader節點在內有5個節點,其中北京有3個節點,香港有2個節點。
 假如我們這5個節點的集群只有1個leader和其他4個follower,leader在北京,那么遠在香港的兩個follower每一次寫請求都需要參與到整個集群的propose和accept,這兩個rpc對于跨地域的集群部署來說代價非常大,會嚴重降低系統可用性和性能。
這個時候如上圖,我們將香港的兩個節點設置為observer,2個rpc就可以節省下來,observer只需要forward到leader 并接受leader的inform即可。
當然這樣的集群部署模式在實際的場景還不如分成兩個本地集群,從而降低網絡分區對系統可用性的影響。
那么如何將一個節點部署成observer節點?
實際的部署配置可以 修改observer所在節點的zoo.cfg啟動配置的集群選項配置如下:
server.1=127.0.0.1:7777:7778
server.2=127.0.0.1:6666:6667
server.3=127.0.0.1:5555:5556
server.4=127.0.0.1:3333:3334:observer #增加集群的配置選項,將當前節點指定為observer
4lw.commands.whitelist=*
啟動第四個節點之后,通過echo srvr | nc $ip 2181 指定第四個ip的端口即可知道我們設置的角色是否成功(observer角色)。
4. zookeeper 不中斷服務的集群成員變更方法
上文中我們也有關于調整集群成員的一些操作,總體步驟如下:
- 停止整個集群中所有的server
- 更改現有集群中所有server的cfg項中的server.n項(集群成員變更可能導致某一個節點已有數據被覆蓋)
- 重新啟動集群中所有的server
如果zookeeper集群達到一定規模,這一系列操作low且耗時,尤其是中斷服務,降低系統可用性。
由于這種手動方式調整代價很高,所有zookeeper在3.5.0版本合入了 dynamic reconfiguration特性。
 這個特性能夠支持不停止zookeeper服務的前提下調整集群成員。
- 獲取一個給super用的digest, 其中supper是zookeeper提供的認證機制。
 運行代碼即可生成一個digest認證package org.yao;import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;/*** Generate digest for ZooKeeper super user authentication.*/ public class DigestGenerator {public static void main(String[] args) throws Exception {System.out.println(DigestAuthenticationProvider.generateDigest("super:z_stand"));} }xxx
- 導入該認證到環境變量中export SERVER_JVMFLAGS=-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xxx
- zookeeper配置文件
 zoo-node2.cfg,將server.n相關配置放在了單獨的配置文件中,因為后續動態修改的話是需要修改該文件的內容。
 dyn.cfgtickTime=2000 initLimit=10 syncLimit=5 dataDir=/tmp/zookeeper2 clientPort=2181 4lw.commands.whitelist=* dynamicConfigFile=conf/dyn.cfgserver.1=127.0.0.1:7777:7778:participant;127.0.0.1:2181 server.2=127.0.0.1:6666:6667:participant;127.0.0.1:2181 server.3=127.0.0.1:5555:5556:participant;127.0.0.1:2181
所有節點的配置文件修改好之后可以將每個節點的server都啟動起來。
- 開啟客戶端,這個客戶端需要連接到我們第一步配置認證的那個節點上運行,當然這里是mac上模擬的,所以也就直接連接本地即可。zkCli.sh -server localhost:2181
- 在客戶端的交互命令行中 添加認證
 addauth digest super:z_stand即可
 接下來展示客戶端交互命令如何動態配置節點情況- config顯示集群中的server配置
- reconfig -remove 3移除server.3
- config查看集群成員的變更情況
- reconfig -add server.4=127.0.0.1:3333:3334:participant;127.0.0.1:2181向集群中增加新的成員
 
到此集群成員的動態變更方式基本描述清楚了,感興趣的同學可以嘗試一下,需要注意的supper認證部分,zkCli.sh連接的server是需要完成認證的server節點。
5. zookeeper數據存儲文件:事務文件和快照文件
zookeeper本地存儲中的數據形態如下圖:
 
 data tree 中的znode信息都是存放在內存中的
 而磁盤上主要的是兩種文件:事務文件和快照文件
事務文件中的entry是追加的,每一次有zookeeper集群的寫入(創建znode,刪除znode)都會作為事務更新到事務文件中。事務使用64位的整數zxid唯一標識,其中高四個字節是epoch,低四個字節是counter。
快照文件是當節點啟動/重啟時會創建一個文件
除此之外還會有兩個文件:acceptedEpoch和currentEpoch分別保存當前集群的一些版本信息。
└> ls /tmp/zookeeper1/version-2
acceptedEpoch      log.100000001      log.600000001      snapshot.0
currentEpoch       log.400000001      log.a00000001      snapshot.600000001
zookeeper提供了工具能夠查看其中的內容zkTxnLogToolkit.sh,可以看到如下信息
└> zkTxnLogToolkit.sh log.a00000001
/usr/bin/java
ZooKeeper Transactional Log File with dbid 0 txnlog format version 2
2020/12/12 CST 下午3:18:27 session 0x100080708e50000 cxid 0x0 zxid 0xa00000001 createSession 30000
2020/12/12 CST 下午3:37:55 session 0x100080708e50000 cxid 0x0 zxid 0xa00000002 closeSession
2020/12/12 CST 下午3:55:57 session 0x100080708e50001 cxid 0x0 zxid 0xa00000003 createSession 30000
EOF reached after 3 txns.
當對集群中的數據進行變更時這一些日志條目也會發生變更。
查看zookeeper的快照文件,這里zookeeper僅僅提供了一個類org.apache.server.SnapshotFormatter來查看,可以編寫如下腳本snapshot.sh實現快照文件的查看:
#!/bin/bash
zkEnv.sh
export CLASSPATH="$CLASSPATH"
java org.apache.server.SnapshotFormatter "$@"
最后通過運行sh snapshot.sh /tmp/zookeeper3/version-2/snapshot.0來查看具體的快照文件內容。
ps: 只要zookeeper節點發生重啟,或者啟動 就會生成新的快照文件,記錄當前狀態下zookeeper的內存狀態。
剩下的兩個epoch文件acceptedEpoch和currentEpoch在單機模式下并不存在, 只有在集群模式下才會生成。
6. 總結
以上從zookeeper集群模式的搭建,到基本zookeeper監控,再到zookeeper實現跨地域部署的優缺點 以及 更加高級的 不中斷服務的場景下實現集群成員變更。利用以上特性,我們能夠很好的運維一個zookeeper集群。
對于更加底層的細節:zookeeper的讀寫流程 中如何保證集群寫入的原子性(ZAB協議–multi paxos的一種變種),如何完成節點異常時的數據重放,這一些有趣的分布式細節會在后續討論。
總結
以上是生活随笔為你收集整理的一文运维zookeeper的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 圣冕麒麟的问题
- 下一篇: 永劫无间秘宝猎人成就怎么达成?
