Docker實戰之Zookeeper叢集
1. 概述
這裡是 Docker 實戰系列第四篇。主要介紹分散式系統中的元老級元件 Zookeeper。
ZooKeeper 是一個開源的分散式協調服務,是 Hadoop,HBase 和其他分散式框架使用的有組織服務的標準。
分散式應用程式可以基於 ZooKeeper 實現諸如資料釋出/訂閱、負載均衡、命名服務、分散式協調/通知、叢集管理、Master 選舉、分散式鎖和分散式佇列等功能。
讀過 Docker 實戰之 Consul 叢集 的小夥伴應該有印象,裡邊有一張一致性演算法的對比圖。所有的分散式系統都面臨著 CAP 理論的抉擇,都需要一致性演算法的保障。這裡先放上一個簡單的總結,用於大家借鑑那些頂級開源軟體在分散式上的思路。
分散式元件 | 演算法/協議 | 服務 |
---|---|---|
Redis Cluster | Gossip | master 提供讀寫,slave 只備份 |
Zookeeper | ZAB | Leader 提供讀寫,Follower 只讀,遇到寫請求轉發給 Leader |
Kafka | ZK 臨時節點 | 只有 leader 提供讀寫服務 |
2. 應用場景
大致來說,zookeeper 的使用場景如下:
- 分散式協調
- 分散式鎖
- 元資料/配置資訊管理
- HA 高可用性
- 釋出/訂閱
- 負載均衡
- Master 選舉
這裡引用中華石杉老師的例子
2.1 分散式協調
這個其實是 zookeeper 很經典的一個用法,簡單來說,就好比,你 A 系統傳送個請求到 mq,然後 B 系統訊息消費之後處理了。那 A 系統如何知道 B 系統的處理結果?用 zookeeper 就可以實現分散式系統之間的協調工作。A 系統傳送請求之後可以在 zookeeper 上對某個節點的值註冊個監聽器,一旦 B 系統處理完了就修改 zookeeper 那個節點的值,A 系統立馬就可以收到通知,完美解決。
2.2 分散式鎖
舉個栗子。對某一個數據連續發出兩個修改操作,兩臺機器同時收到了請求,但是隻能一臺機器先執行完另外一個機器再執行。那麼此時就可以使用 zookeeper 分散式鎖,一個機器接收到了請求之後先獲取 zookeeper 上的一把分散式鎖,就是可以去建立一個 znode,接著執行操作;然後另外一個機器也嘗試去建立那個 znode,結果發現自己建立不了,因為被別人建立了,那隻能等著,等第一個機器執行完了自己再執行。
2.3 元資料/配置資訊管理
zookeeper 可以用作很多系統的配置資訊的管理,比如 kafka、storm 等等很多分散式系統都會選用 zookeeper 來做一些元資料、配置資訊的管理,包括 dubbo 註冊中心不也支援 zookeeper 麼?
2.4 HA 高可用性
這個應該是很常見的,比如 hadoop、hdfs、yarn 等很多大資料系統,都選擇基於 zookeeper 來開發 HA 高可用機制,就是一個重要程序一般會做主備兩個,主程序掛了立馬通過 zookeeper 感知到切換到備用程序。
3. Docker 配置
docker-compose-zookeeper-cluster.yml
version: '3.7'
networks:
docker_net:
external: true
services:
zoo1:
image: zookeeper
restart: unless-stopped
hostname: zoo1
container_name: zoo1
ports:
- 2182:2181
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181
volumes:
- ./zookeeper/zoo1/data:/data
- ./zookeeper/zoo1/datalog:/datalog
networks:
- docker_net
zoo2:
image: zookeeper
restart: unless-stopped
hostname: zoo2
container_name: zoo2
ports:
- 2183:2181
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=0.0.0.0:2888:3888;2181 server.3=zoo3:2888:3888;2181
volumes:
- ./zookeeper/zoo2/data:/data
- ./zookeeper/zoo2/datalog:/datalog
networks:
- docker_net
zoo3:
image: zookeeper
restart: unless-stopped
hostname: zoo3
container_name: zoo3
ports:
- 2184:2181
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=0.0.0.0:2888:3888;2181
volumes:
- ./zookeeper/zoo3/data:/data
- ./zookeeper/zoo3/datalog:/datalog
networks:
- docker_net
啟動叢集
docker-compose -f docker-compose-zookeeper-cluster.yml up -d
4. 叢集初認識
在 ZAB 演算法中,存在 Leader、Follower、Observer 三種角色,現在我們就來認識下它們。
- 檢視 zoo1 角色
➜ docker docker exec -it zoo1 /bin/sh
# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
由上結果可知,zoo1 是 follower
- 檢視 zoo2 角色
➜ docker docker exec -it zoo2 /bin/sh
# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
由上結果可知,zoo2 是 follower
- 檢視 zoo3 角色
➜ docker docker exec -it zoo3 /bin/sh
# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
由上結果可知,zoo3 是 leader。負責叢集的讀寫。
- 檢視 zoo3 選舉資料
➜ docker echo srvr | nc localhost 2184
Zookeeper version: 3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT
Latency min/avg/max: 0/0/0
Received: 2
Sent: 1
Connections: 1
Outstanding: 0
Zxid: 0x100000000
Mode: leader
Node count: 5
Proposal sizes last/min/max: -1/-1/-1
- 檢視對映資料
如果實踐了上述操作的小夥伴一定會發現,對映路徑下的資料夾多了好多東西,感興趣的小夥伴可以開啟看一下,瞭解下 ZAB 的選舉演算法(沒錯,裡邊記錄的就是選舉相關的資料)。
➜ zookeeper cd zoo1
➜ zoo1 tree
.
├── data
│ ├── myid
│ └── version-2
│ ├── acceptedEpoch
│ ├── currentEpoch
│ └── snapshot.0
└── datalog
└── version-2
注意:留意 currentEpoch 中的數值
5. 選舉演練
5.1 模擬 Leader 掉線
➜ zoo1 docker stop zoo3
zoo3
檢視此時的選舉結果(操作同檢視角色操作步驟)。可以看到 Zookeeper 叢集重新選舉結果: zoo2 被選為 leader
5.2 zoo3 節點重新上線
➜ zoo1 docker start zoo3
zoo3
檢視 zoo3 角色,發現 zoo3 自動作為 follower 加入叢集。
注意:檢視 currentEpoch 中的數值,儲存值為 2,代表經過了 2 次選舉。第一次為剛啟動時觸發選舉,第二次為 leader 宕機後重新選舉
6. 常用操作
6.1 檢視檔案目錄
筆者本地有安裝的 Zookeeper 環境,所以這裡用本地的 zkCli 進行測試。
zkCli -server localhost:2182,localhost:2183,localhost:2184
Connecting to localhost:2182,localhost:2183,localhost:2184
Welcome to ZooKeeper!
JLine support is enabled
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 0] ls /
[zookeeper]
6.2 建立順序節點
順序節點保證 znode 路徑將是唯一的。
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 1] create -s /zk-test 123
Created /zk-test0000000000
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 2] ls /
[zk-test0000000000, zookeeper]
6.3 建立臨時節點
當會話過期或客戶端斷開連線時,臨時節點將被自動刪除
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 3] create -e /zk-temp 123
Created /zk-temp
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 4] ls /
[zk-test0000000000, zookeeper, zk-temp]
臨時節點在客戶端會話結束後就會自動刪除,下面使用 quit 命令列退出客戶端,再次連線後即可驗證。
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 5] quit
Quitting...
➜ docker zkCli -server localhost:2182,localhost:2183,localhost:2184
Connecting to localhost:2182,localhost:2183,localhost:2184
Welcome to ZooKeeper!
JLine support is enabled
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 0] ls /
[zk-test0000000000, zookeeper]
6.4 建立永久節點
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 1] create /zk-permanent 123
Created /zk-permanent
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 2] ls /
[zk-permanent, zk-test0000000000, zookeeper]
6.5 讀取節點
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 3] get /
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x400000008
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 3
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 4] ls2 /
[zk-permanent, zk-test0000000000, zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x400000008
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 3
使用 ls2 命令來檢視某個目錄包含的所有檔案,與 ls 不同的是它檢視到 time、version 等資訊
6.6 更新節點
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 5] set /zk-permanent 456
cZxid = 0x400000008
ctime = Tue Mar 03 21:35:20 CST 2020
mZxid = 0x400000009
mtime = Tue Mar 03 21:40:11 CST 2020
pZxid = 0x400000008
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
6.7 檢查狀態
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 6] stat /zk-permanent
cZxid = 0x400000008
ctime = Tue Mar 03 21:35:20 CST 2020
mZxid = 0x400000009
mtime = Tue Mar 03 21:40:11 CST 2020
pZxid = 0x400000008
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
6.8 刪除節點
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 7] rmr /zk-permanent
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 8] ls /
[zk-test0000000000, zookeeper]
7. AD
公眾號【當我遇上你】, 每天帶給你不一樣的內容