Zookeeper是一個分佈式協調框架,有不錯的性能,也通過許多公司的驗證,因此在不少場景都有使用。你們通常用Zookeeper來實現服務發現(相似DNS),配置管理,分佈式鎖,leader選舉等。在這些場景中,Zookeeper成爲了一個被依賴的核心組件,Zookeeper的穩定性是須要特別關注的。 去哪兒網也在不少場景依賴Zookeeper,因此咱們也一直在摸索怎麼更好的運維穩定的Zookeeper集羣。在過去的幾年咱們也踩過一些坑,也由於Zookeeper致使了故障。如今將咱們運維Zookeeper集羣的一些經驗分享,也歡迎你們提供更好的建議。 那麼在打算運維一套Zookeeper集羣以前,咱們先了解一些Zookeeper的基本原理。 集羣裏分三種角色: Leader, Follower和Observer。Leader和Follower參與投票,Observer只會『聽』投票的結果,不參與投票。 投票集羣裏的節點數要求是奇數 一個集羣容忍掛掉的節點數的等式爲 N = 2F + 1,N爲投票集羣節點數,F爲能同時容忍失敗節點數。好比一個三節點集羣,能夠掛掉一個節點,5節點集羣能夠掛掉兩個... 一個寫操做須要半數以上的節點ack,因此集羣節點數越多,整個集羣能夠抗掛點的節點數越多(越可靠),可是吞吐量越差。 Zookeeper裏全部節點以及節點的數據都會放在內存裏,造成一棵樹的數據結構。而且定時的dump snapshot到磁盤。 Zookeeper的Client與Zookeeper之間維持的是長鏈接,而且保持心跳,Client會與Zookeeper之間協商出一個Session超時時間出來(其實就是Zookeeper Server裏配置了最小值,最大值,若是client的值在這兩個值之間則採用client的,小於最小值就是最小值,大於最大值就用最大值),若是在Session超時時間內沒有收到心跳,則該Session過時。 Client能夠watch Zookeeper那個樹形數據結構裏的某個節點或數據,當有變化的時候會獲得通知。 有了這些瞭解後,那麼咱們內心其實有底了。 1. 最小生產集羣 要確保Zookeeper可以穩定運行,那麼就須要確保投票可以正常進行,最好不要掛一個節點整個就不work了,因此咱們通常要求最少5個節點部署。 2. 網絡 除了節點外,咱們還要看不能一臺物理機器,一個機櫃或一個交換機掛掉而後影響了整個集羣,因此節點的網絡結構也要考慮。這個可能就比不少應用服務器的要求更加嚴格。 3. 分Group,保護核心Group 要確保Zookeeper整個集羣可靠運行,就是要確保投票集羣可靠。那在咱們這裏,將一個Zookeeper集羣劃分爲多個小的Group,咱們稱Leader+Follower爲核心Group,核心Group咱們通常是不向外提供服務的,而後咱們會根據不一樣的業務再加一些Observer,好比一個Zookeeper集羣爲服務發現,消息,定時任務三個不一樣的組件提供服務,那麼咱們爲創建三個Observer Group,分別給這三個組件使用,而Client只會鏈接分配給它的Observer Group,不去鏈接核心Group。這樣核心Group就不會給Client提供長鏈接服務,也不負責長鏈接的心跳,這大大的減輕了核心Group的壓力,由於在實際環境中,一個Zookeeper集羣要爲上萬臺機器提供服務,維持長鏈接和心跳仍是要消耗必定的資源的。由於Observer是不參與投票的因此加Observer並不會下降總體的吞吐量,並且Observer掛掉不會影響整個集羣的健康。 可是這裏要注意的是,分Observer Group只能解決部分問題,由於畢竟全部的寫入仍是要交給核心Group來處理的,因此對於寫入量特別大的應用來講,仍是須要進行集羣上的隔離,好比Storm和Kafka就對Zookeeper壓力比較大,你就不能將其與服務發現的集羣放在一塊兒。 4. 內存 由於Zookeeper將全部數據都放在內存裏,因此對JVM以及機器的內存也要預先計劃,若是出現Swap那將嚴重的影響Zookeeper集羣的性能,因此我通常不怎麼推薦將Zookeeper用做通用的配置管理服務。由於通常配置數據仍是挺大的,這些所有放在內存裏不太可控。 5. 日誌清理 由於Zookeeper要頻繁的寫txlog(Zookeeper寫的一種順序日誌)以及按期dump內存snapshot到磁盤,這樣磁盤佔用就愈來愈大,因此Zookeeper提供了清理這些文件的機制,可是這種機制並不太合理,它只能設置間隔多久清理,而不能設置具體的時間段。那麼就有可能碰到高峯期間清理,因此建議將其關閉:autopurge.purgeInterval=0。而後使用crontab等機制,在業務低谷的時候清理。 6. 日誌,jvm配置 從官網直接下載的包若是直接啓動運行是很糟糕的,這個包默認的配置日誌是不會輪轉的,並且是直接輸出到終端。咱們最開始並不瞭解這點,而後運行一段時間後發現生成一個龐大的zookeeper.out的日誌文件。除此以外,這個默認配置尚未設置任何jvm相關的參數(因此堆大小是個默認值),這也是不可取的。那麼有的同窗說那我去修改Zookeeper的啓動腳本吧。最好不要這樣作,Zookeeper會加載conf文件夾下一個名爲zookeeper-env.sh的腳本,因此你能夠將一些定製化的配置寫在這裏,而不是直接去修改Zookeeper自帶的腳本。 #!/usr/bin/env bash JAVA_HOME= #java home ZOO_LOG_DIR= #日誌文件放置的路徑 ZOO_LOG4J_PROP="INFO,ROLLINGFILE" #設置日誌輪轉 JVMFLAGS="jvm的一些設置,好比堆大小,開gc log等" 7. 地址 在實際環境中,咱們可能由於各類緣由好比機器過保,硬件故障等須要遷移Zookeeper集羣,因此Zookeeper的地址是一個很頭痛的事情。這個地址有兩方面,第一個是提供給Client的地址,建議這個地址經過配置的方式下發,不要讓使用方直接使用,這一點咱們前期作的很差。另一個是集羣配置裏,集羣之間須要通信,也須要地址。咱們的處理方式是設置hosts: 192.168.1.20 zk1 192.168.1.21 zk2 192.168.1.22 zk3 在配置裏: server.1=zk1:2081:3801 server.2=zk2:2801:3801 server.3=zk3:2801:3801 這樣在須要遷移的時候,咱們停老的節點,起新的節點只須要修改hosts映射就能夠了。好比如今server.3須要遷移,那咱們在hosts裏將zk3映射到新的ip地址。可是對於java有一個問題是,java默認會永久緩存DNS cache,即便你將zk3映射到別的ip,若是並不重啓server.1, server.2,它是不會解析到新的ip的,這個須要修改$JAVA_HOME/jre/lib/security/java.security文件裏的networkaddress.cache.ttl=60,將其修改成一個比較小的數。 對於這個遷移的問題,咱們還遇到一個比較尷尬的狀況,在最後的坑裏會有說起。 8. 日誌位置 Zookeeper主要產生三種IO: txlog(每一個寫操做,包括新Session都會記錄一條log),Snapshot以及運行的應用日誌。通常建議將這三個IO分散到三個不一樣的盤上。不過咱們卻是一直沒有這麼實驗過,咱們的Zookeeper也是運行在虛擬機(通常認爲虛擬機IO較差)上。 9. 監控 咱們對Zookeeper作了這樣一些監控: a. 是否可寫。 就是一個定時任務定時的去建立節點,刪節點等操做。這裏要注意的是Zookeeper是一個集羣,咱們監控的時候我仍是但願對單個節點作監控,因此這些操做的時候不要鏈接整個集羣,而是直接去鏈接單個節點。 b. 監控watcher數和鏈接數 特別是這兩個數據有較大波動的時候,能夠發現使用方是否有誤用的狀況 c. 網絡流量以及client ip 這個會記錄到監控系統裏,這樣很快能發現『害羣之馬』 10. 一些使用建議 a. 不要強依賴Zookeeper,也就是Zookeeper出現問題業務已然能夠正常運行。Zookeeper是一個分佈式的協調框架,主要作的事情就是分佈式環境的一致性。這是一個很是苛刻的事情,因此它的穩定性受不少方面的影響。好比咱們經常使用Zookeeper作服務發現,那麼服務發現實際上是不須要嚴格的一致性的,咱們能夠緩存server list,當Zookeeper出現問題的時候已然能夠正常工做,在這方面etcd要作的更好一些,Zookeeper若是出現分區,少數派是不能提供任何服務的,讀都不能夠,而etcd的少數派仍然能夠提供讀服務,這在服務發現的時候仍是不錯的。 b. 不要將不少東西塞到Zookeeper裏,這個上面已經提到過。 c. 不要使用Zookeeper作細粒度鎖,好比不少業務在訂單這個粒度上使用Zookeeper作分佈式鎖,這會頻繁的和Zookeeper交互,對Zookeeper壓力較大,並且一旦出現問題影響面廣。可是可使用粗粒度的鎖(其實leader選舉也是一種鎖)。 d. 不建議作通用配置的第二個理由是,通用配置要提供給特別多特別多系統使用,並且一些公共配置甚至全部系統都會使用,一旦這樣的配置發生變動,Zookeeper會廣播給全部的watcher,而後全部Client都來拉取,瞬間形成很是大的網絡流量,引發所謂的『驚羣』。而本身實現通用配置系統的時候,通常會對這種配置採起排隊或分批通知的方式。 11. 一些坑 a. zookeeper client 3.4.5 ping時間間隔算法有問題,在遇到網絡抖動等緣由致使一次ping失敗後會斷開鏈接。3.4.6解決了這個問題 Bug1751。 b. zookeeper client若是由於網絡抖動斷開了鏈接,若是後來又重連上了,zookeeper client會自動的將以前訂閱的watcher等又所有訂閱一遍,而Zookeeper默認對單個數據包的大小有個1M的限制,這每每就會超限,最後致使一直不斷地的重試。這個問題在較新的版本獲得了修復。Bug706 c. 拋出UnresolvedAddressException異常致使Zookeeper選舉線程退出,整個集羣沒法再選舉,處於崩潰的邊緣。這個問題是,某次OPS遷移機器,將老的機器回收了,因此老的機器的IP和機器名不復存在,最後拋出UnresolvedAddressException這個異常,而Zookeeper的選舉線程(QuorumCnxManager類裏的Listener)只捕獲了IOException,致使該線程退出,該線程一旦退出只要如今的leader出現問題,須要從新選舉,則不會選出新的leader來,整個集羣就會崩潰。Bug2319(PS,這個bug是我report的) |