Consul叢集版容器化部署與應用整合
背景
由於公司目前的主要產品使用的註冊中心是consul
,consul
需要用叢集來保證高可用,傳統的方式(Nginx/HAProxy)會有單點故障問題,為瞭解決該問題,我開始研究如何只依賴consul
做叢集的註冊的方式,經過一天的折騰,總算驗證了可以通過叢集版ConsulClient來進行叢集註冊,在部署實施過程中也遇到了一些問題,特此記錄分享,希望能對有需要的同學有幫助。
主機版叢集和docker版叢集對比
client+server轉發模式的叢集部署涉及到兩種選擇,第一種是直接主機模式部署,2個client
+3個server
,每個consul
例項部署一臺主機(適合土豪使用),此種模式的好處就是簡單暴力,而且運維相對簡單。此種模式的架構部署圖如下:
我們選擇的是另外一種經濟節約模式,docker
化部署,好處就是節約資源,劣勢就是要管理許多docker
映象,在有引入k8s這些容器管理平臺之前,後續docker
的運維會比較麻煩,這種模式的架構部署圖如下:
通過以上兩種模式的架構圖我們很清楚的就能知道主機部署模式是最簡單直接的,而docker
的模式雖然節省了資源,但是加大了複雜性,增加了運維的難度。但是這種模式應該是在目前容器化的環境下很好的選擇,原因很簡單,因為充分的利用了資源,容器的運維可以交給容器運維平臺去完成,比如k8s等。下面我們來實踐下如何進行容器化的consul
叢集部署。
環境準備
這裡準備了兩臺虛擬主機,由於是虛擬的主機,對外ip是一樣的,所以我們以埠區分。
主機A:192.168.23.222:10385 內網ip:192.168.236.3
主機B:192.168.23.222:10585 內網ip:192.168.236.5
複製程式碼
部署配置
步驟一:主機安裝Docker環境(以Centos為例)
yum install docker
複製程式碼
步驟二:拉取Consul映象進行部署
docker pull consul
複製程式碼
步驟三:給主機Docker分配ip段,防止多主機ip重複
- 在主機A編輯
docker
的/etc/docker/daemon.json
檔案,新增下面的內容
"bip": "172.17.1.252/24"
複製程式碼
- 在主機B編輯
docker
的/etc/docker/daemon.json
檔案,新增下面的內容
"bip": "172.17.2.252/24"
複製程式碼
這裡的配置是給主機的docker
例項分配ip,因為後續docker
會進行跨主機註冊,如果預設註冊的話,docker
是用的主機內網,從而導致ip重複,所以這裡手動進行ip分配,當然上述的ip配置你可以自定義。
步驟四:在主機A部署Consul
Node1:
docker run -d --name=node_31 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 11300:8300 \
-p 11301:8301 \
-p 11301:8301/udp \
-p 11302:8302/udp \
-p 11302:8302 \
-p 11400:8400 \
-p 11500:8500 \
-p 11600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node31 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
複製程式碼
這裡重點說明幾個引數:
--name
:是docker
容器的名字,每個容器例項不一樣
-node
:是consul
節點的名字,每個節點不一樣
-bootstrap-expect
:是啟動叢集期望至少多少個節點,這裡設定是3個。
-data-dir
:是consul
的資料中心的目錄,必須給與consul
讀寫許可權,否則啟動會報錯。
啟動成功後,執行命令檢視consul
的節點。
docker exec -t node_31 consul members
複製程式碼
顯示結果如下:
Node Address Status Type Build Protocol DC Segment
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
複製程式碼
這說明第一個節點正常啟動了,接下來正常啟動主機A剩下的節點。
Node2:
docker run -d --name=node_32 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 9300:8300 \
-p 9301:8301 \
-p 9301:8301/udp \
-p 9302:8302/udp \
-p 9302:8302 \
-p 9400:8400 \
-p 9500:8500 \
-p 9600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node32 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
複製程式碼
Node3:
docker run -d --name=node_33 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 10300:8300 \
-p 10301:8301 \
-p 10301:8301/udp \
-p 10302:8302/udp \
-p 10302:8302 \
-p 10400:8400 \
-p 10500:8500 \
-p 10600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node33 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
複製程式碼
三個節點啟動完畢後,執行命令,檢視節點的狀態:
docker exec -t node_31 consul operator raft list-peers
複製程式碼
結果如下:
Node ID Address State Voter RaftProtocol
node32 ee186aef-5f8a-976b-2a33-b20bf79e7da9 172.17.1.2:8300 follower true 3
node33 d86b6b92-19e6-bb00-9437-f988b6dac4b2 172.17.1.3:8300 follower true 3
node31 0ab60093-bed5-be77-f551-6051da7fe790 172.17.1.1:8300 leader true 3
複製程式碼
這裡已經顯示,三個server
的節點已經完成了叢集部署,並且選舉了node_31
作為主節點。最後給該主機叢集部署一個client就大功告成了。
Node4(client節點)
docker run -d --name=node_34 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' \
-p 8300:8300 \
-p 8301:8301 \
-p 8301:8301/udp \
-p 8302:8302/udp \
-p 8302:8302 \
-p 8400:8400 \
-p 8500:8500 \
-p 8600:8600 \
consul agent -retry-join=172.17.1.1 \
-node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node34 -client 0.0.0.0 -ui
複製程式碼
執行docker exec -t node_31 consul members
命令,結果如下:
Node Address Status Type Build Protocol DC Segment
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
複製程式碼
這裡說明,主機A的consul
節點全部啟動完成,並且完成了叢集部署,可以說這就是一個單主機版的consul
叢集,那麼接下來我們要做的就是把主機B的consul
加入到主機A的叢集中即可。
步驟五:在主機B部署Consul
Node5
docker run -d --name=node_51 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 11300:8300 \
-p 11301:8301 \
-p 11301:8301/udp \
-p 11302:8302/udp \
-p 11302:8302 \
-p 11400:8400 \
-p 11500:8500 \
-p 11600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node_51 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
複製程式碼
Node6
docker run -d --name=node_52 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 9300:8300 \
-p 9301:8301 \
-p 9301:8301/udp \
-p 9302:8302/udp \
-p 9302:8302 \
-p 9400:8400 \
-p 9500:8500 \
-p 9600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node_52 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
複製程式碼
Node7
docker run -d --name=node_53 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' \
-p 10300:8300 \
-p 10301:8301 \
-p 10301:8301/udp \
-p 10302:8302/udp \
-p 10302:8302 \
-p 10400:8400 \
-p 10500:8500 \
-p 10600:8600 \
consul agent -server -join=172.17.1.1 -bootstrap-expect=3 -node=node_53 \
-data-dir=/consul/data/ -client 0.0.0.0 -ui
複製程式碼
主機B的三個server節點部署完成後,我們執行命令docker exec -t node_51 consul members
檢視下叢集節點狀態
Node Address Status Type Build Protocol DC Segment
node_51 172.17.2.1:8301 alive server 1.6.2 2 dc1 <all>
複製程式碼
為什麼只有node_51
這個單獨的節點呢?是不是節點的問題?我們在主機B中查詢同樣查詢一下,結果如下:
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
複製程式碼
主機A的節點只有他們自己機器的節點,主機B中的節點全部未註冊過來,這是為什麼呢?原因就是consul
繫結的ip是容器的內網ip,主機內部通訊是可以的,跨主機通訊是無法通過內網地址進行通訊的,那麼我們怎麼做呢?我們通過路由規則進行轉發即可,把主機A請求主機B容器的內網地址轉發到主機B即可,這裡就體現出我們開始給容器分配ip的作用了。
我們在主機A執行如下命令:
route add -net 172.17.2.0 netmask 255.255.255.0 gw 192.168.236.5
複製程式碼
這條命令的意思是,新增一個路由規則172.17.2.1~172.17.2.254
範圍的ip請求,全部轉發到192.168.236.5
地址下,也就是我們的主機B。
同理主機B也執行如下命令:
route add -net 172.17.1.0 netmask 255.255.255.0 gw 192.168.236.3
複製程式碼
新增完成後,在執行docker exec -t node_53 consul members
命令:
Node Address Status Type Build Protocol DC Segment
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node_51 172.17.2.1:8301 alive server 1.6.2 2 dc1 <all>
node_52 172.17.2.2:8301 alive server 1.6.2 2 dc1 <all>
node_53 172.17.2.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
複製程式碼
叢集加入就成功了,這就完成了跨主機的docker容器加入。
最後給主機B部署一個client
Node8(client節點)
docker run -d --name=node_54 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' \
-p 8300:8300 \
-p 8301:8301 \
-p 8301:8301/udp \
-p 8302:8302/udp \
-p 8302:8302 \
-p 8400:8400 \
-p 8500:8500 \
-p 8600:8600 \
consul agent -retry-join=172.17.1.1 \
-node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node54 -client 0.0.0.0 -ui
複製程式碼
最後的叢集節點全部加入成功了,結果如下:
node31 172.17.1.1:8301 alive server 1.6.2 2 dc1 <all>
node32 172.17.1.2:8301 alive server 1.6.2 2 dc1 <all>
node33 172.17.1.3:8301 alive server 1.6.2 2 dc1 <all>
node_51 172.17.2.1:8301 alive server 1.6.2 2 dc1 <all>
node_52 172.17.2.2:8301 alive server 1.6.2 2 dc1 <all>
node_53 172.17.2.3:8301 alive server 1.6.2 2 dc1 <all>
node34 172.17.1.4:8301 alive client 1.6.2 2 dc1 <default>
node54 172.17.2.4:8301 alive client 1.6.2 2 dc1 <default>
複製程式碼
執行節點狀態命令docker exec -t node_31 consul operator raft list-peers
:
node32 ee186aef-5f8a-976b-2a33-b20bf79e7da9 172.17.1.2:8300 follower true 3
node33 d86b6b92-19e6-bb00-9437-f988b6dac4b2 172.17.1.3:8300 follower true 3
node31 0ab60093-bed5-be77-f551-6051da7fe790 172.17.1.1:8300 leader true 3
node_51 cfac3b67-fb47-8726-fa31-158516467792 172.17.2.1:8300 follower true 3
node_53 31679abe-923f-0eb7-9709-1ed09980ca9d 172.17.2.3:8300 follower true 3
node_52 207eeb6d-57f2-c65f-0be6-079c402f6afe 172.17.2.2:8300 follower true 3
複製程式碼
這樣一個包含6個server
+2個client
的consul
容器化叢集就部署完成了,我們檢視consul
的web
面板如下:
應用整合
叢集版本的consul
我們就部署好了,那麼我們如何與應用整合呢?我們只要整合叢集版本的consul
註冊客戶端就行了。
首先加入依賴
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>spring-cloud-starter-consul-cluster</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
複製程式碼
第二步在bootstrap.yml|properties
中指定spring.cloud.consul.host
為多節點,如下所示:
spring.cloud.consul.host=192.168.23.222:10385,192.168.23.222:10585
複製程式碼
如果想輸出註冊的相關日誌的話也可以在logback上加上日誌配置
<logger name="org.springframework.cloud.consul" level="TRACE"/>
複製程式碼
這樣配置完成後啟動成功就能看到我們的應用註冊成功了,下圖是我測試的註冊成功的效果:
這裡顯示我的應用節點分別都註冊到了叢集的2個client
上面,通過client
的代理轉發請求到健康的server
,從而實現了consul
的高可用。
總結
這篇文章沒有研究什麼技術乾貨,純粹是工作經驗分享,主要講了consul
叢集部署的方式,傳統模式可以通過HAProxy
來完成叢集的部署,但是這種方式的弊端很明顯,通過虛擬ip還是可能會指向故障的節點,所以我們用consul
的client
+server
模式的叢集部署,通過docker
化來充分利用了機器的資源,只需要2臺機器就能完成叢集的高可用效果。