Zookeeper 的部署和運維小記

第一章   Zookeeper 的架構原理和使用

第 4 節

Zookeeper 的部署和運維

部署 Zookeeper 集羣

虛擬機環境規劃

YynS7a

1)安裝 CentOS7 操作系統

使用 visualbox(或 VMware)安裝 CentOS-7-x86_64-Minimal-1804.iso(Minimal 表示是最小化版本)

2)CentOS7 系統基礎配置

#1.設置網絡環境
vi /etc/sysconfig/network-scripts/ifcfg-enp0s3
ONBOOT=yes
IPADDR=192.168.30.10
BOOTPROTO=static

/etc/init.d/network restart

#2.關閉防火牆和Selinux
systemctl disable firewalld
systemctl stop firewalld

vi /etc/selinux/config
SELINUX=disabled
setenforce 0

#3.準備安裝環境,安裝一個上傳下載的命令、清空目錄。之後軟件都安裝在/usr/local目錄下
yum install -y lrzsz
cd /usr/local
rm -rf *

#4 設置置主機名
vi /etc/hostname
vi /etc/sysconfig/network
hostname centos7_1

vi /etc/hosts
#配置3臺機器的域名映射
192.168.80.30 centos7_1
192.168.80.31 centos7_2
192.168.80.32 centos7_3

## ctrl+D退出登錄,重新登錄,可以check下host配置是否生效
#到這裏,我們可以使用CRT和XShell連接了。

##############可選配置##################
#配置dns (可選)
[root@localhost ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8

#自動補全 (可選)
yum install bash-completion

其他 2 臺虛擬機類似的重複以上 3 步。(當然熟練使用 VmWare 的同學,可以使用克隆功能,修改下克隆後的虛擬機 mac 地址和網絡配置中的 ip、host 配置,這樣可以省去重新安裝操作系統的步驟。不過安裝 CentOS-7-x86_64-Minimal-1804.iso 時間也不長,一般也就 5 分鐘)。

2、Zookeeper 的集羣部署

下載 jdk1.8 linux 版本,下載 zookeeper-3.4.5.tar.gz。通過 rz 命令,上傳到 / usr/local 目錄下。之後按照如下腳本執行命令即可。

#配置JDK環境
tar zxf jdk-8u201-linux-x64.tar.gz
mv jdk1.8.0_201/ jdk
vi ~/.bashrc
export ZOOKEEPER_HOME=/usr/local/zk
export JAVA_HOME=/usr/local/jdk
export CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin
source ~/.bashrc
#安裝zookeeper(設置環境變量、
cd /usr/local
tar zxf zookeeper-3.4.5.tar.gz
mv zookeeper-3.4.5 zk
vi ~/.bashrc
export ZOOKEEPER_HOME=/usr/local/zk
export PATH=$PATH:$ZOOKEEPER_HOME/bin
source ~/.bashrc

#修改zookeeper配置文件
cd /usr/local/zk/conf/
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg
#修改
dataDir=/usr/local/zk/data
#新增
server.0=node1:2888:3888        
server.1=node1:2888:3888
server.2=node3:2888:3888

# 創建數據存放目錄
mkdir /usr/local/zk/data
cd /usr/local/zk/data
vi myid
0
#檢查命令:
cat /usr/local/zk/conf/zoo.cfg
cat /usr/local/zk/data/myid

#拷貝整個zk目錄到其他2臺機器
scp -P 22 -r zk/ root@192.168.30.11:/usr/local/  
scp -P 22 -r zk/ root@192.168.30.12:/usr/local/  
#修改下兩臺機器的myid配置,分別爲1、2

#啓動:分別在三臺機器上執行
zkServer.sh start
#檢查ZooKeeper狀態 應該是一個leader,兩個follower
zkServer.sh status
#檢查三個節點是否都有QuromPeerMain進程
jps

部署完成後,最終得到了如下圖所示的 Zookeeper 集羣:

Tips: 2888 端口:數據同步和運行時通信  3888 端口:leader 選舉是通信 2181 端口:提供給客戶端連接使用。

Zookeeper 的機器配置和啓動參數分析

通過上面的步驟, 你應該可以基本跑起來 Zookeeper 了。但是我們用的是虛擬機,但是實際生產運維部署的時候,Zookeeper 的物理機的配置又應該怎麼選擇呢?

其實這個要結合之前介紹的 Zookeeper 基本架構原理,就不難分析出。

首先 Zookeeper 需要支撐高併發,CPU 核數高肯定是好的 16 核,一般就算比較高配了,當然也可以是 24、36、48 核一般中小公司,甚至一些大公司都是是用不到的,只有超大互聯網公司纔可能用,一般使用 16 核就差不多了。

Zookeeper 需要支撐高性能,數據主要在內存維護。而且內存結構無法存海量數據,所以內存配置的大一點最好。一般可以使用 32G 的內存。

Zookeeper 爲了保證數據一致性,受單機寫、過半寫限制、寫的時候又會持久化事務日誌和快照數據,這個寫入速度越快越好,而且是順序寫爲主。所以磁盤使用 SSD 硬盤,肯定可以更好的提高寫吞吐量。

通過上面的分析,可以得出,一般建議 Zookeeper 使用 16 核 32G SSD 硬盤的物理機配置,而且 Zookeeper 集羣一般是小集羣部署,所以通常是 3 臺或 5 臺這樣的高配物理機。

有了上面的機器配置後,這樣的集羣到你能抗多少的寫 qps 和讀 qps 呢?你可以參考下官方給出的一個壓測報告,如下圖所示:

上面是 3.2 版本,2 核並且是 SATA 硬盤的測試,每秒讀寫都是 1k 的測試。都能輕鬆的抗下幾萬的 qps。

那麼一般來說,16 核 32G,3 臺機器,1 個 leader,2 個 follower,leader 主要是寫,每秒抗幾萬併發寫入,每秒抗十幾萬的讀是沒有問題的。

之前我們提到過,Zookeeper 的寫 QPS 無法線性的擴展。但是讀可以通過增加 Observer 節點來擴展。所以如果需要抗更多的讀 QPS,可以考慮使用 Observer。

實際在中間件使用時,幾乎不會用到 Observer 節點,因爲中間的系統的高併發不是在 Zookeeper 上,通常是在中間件上。而一般只有一些業務系統,大數據或者 java 業務系統需要使用 zk 做分佈式協調通知、配置管理等纔可能有高併發讀的場景。可能需要擴展 Observer 節點。

你可以取找運維聊聊,看看你們公司的 Zookeeper 機器集羣是什麼樣的配置。熟悉生產機器配置不光是運維知道就夠了,如果你是高級、資深、架構師、技術專家這個級別的程序員,這個起碼要掌握的。這樣你才能更好地 cover 住你負責的系統,對系統的瓶頸做到心中有數。

zookeeper 的啓動參數

你還記得 zk 啓動時的命令麼?啓動命令如下:

zkServer.sh start

腳本對應的核心啓動代碼如下:

if [ "x$JMXDISABLE" = "x" ]
then
   echo "JMX enabled by default" >&2
   # for some reason these two options are necessary on jdk6 on Ubuntu
   #   accord to the docs they are not necessary, but otw jconsole cannot
   #   do a local attach
   # 默認JMX端口,是隻對本地開放
   ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain"
else
   echo "JMX disabled by user request" >&2
   #QuorumPeerMain代表了peer架構,Quorum機制,大多是成功就算成功
   ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain"
fi
##省略其他
case $1 in
start)
   echo  -n "Starting zookeeper ... "
   if [ -f $ZOOPIDFILE ]; then
     if kill -0 `cat $ZOOPIDFILE` > /dev/null 2>&1; then
        echo $command already running as process `cat $ZOOPIDFILE`.
        exit 0
     fi
   fi
   #通過nohup啓動一個後臺線程,啓動入口就是之前的ZOOMAIN變量指定的代碼類
  nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
   -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &

 可以看到 zk 啓動主要是通過 zkServer.sh start,之後設置了一個環境變量 ZOOMAIN,在之後通過 nohup 啓動了一個進程。整體大致如下圖所示:

調整 zookeeper 的 JVM 參數

從之前的啓動參數可以看出,$JVMFLAGS 這個就是 zookeeper 設置 JVM 參數的變量。默認是通過 zookeeper/bin/zkEnv.sh 這個腳本設置這個變量的。如下所示:

可以看到,通過 zookeeper/conf/java.env 這個文件設置 java 相關參數的。當然如果你直接在啓動腳本 zkServer.sh 的啓動命令裏中加,當然也沒有什麼問題。

zookeeper 設置 JVM 內存參數思路,由於 zookeeper 使用了高配的物理機,內存一般是 16G 或者更大。此時使用經典的 ParNew+CMS 垃圾回收器就不太適合了,大內存一般使用 G1 會好一點。

而 G1 默認 region 是 2MB,儘可能最大回收回收時間是 200ms,這個默認值是否需要調整根據 zookeeper 存放數據的情況,運行情況來看。你可以通過 jstat 觀察高峯時,對象的回收情況,年輕代、老年代回收的次數、時間,增長大小等等。來進行調優,這裏我就不帶大家展開討論了。這裏給一個 demo 供大家參考。

默認這個文件沒有創建,你可以創建如下的 java.env:

#!/bin/sh

export JAVA_HOME=$ZOOBINDIR/../../jdk6

# heap size MUST be modified according to cluster environment

export JVMFLAGS="-server -Xms12g -Xmx12g -Xmn6g -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=1000 -verbose:gc -Xloggc:/usr/local/zk/log/zk_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m -XX:-OmitStackTraceInFastThrow -XX:+AlwaysPreTouch -XX:MaxDirectMemorySize=10g -XX:-UseLargePages -XX:-UseBiasedLocking $JVMFLAGS"

這些參數我簡單解釋一下:

-server:這個參數就是說用服務器模式啓動,這個沒什麼好說

-Xms18g -Xmx18g -Xmn10g :由於使用了高配物理機是 32g 內存的。如果你不太好評估 zookeeper 之後的使用場景的話,有一個經驗值是,總堆內存一般不超過機器內存的 80%,年輕代一般爲堆內存的一半左右。之後觀察下,有需要在調整就可以了。

-XX:+UseG1GC -XX:G1HeapRegionSize=16m:選用了 G1 垃圾回收器來做分代回收,這裏把 G1 的 region 大小設置爲了 16m,這個因爲機器內存比較多,所以 region 大小可以調大一些給到 16m,不然用 2m 的 region,會導致 region 數量過多的。

-XX:G1ReservePercent=25:這個參數是,默認值是 10%,意思是在 G1 管理的老年代裏預留 25% 的空閒內存,保證新生代對象晉升到老年代的時候有足夠空間,避免老年代內存都滿了,新生代有對象要進入老年代沒有充足內存了,這裏調整的大一些好一點,10% 有點小。

-XX:InitiatingHeapOccupancyPercent=30:這個參數是說,默認值是 45%。當堆內存的使用率達到 30% 之後就會自動啓動 G1 的併發垃圾回收,開始嘗試回收一些垃圾對象。這裏調低了一些,是爲了提高了 GC 的頻率,如果積累的垃圾對象過多,一次垃圾回收耗時過長的問題。

-XX:SoftRefLRUPolicyMSPerMB=1000:這個參數默認設置爲 0,建議這個參數不要設置爲 0,避免頻繁回收一些軟引用的 Class 對象,這裏可以調整爲比如 1000

**-verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m**:這一堆參數都是控制 GC 日誌打印輸出的,確定了 gc 日誌文件的地址,要打印哪些詳細信息,然後控制每個 gc 日誌文件的大小是 30m,最多保留 5 個 gc 日誌文件。

-XX:-OmitStackTraceInFastThrow:這個參數是說,有時候 JVM 會拋棄一些異常堆棧信息,因此這個參數設置之後,就是禁用這個特性,要把完整的異常堆棧信息打印出來

-XX:+AlwaysPreTouch:這個參數的意思是我們剛開始指定 JVM 用多少內存,不會真正分配給他,會在實際需要使用的時候再分配給他

所以使用這個參數之後,就是強制讓 JVM 啓動的時候直接分配我們指定的內存,不要等到使用內存的時候再分配

-XX:MaxDirectMemorySize=10g:NIO 中的 direct buffer 限定。這裏限定了 direct buffer 最多申請多少,如果你機器內存比較大,可以適當調大這個值。

-XX:-UseLargePages -XX:-UseBiasedLocking:這兩個參數的意思是禁用大內存頁和偏向鎖。

Zookeeper 最最核心的配置

Zookeeper 也一樣,有一些配置。這裏先給大家介紹其中最核心的幾個配置,研究源碼的涉及到的時候,你起碼心理有個數,起碼知道它是幹嘛的。

其實這些配置和之前的架構原理是分不開的。

比如持久化機制中,涉及的配置主要有:

dataDir:存放 zookeeper 的數據快照目錄。

dataLogDir:每臺機器都會寫入一個本地磁盤的事務日誌的目錄。

snapCount:100000,默認是 10 萬條事務日誌,生成一次快照數據。

如果有 10w 零 300 個事務。此時 zk 重啓,他可以直接把包含 10w 個事務的快照直接加載到內存裏來。然後把 10w 之後的 300 個事務,100001~1000300 的事務,在內存裏回放一遍,就可以在內存裏恢復出來重啓之前的數據了。即使沒到達到 10 萬條事務,就重啓了,此時沒有快照,10 萬個事務以內,不需要快照,因爲直接讀取事務日誌回放到內存,就可以重建內存數據。

autopurge.purgeInterval=1

autopurge.snapRetainCount=3

定時清理數據文件的功能 每一個小時執行一次清理,每次保留 3 個數據快照文件

補充到之前的架構原理圖中,如下圖所示:

數據一致性、主從同步、選舉原理相關核心參數

**forceSync:**yes  在 commit 提交的時候一般默認會強制把寫的事務 fsync 到磁盤上去。

事務進行 commit 提交的時候,有沒有日誌丟失的風險?默認情況下,在 2PC 階段的第一個階段裏,各個機器把事務日誌寫入磁盤,此時一般進入 os cache 的,沒有直接進入物理磁盤上去。機器壞了,有可能會丟失部分 os cache 裏沒刷入磁盤的數據,所以需要 fsync 到磁盤上去。

**tickTime:**是 zookeeper 定義的時間單位,默認 2000 毫秒。其他的一些參數就會以這個 tickTime 爲基準來進行設置。

**initLimit:**默認值 10,意思是 10 * tickTime=20s。多少個 tickTime 內,允許其他 server 連接並初始化數據,如果 zooKeeper 管理的數據較大,則應相應增大這個值。

**syncLimit:**默認值 5,意思就是 5 * tickTime=10s,leader 跟 follower 之間會進行心跳,如果超過 10s 沒有心跳,leader 就把這個 follower 給踢出去了,認爲它掛了。

**leaderServers:**默認值爲 yes。leader 負責協調更新。當更新吞吐量遠高於讀取吞吐量時,可以設置爲不接受客戶端連接,以便 leader 可以專注於同步協調工作。

maxClientCnxns: 3.4.5 默認是 60,一臺機器上,我們可以創建多少個 zk 客戶端?是有限制的,默認來說 60。注意這個 zk 客戶端是 API 層面的一個對象,不是 tcp 連接。說白了,每臺服務,無論是 java 業務系統還是中間件,一般使用一個 zk 客戶端,單例的就行。不要每次使用都新創建,併發一高,會拒絕超過 60 個的其他客戶端的。也就是多少臺機器和服務一般就有多少個客戶端。這個要根據使用 zk 集羣的系統規模適當調整。

minSessionTimeout: 最小的客戶端 session 超時時間,默認值爲 2 個 tickTime,單位是毫秒 maxSessionTimeout: 最大的客戶端 session 超時時間,默認值爲 20 個 tickTime,單位是毫秒

jute.maxbuffer  znode 中最多能存儲多大數據量?默認 1048575bytes,也就是 1mb。一般來說建議,不要在 znode 中存儲的數據量過大,默認這個值一般足夠了。

peerType:  可以設置 zookeeper 的節點類型,比如 observer 的設置,設置成 observer 的節點不會參與選舉,只會同步數據,提供只讀服務

結合之前的架構原理圖,參數對應的環節,基本如下所示:

zookeeper 監控

除了通過 zkServer.sh status 查看集羣狀態,zookeeper 還可以通過四字命令進行一些監控,舉個例子:

yum install nc -y
echo conf | nc localhost 2181
[root@centos7_2 ~]# echo conf|nc localhost 2181
clientPort=2181
dataDir=/usr/local/zk/data/version-2
dataLogDir=/usr/local/zk/data/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=10
syncLimit=5
electionAlg=3
electionPort=3888
quorumPort=2888
peerType=0

就可以看到 zk 相關的寫配置,類似的還有很多 conf(查看配置)、cons(查看連接)、mntr(輸出比 stat 更詳細的)等等。你有興趣可以去查找下相關命令,甚至可以開發一個可視化的監控平臺,可以自己基於他的這些命令的輸出繪製出來一些圖形實現可視化的監控。

而且 zookeeper 是 java 的一個進程,當然可以開啓 JVM 進程遠程端口,進行 JVM 內存監控和分析。在之前的 zkServer.sh 中可以配置如下參數即可:

-Dcom.sun.management.jmxremote.port=21811
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

這裏不是運維培訓,介紹這些是爲了給大家提一個思想。你要思考,監控是如何實現的。其實監控的本質萬變不離其宗,就是暴露監控數據接口,或者通過代碼埋點記錄,之後統計數據,繪製成圖形之類的。你要抓住這一點本質思想,不要抓住一些表象手段,比如命令或者圖形工具,而是多應該思考不同的中間件實現方式的區別,爲什麼,各自好處。爲你之後自己實現監控或者開源項目時候提供思路和方向。

小結

好了,今天主要帶你熟悉了 zookeeper 基本的部署、機器配置、啓動參數、核心配置、基本監控這幾點。這些雖然偏向運維的一些知識,這些知識本身和你可能平常接觸的業務系統無關。但是你要的要明白,你要對其他中間件,包括自己的業務系統,也要有這樣的把握能力。這個是一個優秀的工程師應該具備的,要想更好地職業發展,就不要被職業劃分限制自己,你不是僅僅的熟悉業務系統就夠了的。

金句甜點

之前我和大家聊了聊,貼標籤和揭標籤、不忘初心,找回鬥志和單純。這些其實都是爲了讓你突破和改變自己。因爲之前我也說過,改變自己比改變別人更重要。你要想成長,進步這個是必須的。

你可以把這些當成心靈雞湯,也可以當成我的一些指點。但前提是你要相信這些。相信不是靠你說什麼,而是看你做什麼的。你說你相信我說的這,認可我這些。其實沒用。其實你內心不一定真的願意接受的。我給大家講個故事,有一個大師非常擅長祈雨,有一個村子乾旱了很久,村裏的人就請來了這個大師,大師祈雨問村民們:“你們相信我能祈來雨麼?”。村民們大聲地說:“相信!相信!”。大師看了看大家,說道:“既然你們相信,那爲什麼沒有一個人帶傘呢?”

其實很多時候你可能就是這樣,不在乎一些有經驗人的指點,在乎雜七雜八的人指指點點,被打擊的沒有鬥志,被貼了一頓標籤,禁錮了自己。你需要改變一下自己,慢慢接受成長記中的一些思想,慢慢的去嘗試的做。慢慢的你就會發現,你變得不一樣了。

希望各位閱讀成長記的人,不僅僅成長的是知識,更重要的是思想和觀念的成長。這也是我很重要的初心,很單純的初心。大家一起努力吧!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/tefvX_uibf3DKKoocbxVrw