1. 程式人生 > >服務發現之Consul介紹、部署和使用

服務發現之Consul介紹、部署和使用

服務發現之Consul介紹、部署和使用

為什麼使用服務發現

微服務的框架體系中,服務發現是不得不提的一個模組。我相信瞭解或者熟悉微服務的應該都知道它的重要性。這裡簡單的介紹一下。我們看下面的一幅圖片:

Alt

圖中,客戶端的一個介面,需要呼叫服務A-N。客戶端必須要知道所有服務的網路位置的,以往的做法是將配置放到配置檔案或資料庫中。這裡就帶出幾個問題:

  • 需要配置N個服務的網路位置,加大配置的複雜性
  • 服務的網路位置變化,都需要改變每個呼叫者的配置
  • 叢集的情況下,難以做負載(反向代理的方式除外)

總結起來一句話:服務多了,配置很麻煩,問題多多

既然有這些問題,那麼服務發現就是解決這些問題的。話說,怎麼解決呢?我們再看一張圖

Alt

與之前一張不同的是,加了個服務發現模組。服務A-N把當前自己的網路位置註冊到服務發現模組(這裡註冊的意思就是告訴),服務發現就以K-V的方式記錄下,K一般是服務名,V就是IP:PORT。服務發現模組定時的輪詢檢視這些服務能不能訪問的了(這就是健康檢查)。客戶端在呼叫服務A-N的時候,就跑去服務發現模組問下它們的網路位置,然後再呼叫它們的服務。這樣的方式是不是就可以解決上面的問題了呢?客戶端完全不需要記錄這些服務網路位置,客戶端和服務端完全解耦!

這個過程大體是這樣,當然服務發現模組沒這麼簡單。裡面包含的東西還很多。這樣表述只是方便理解。

圖中的服務發現模組基本上就是微服務架構中服務發現的作用了。

Consul 簡介

做服務發現的框架常用的有

Consul和Eureka的區別

Eureka是一個服務發現工具。該體系結構主要是客戶端/伺服器,每個資料中心有一組Eureka伺服器,通常每個可用區域一個。通常Eureka的客戶使用嵌入式SDK來註冊和發現服務。對於非本地整合的客戶,使用功能區邊框等透過Eureka透明地發現服務。

Eureka提供了一個弱一致的服務檢視,使用盡力而為複製。當客戶端向伺服器註冊時,該伺服器將嘗試複製到其他伺服器,但不提供保證。服務註冊的生存時間(TTL)較短,要求客戶端對伺服器心存感激。不健康的服務或節點將停止心跳,導致它們超時並從登錄檔中刪除。發現請求可以路由到任何服務,由於盡力而為的複製,這些服務可能會導致陳舊或丟失資料。這個簡化的模型允許簡單的群集管理和高可擴充套件性。

領事提供了一套超級功能,包括更豐富的健康檢查,關鍵/價值儲存以及多資料中心意識。Consul需要每個資料中心都有一套伺服器,以及每個客戶端的代理,類似於使用像Ribbon這樣的邊車。Consul代理允許大多數應用程式成為Consul不知情者,通過配置檔案執行服務註冊並通過DNS或負載平衡器sidecars發現。

Consul提供強大的一致性保證,因為伺服器使用Raft協議複製狀態 。Consul支援豐富的健康檢查,包括TCP,HTTP,Nagios / Sensu相容指令碼或基於Eureka的TTL。客戶端節點參與基於八卦的健康檢查,該檢查分發健康檢查工作,而不像集中式心跳檢測那樣成為可擴充套件性挑戰。發現請求被路由到選舉出來的領事領導,這使他們預設情況下強烈一致。允許陳舊讀取的客戶端使任何伺服器都可以處理他們的請求,從而實現像Eureka這樣的線性可伸縮性。

Consul強烈的一致性意味著它可以作為領導選舉和叢集協調的鎖定服務。Eureka不提供類似的保證,並且通常需要為需要執行協調或具有更強一致性需求的服務執行ZooKeeper。

Consul提供了支援面向服務的體系結構所需的一系列功能。這包括服務發現,還包括豐富的執行狀況檢查,鎖定,金鑰/值,多資料中心聯合,事件系統和ACL。Consul和consul-template和envconsul等工具生態系統都試圖儘量減少整合所需的應用程式更改,以避免需要通過SDK進行本地整合。Eureka是一個更大的Netflix OSS套件的一部分,該套件預計應用程式相對均勻且緊密整合。因此,Eureka只解決了一小部分問題,希望ZooKeeper等其他工具可以一起使用。

CAP中,Consul使用CP體系結構,有利於實現可用性的一致性。

Consul概述

Consul是分散式的、高可用、橫向擴充套件的。consul提供的一些關鍵特性:

  • service discovery:consul通過DNS或者HTTP介面使服務註冊和服務發現變的很容易,一些外部服務,例如saas提供的也可以一樣註冊。
  • health checking:健康檢測使consul可以快速的告警在叢集中的操作。和服務發現的整合,可以防止服務轉發到故障的服務上面。
  • key/value storage:一個用來儲存動態配置的系統。提供簡單的HTTP介面,可以在任何地方操作。
  • multi-datacenter:無需複雜的配置,即可支援任意數量的區域。
  • Consul使用Go語言編寫,因此具有天然可移植性(支援Linux、windows和Mac OS X);安裝包僅包含一個可執行檔案,方便部署,與Docker等輕量級容器可無縫配合 。

Consul內部原理

下面這張圖來源於Consul官網,很好的解釋了Consul的工作原理,先大致看一下。

Alt

首先Consul支援多資料中心,在上圖中有兩個DataCenter,他們通過Internet互聯,同時請注意為了提高通訊效率,只有Server節點才加入跨資料中心的通訊。

在單個數據中心中,Consul分為Client和Server兩種節點(所有的節點也被稱為Agent),Server節點儲存資料,Client負責健康檢查及轉發資料請求到Server;Server節點有一個Leader和多個Follower,Leader節點會將資料同步到Follower,Server的數量推薦是3個或者5個,在Leader掛掉的時候會啟動選舉機制產生一個新的Leader。

叢集內的Consul節點通過gossip協議(流言協議)維護成員關係,也就是說某個節點了解叢集內現在還有哪些節點,這些節點是Client還是Server。單個數據中心的流言協議同時使用TCP和UDP通訊,並且都使用8301埠。跨資料中心的流言協議也同時使用TCP和UDP通訊,埠使用8302。

叢集內資料的讀寫請求既可以直接發到Server,也可以通過Client使用RPC轉發到Server,請求最終會到達Leader節點,在允許資料輕微陳舊的情況下,讀請求也可以在普通的Server節點完成,叢集內資料的讀寫和複製都是通過TCP的8300埠完成。

現在我們只看DataCenter,可以看出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,同時也要負責各個節點的健康監測。

  • 其它資訊

其它資訊包括它們之間的通訊方式,還有一些協議資訊,演算法。它們是用於保證節點之間的資料同步,實時性要求等等一系列叢集問題的解決。這些有興趣的自己看看官方文件

Consul服務發現原理

下面這張圖基本描述了服務發現的完整流程,先大致看一下。

Alt

首先需要有一個正常的Consul叢集,有Server,有Leader。這裡在伺服器Server1、Server2、Server3上分別部署了Consul Server,假設他們選舉了Server2上的Consul Server節點為Leader。這些伺服器上最好只部署Consul程式,以儘量維護Consul Server的穩定。

然後在伺服器Server4和Server5上通過Consul Client分別註冊Service A、B、C,這裡每個Service分別部署在了兩個伺服器上,這樣可以避免Service的單點問題。服務註冊到Consul可以通過HTTP API(8500埠)的方式,也可以通過Consul配置檔案的方式。Consul Client可以認為是無狀態的,它將註冊資訊通過RPC轉發到Consul Server,服務資訊儲存在Server的各個節點中,並且通過Raft實現了強一致性。

最後在伺服器Server6中Program D需要訪問Service B,這時候Program D首先訪問本機Consul Client提供的HTTP API,本機Client會將請求轉發到Consul Server,Consul Server查詢到Service B當前的資訊返回,最終Program D拿到了Service B的所有部署的IP和埠,然後就可以選擇Service B的其中一個部署並向其發起請求了。如果服務發現採用的是DNS方式,則Program D中直接使用Service B的服務發現域名,域名解析請求首先到達本機DNS代理,然後轉發到本機Consul Client,本機Client會將請求轉發到Consul Server,Consul Server查詢到Service B當前的資訊返回,最終Program D拿到了Service B的某個部署的IP和埠。

圖中描述的部署架構筆者認為是最普適最簡單的方案,從某些預設配置或設計上看也是官方希望使用者採用的方案,比如8500埠預設監聽127.0.0.1,當然有些同學不贊同,後邊會提到其他方案。

Consul基本使用

為了更快的熟悉Consul的原理及其使用方式,最好還是自己實際測試下。

Consul安裝十分簡單,但是在一臺機器上不方便搭建叢集進行測試,使用虛擬機器比較重,所以這裡就演示下docker下部署使用Consul。容器與宿主機的埠對映忽略,正常生產環境每個宿主機一個Consul,埠需要對映到宿主機

安裝Docker

更新yum

yum update

安裝依賴包

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

設定阿里雲映象源

sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安裝 Docker-CE

sudo yum install docker-ce

檢查並啟動 Docker-CE

docker -v
sudo yum install docker-ce
sudo systemctl start docker

GUI 管理配置(可選)

這裡推薦使用 Portainer 作為容器的 GUI 管理方案。

官方地址:https://portainer.io/install.html

安裝命令:

docker volume create portainer_data
docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

訪問你的 IP:9000 即可進入容器管理頁面。

部署Consul

拉取映象

docker search consul
docker pull consul

不指定tag就拉取last,當前版本是0.8.1

啟動Consul

這裡啟動4個Consul Agent,3個Server(會選舉出一個leader),1個Client。

  • 啟動第一個Server節點同時開啟管理介面

docker run -d -p 8500:8500 -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node1 consul agent -server -bind=172.17.0.2  -bootstrap-expect=3 -node=node1 --bootstrap-expect=3 --client=0.0.0.0 -ui

-node:節點的名稱
-p: 將容器8500埠對映到主機8500埠
-bind:繫結的一個地址,用於節點之間通訊的地址,可以是內外網,必須是可以訪問到的地址
-server:這個就是表示這個節點是個SERVER
-bootstrap-expect:這個就是表示期望提供的SERVER節點數目,數目一達到,它就會被啟用,然後就是LEADER了
-ui 開啟管理介面

  • 啟動第2-3個Server節點,並加入叢集

docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node2 consul agent -server -bind=172.17.0.3  -join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}')  -node=node2
docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node3 consul agent -server -bind=172.17.0.4  -join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}')  -node=node3 -client=172.17.0.4

-join:這個表示啟動的時候,要加入到哪個叢集內,這裡就是說要加入到節點1的叢集
-node-id:這個貌似版本8才加入的,這裡用這個來指定唯一的節點ID,可以檢視這個issue
-client:這個表示註冊或者查詢等一系列客戶端對它操作的IP,如果不指定這個IP,預設是127.0.0.1。

  • 啟動第4個Client節點,並加入叢集

docker run -d -e 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' --name=node4 consul agent -bind=172.17.0.5 -retry-join=172.17.0.2 -node-id=$(uuidgen | awk '{print tolower($0)}')  -node=node4

除了沒有 -server,其它都是一樣的,沒有這個就說明這個節點是CLIENT

  • 檢視叢集狀態

docker exec -t node1 consul members

叢集狀態

4個節點都列出來了。Status表示它們的狀態,都是alive。Type表示它們的型別,三個SERVER一個CLIENT,和我們之前啟動的一樣。DC表示資料中心,都是dc1。

Consul的其它部署架構

如果你實在不想在每個主機部署Consul Client,還有一個多路註冊的方案可供選擇

Consul的其它部署架構

如圖所示,在專門的伺服器上部署Consul Client,然後每個服務都註冊到多個Client,這裡為了避免服務單點問題還是每個服務部署多份,需要服務發現時,程式向一個提供負載均衡的程式發起請求,該程式將請求轉發到某個Consul Client。這種方案需要注意將Consul的8500埠繫結到私網IP上,預設只有127.0.0.1。

這個架構的優勢:

  • Consul節點伺服器與應用伺服器隔離,互相干擾少;
  • 不用每臺主機都部署Consul,方便Consul的集中管理;
  • 某個Consul Client掛掉的情況下,註冊到其上的服務仍有機會被訪問到;

但也需要注意其缺點:

  • 引入更多技術棧:負載均衡的實現,不僅要考慮Consul Client的負載均衡,還要考慮負載均衡本身的單點問題。
  • Client的節點數量:單個Client如果註冊的服務太多,負載較重,需要有個演算法(比如hash一致)合理分配每個Client上的服務數量,以及確定Client的總體數量。
  • 服務發現要過濾掉重複的註冊,因為註冊到了多個節點會認為是多個部署(DNS介面不會有這個問題)。

這個方案其實還可以優化,服務發現使用的負載均衡可以直接代理Server節點,因為相關請求還是會轉發到Server節點,不如直接就發到Server。

是否可以只有Server?

這個問題的答案還是有關服務數量的問題,首先Server的節點數量不是越多越好,3個或者5個是推薦的數量,數量越多資料同步的處理越慢(強一致性);然後每個節點可以註冊的服務數量是有上限的,這個受限於軟硬體的處理能力。所以如果你的服務只有10個左右,只有Server問題是不大的,但是這時候有沒有必要使用Consul呢?因此正常使用Consul的時候還是要有Client才好,這也符合Consul的反熵設計。