前言
近年微服務架構在網際網路應用領域中愈來愈火,引入微服務主要解決了單體應用多個模組的緊耦合、無法擴充套件和運維困難等問題。微服務架構就是按照功能粒度將業務模組進行垂直拆分,對單體應用本身進行服務化和元件化,每個元件單獨部署為小應用(從DB
到UI
)。微服務與微服務之間通過Service API
進行互動,同時為了支援水平擴充套件、效能提升和服務可用性,單個服務允許同時部署一個或者多個服務例項。在執行時,每個例項通常是一個雲虛擬機器或者Docker
容器。
微服務系統內部多個服務的例項之間如何通訊?如何感知到彼此的存在和銷燬?生產者服務如何知道消費者服務的地址?如何實現服務與註冊中心的解耦?這就需要一個第三方的服務註冊中心,提供對生產者服務節點的註冊管理和消費者服務節點的發現管理。
正文
1. 服務發現與註冊
1.1. 具體流程
- 服務註冊中心:作為整個架構中的核心,要支援分散式、持久化儲存,註冊資訊變動實時通知消費者。
- 服務提供者: 服務以
docker
容器化方式部署(實現服務埠的動態生成),可以通過docker-compose
的方式來管理。通過Registrator
檢測到docker
程式資訊以完成服務的自動註冊。 - 服務消費者:要使用服務提供者提供的服務,和服務提供者往往是動態相互轉位置的。
一個較為完整的服務註冊與發現流程如下:
- 註冊服務:服務提供者到註冊中心註冊;
- 訂閱服務:服務消費者到註冊中心訂閱服務資訊,對其進行監聽;
- 快取服務列表:本地快取服務列表,減少與註冊中心的網路通訊;
- 呼叫服務:先查詢本地快取,找不到再去註冊中心拉取服務地址,然後傳送服務請求;
- 變更通知:服務節點變動時 (新增、刪除等),註冊中心將通知監聽節點,更新服務資訊。
1.2. 相關元件
一個服務發現系統主要由三部分組成:
- 註冊器(registrator):根據服務執行狀態,註冊/登出服務。主要要解決的問題是,何時發起註冊/登出動作。
- 登錄檔(registry):儲存服務資訊。常見的解決方案有zookeeper、etcd、cousul等。
- 發現機制(discovery):從登錄檔讀取服務資訊,給使用者封裝訪問介面。
1.3. 第三方實現
對於第三方的服務註冊與發現的實現,現有的工具主要有以下三種:
- zookeeper:一個高效能、分散式應用程式協調服務,用於名稱服務、分散式鎖定、共享資源同步和分散式配置管理。
- Etcd:一個採用HTTP協議的健/值對儲存系統,主要用於共享配置和服務發現,提供的功能相對Zookeeper和Consul相對簡單。
- Consul:一個分散式高可用的服務發現和配置共享的軟體,支援服務發現與註冊、多資料中心、健康檢查和分散式鍵/值儲存。
簡單對比:
與Zookeeper和etcd不一樣,Consul內嵌實現了服務發現系統,不需要構建自己的系統或使用第三方系統,客戶只需要註冊服務,並通過DNS或HTTP介面執行服務發現。
2. Consul和Registrator
2.1. Consul簡介
Consul是什麼
Consul
是一種分散式的、高可用、支援水平擴充套件的的服務註冊與發現工具。它大致包括以下特性:
- 服務發現:
Consul
通過DNS
或者HTTP
介面使服務註冊和服務發現變的很容易。一些外部服務,例如saas
提供的也可以一樣註冊; - 健康檢查:健康檢測使
consul
可以快速的告警在叢集中的操作。和服務發現的整合,可以防止服務轉發到故障的服務上面; - 鍵/值儲存:一個用來儲存動態配置的系統。提供簡單的
HTTP
介面,可以在任何地方操作; - 多資料中心:支援多資料中心以避免單點故障,內外網的服務採用不同的埠進行監聽。而其部署則需要考慮網路延遲, 分片等情況等。
zookeeper
和etcd
均不提供多資料中心功能的支援; - 一致性演算法:採用
Raft
一致性協議演算法,比Paxos
演算法好用。 使用GOSSIP
協議管理成員和廣播訊息, 並且支援ACL
訪問控制; - 服務管理Dashboard:提供一個
Web UI
的服務註冊於健康狀態監控的管理頁面。
Consul的幾個概念
下圖是Consul
官方文件提供的架構設計圖:
圖中包含兩個Consul
資料中心,每個資料中心都是一個consul
的叢集。在資料中心1中,可以看出consul
的叢集是由N
個SERVER
,加上M
個CLIENT
組成的。而不管是SERVER
還是CLIENT
,都是consul
叢集的一個節點。所有的服務都可以註冊到這些節點上,正是通過這些節點實現服務註冊資訊的共享。除了這兩個,還有一些小細節 一一 簡單介紹。
- CLIENT
CLIENT
表示consul
的client
模式,就是客戶端模式。是consul
節點的一種模式,這種模式下,所有註冊到當前節點的服務會被轉發到SERVER
節點,本身是不持久化這些資訊。
- SERVER
SERVER
表示consul
的server
模式,表明這個consul
是個server
節點。這種模式下,功能和CLIENT
都一樣,唯一不同的是,它會把所有的資訊持久化的本地。這樣遇到故障,資訊是可以被保留的。
- SERVER-LEADER
中間那個SERVER
下面有LEADER
的描述,表明這個SERVER
節點是它們的老大。和其它SERVER
不一樣的一點是,它需要負責同步註冊資訊給其它的SERVER
,同時也要負責各個節點的健康監測。
- 其它資訊
其它資訊包括各個節點之間的通訊方式,還有一些協議資訊、演算法。它們是用於保證節點之間的資料同步、實時性要求等等一系列叢集問題的解決。這些有興趣的自己看看官方文件。
2.2. Registrator簡介
什麼是Registrator
Registrator
是一個獨立於服務登錄檔的自動服務註冊/登出元件,一般以Docker container
的方式進行部署。Registrator
會自動偵測它所在的宿主機上的所有Docker
容器狀態(啟用/銷燬),並根據容器狀態到對應的服務註冊列表註冊/登出服務。
事實上,Registrator
通過讀取同一臺宿主機的其他容器Container
的環境變數進行服務註冊、健康檢查定義等操作。
Registrator
支援可插拔式的服務登錄檔配置,目前支援包括Consul
, etcd
和SkyDNS 2
三種註冊工具。
2.3. Docker安裝Consul叢集
2.3.1. 叢集節點規劃
我本地的使用的是Ubuntu16.04
的虛擬機器:
容器名稱 | 容器IP地址 | 對映埠號 | 宿主機IP地址 | 服務執行模式 |
---|---|---|---|---|
node1 | 172.17.0.2 | 8500 -> 8500 | 192.168.127.128 | Server Master |
node2 | 172.17.0.3 | 9500 -> 8500 | 192.168.127.128 | Server |
node3 | 172.17.0.4 | 10500 -> 8500 | 192.168.127.128 | Server |
node4 | 172.17.0.5 | 11500 -> 8500 | 192.168.127.128 | Client |
2.3.2. Consul叢集安裝
Consul
的配置引數資訊說明:
引數列表 | 引數的含義和使用場景說明 |
---|---|
advertise | 通知展現地址用來改變我們給叢集中的其他節點展現的地址,一般情況下-bind地址就是展現地址 |
bootstrap | 用來控制一個server是否在bootstrap模式,在一個datacenter中只能有一個server處於bootstrap模式,當一個server處於bootstrap模式時,可以自己選舉為raft leader |
bootstrap-expect | 在一個datacenter中期望提供的server節點數目,當該值提供的時候,consul一直等到達到指定sever數目的時候才會引導整個叢集,該標記不能和bootstrap共用 |
bind | 該地址用來在叢集內部的通訊IP地址,叢集內的所有節點到地址都必須是可達的,預設是0.0.0.0 |
client | consul繫結在哪個client地址上,這個地址提供HTTP、DNS、RPC等服務,預設是127.0.0.1 |
config-file | 明確的指定要載入哪個配置檔案 |
config-dir | 配置檔案目錄,裡面所有以.json結尾的檔案都會被載入 |
data-dir | 提供一個目錄用來存放agent的狀態,所有的agent允許都需要該目錄,該目錄必須是穩定的,系統重啟後都繼續存在 |
dc | 該標記控制agent允許的datacenter的名稱,預設是dc1 |
encrypt | 指定secret key,使consul在通訊時進行加密,key可以通過consul keygen生成,同一個叢集中的節點必須使用相同的key |
join | 加入一個已經啟動的agent的ip地址,可以多次指定多個agent的地址。如果consul不能加入任何指定的地址中,則agent會啟動失敗,預設agent啟動時不會加入任何節點 |
retry-interval | 兩次join之間的時間間隔,預設是30s |
retry-max | 嘗試重複join的次數,預設是0,也就是無限次嘗試 |
log-level | consul agent啟動後顯示的日誌資訊級別。預設是info,可選:trace、debug、info、warn、err |
node | 節點在叢集中的名稱,在一個叢集中必須是唯一的,預設是該節點的主機名 |
protocol | consul使用的協議版本 |
rejoin | 使consul忽略先前的離開,在再次啟動後仍舊嘗試加入叢集中 |
server | 定義agent執行在server模式,每個叢集至少有一個server,建議每個叢集的server不要超過5個 |
syslog | 開啟系統日誌功能,只在linux/osx上生效 |
pid-file | 提供一個路徑來存放pid檔案,可以使用該檔案進行SIGINT/SIGHUP(關閉/更新)agent |
2.4. Docker安裝Consul叢集
2.4.1. 拉取consul官方映象
madison@ubuntu:~$ docker pull consul:latest
複製程式碼
2.4.2. 啟動Server節點
執行consul
映象,啟動Server Master
節點node1
:
node1:
madison@ubuntu:~$ docker run -d --name=node1 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": 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 \
-h node1 \
consul agent -server -bind=172.17.0.2 -bootstrap-expect=3 -node=node1 \
-data-dir=/tmp/data-dir -client 0.0.0.0 -ui
複製程式碼
檢視node1
的日誌,追蹤執行情況:
現在叢集中還沒有選舉leader
節點,繼續啟動其餘兩臺Server
節點node2
和node3
:
node2:
madison@ubuntu:~$ docker run -d --name=node2 --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 \
-h node2 \
consul agent -server -bind=172.17.0.3 \
-join=192.168.127.128 -node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node2 \
-data-dir=/tmp/data-dir -client 0.0.0.0 -ui
複製程式碼
檢視node2
節點的程式啟動日誌:
node3:
madison@ubuntu:~$ docker run -d --name=node3 --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 \
-h node2 \
consul agent -server -bind=172.17.0.4 \
-join=192.168.127.128 -node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node3 \
-data-dir=/tmp/data-dir -client 0.0.0.0 -ui
複製程式碼
檢視node3
節點的程式啟動日誌:
當3個Server
節點都啟動並正常執行時,觀察node2
和node3
的程式日誌,可以發現node1
被選舉為leader
節點,也就是這個資料中心的Server Master
。
再次檢視node1
節點的程式啟動日誌:
觀察日誌發現,node2
和node3
都成功join到了node1
所在的資料中心dc1
。當叢集中有3臺Consul Server
啟動時,node1
被選舉為dc1
中的主節點。然後,node1
會通過心跳檢查的方式,不斷地對node2
和node3
進行健康檢查。
2.4.4. 啟動Client節點
node4:
madison@ubuntu:~$ docker run -d --name=node4 --restart=always \
-e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": 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 \
-h node4 \
consul agent -bind=172.17.0.5 -retry-join=192.168.127.128 \
-node-id=$(uuidgen | awk '{print tolower($0)}') \
-node=node4 -client 0.0.0.0 -ui
複製程式碼
檢視node4
節點的程式啟動日誌:
可以發現:node4
是以Client
模式啟動執行的。啟動後完成後,把dc1
資料中心中的以Server
模式啟動的節點node1
、node2
和node3
都新增到本地快取列表中。當客戶端向node4
發起服務發現的請求後,node4
會通過RPC
將請求轉發給Server
節點中的其中一臺做處理。
2.4.5. 檢視叢集狀態
madison@ubuntu:~$ docker exec -t node1 consul members
複製程式碼
dc1
資料中心中的4個節點node1
, node2
, node3
和node4
分別成功啟動,Status
表示他們的狀態,都為alive
。node1
, node2
, node3
以Server
模式啟動,而node4
以Client
模式啟動。
2.5. Docker安裝Registrator
2.5.1. 拉取Registrator的映象
madison@ubuntu:~$ docker pull gliderlabs/registrator:latest
複製程式碼
2.5.2. 啟動Registrator節點
madison@ubuntu:~$ docker run -d --name=registrator \
-v /var/run/docker.sock:/tmp/docker.sock \
--net=host \
gliderlabs/registrator -ip="192.168.127.128" consul://192.168.127.128:8500
複製程式碼
--net指定為host表明使用主機模式。 -ip用於指定宿主機的IP地址,用於健康檢查的通訊地址。 consul://192.168.127.128:8500: 使用Consul作為服務登錄檔,指定具體的Consul通訊地址進行服務註冊和登出(注意:8500是Consul對外暴露的HTTP通訊埠)。
檢視Registrator
的容器程式啟動日誌:
Registrator
在啟動過程完成了以下幾步操作:
- 檢視Consul資料中心的leader節點,作為服務登錄檔;
- 同步當前宿主機的啟用容器,以及所有的服務埠;
- 分別將各個容器釋出的服務地址/埠註冊到Consul的服務註冊列表。
2.5.3. 檢視Consul的註冊狀態
Consul
提供了一個Web UI
來視覺化服務註冊列表、通訊節點、資料中心和鍵/值儲存等,直接訪問宿主機的8500
埠。
服務註冊列表:
NODES
節點下掛載著dc1
資料中心中的所有的Consul
節點,包括Consul Server
和Client
。
通訊節點列表:
啟動Registrator
以後,宿主機中的所有容器把服務都註冊到Consul
的SERVICES
上,測試完成!
總結
單資料中心的Consul
叢集的搭建就完成了!!!後續章節我會介紹如何使用Registrator
進行服務註冊的標籤化。然後通過docker
部署多例項的Web
容器來實現基於HTTP
的RESTful Service
和基於TCP
的RPC Service
的服務註冊和健康檢查定義,並演示如何以標籤標識一個服務的多個例項。
歡迎關注技術公眾號: 零壹技術棧
本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。