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


什么是服務發現

微服務的框架體系中,服務發現是不能不提的一個模塊。我相信了解或者熟悉微服務的童鞋應該都知道它的重要性。這里我只是簡單的提一下,畢竟這不是我們的重點。我們看下面的一幅圖片:

 
 

圖中,客戶端的一個接口,需要調用服務A-N。客戶端必須要知道所有服務的網絡位置的,以往的做法是配置是配置文件中,或者有些配置在數據庫中。這里就帶出幾個問題:

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

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

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

 
 

與之前一張不同的是,加了個服務發現模塊。圖比較簡單,這邊文字描述下。服務A-N把當前自己的網絡位置注冊到服務發現模塊(這里注冊的意思就是告訴),服務發現就以K-V的方式記錄下,K一般是服務名,V就是IP:PORT。服務發現模塊定時的輪詢查看這些服務能不能訪問的了(這就是健康檢查)。客戶端在調用服務A-N的時候,就跑去服務發現模塊問下它們的網絡位置,然后再調用它們的服務。這樣的方式是不是就可以解決上面的問題了呢?客戶端完全不需要記錄這些服務網絡位置,客戶端和服務端完全解耦!

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

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

consul 簡介

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

  • zookeeper
  • eureka
  • etcd
  • consul

這里就不比較哪個好哪個差了,需要的童鞋自己谷歌百度。

那么consul是啥?consul就是提供服務發現的工具。然后下面是簡單的介紹:

consul是分布式的、高可用、橫向擴展的。consul提供的一些關鍵特性:

  • service discovery:consul通過DNS或者HTTP接口使服務注冊和服務發現變的很容易,一些外部服務,例如saas提供的也可以一樣注冊。
  • health checking:健康檢測使consul可以快速的告警在集群中的操作。和服務發現的集成,可以防止服務轉發到故障的服務上面。
  • key/value storage:一個用來存儲動態配置的系統。提供簡單的HTTP接口,可以在任何地方操作。
  • multi-datacenter:無需復雜的配置,即可支持任意數量的區域。

我們這里會介紹服務發現,健康檢查,還有一些基本KV存儲。多數據中心有機會另一篇文章再說。

總結:只要知道它是解決我上一部分提出的問題就行,其它的東西慢慢理解

consul的幾個概念

Image.png
上圖是我從 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,同時也要負責各個節點的健康監測。

  • 其它信息

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

上圖是官網提供的一個事例系統圖,圖中的Server是consul服務端高可用集群,Client是consul客戶端。consul客戶端不保存數據,客戶端將接收到的請求轉發給響應的Server端。Server之間通過局域網或廣域網通信實現數據一致性。每個Server或Client都是一個consul agent。Consul集群間使用了GOSSIP協議通信和raft一致性算法。上面這張圖涉及到了很多術語:

  • Agent——agent是一直運行在Consul集群中每個成員上的守護進程。通過運行 consul agent 來啟動。agent可以運行在client或者server模式。指定節點作為client或者server是非常簡單的,除非有其他agent實例。所有的agent都能運行DNS或者HTTP接口,並負責運行時檢查和保持服務同步。
  • Client——一個Client是一個轉發所有RPC到server的代理。這個client是相對無狀態的。client唯一執行的后台活動是加入LAN gossip池。這有一個最低的資源開銷並且僅消耗少量的網絡帶寬。
  • Server——一個server是一個有一組擴展功能的代理,這些功能包括參與Raft選舉,維護集群狀態,響應RPC查詢,與其他數據中心交互WAN gossip和轉發查詢給leader或者遠程數據中心。
  • DataCenter——雖然數據中心的定義是顯而易見的,但是有一些細微的細節必須考慮。例如,在EC2中,多個可用區域被認為組成一個數據中心?我們定義數據中心為一個私有的,低延遲和高帶寬的一個網絡環境。這不包括訪問公共網絡,但是對於我們而言,同一個EC2中的多個可用區域可以被認為是一個數據中心的一部分。
  • Consensus——在我們的文檔中,我們使用Consensus來表明就leader選舉和事務的順序達成一致。由於這些事務都被應用到有限狀態機上,Consensus暗示復制狀態機的一致性。
  • Gossip——Consul建立在Serf的基礎之上,它提供了一個用於多播目的的完整的gossip協議。Serf提供成員關系,故障檢測和事件廣播。更多的信息在gossip文檔中描述。這足以知道gossip使用基於UDP的隨機的點到點通信。
  • LAN Gossip——它包含所有位於同一個局域網或者數據中心的所有節點。
  • WAN Gossip——它只包含Server。這些server主要分布在不同的數據中心並且通常通過因特網或者廣域網通信。
  • RPC——遠程過程調用。這是一個允許client請求server的請求/響應機制。

在每個數據中心,client和server是混合的。一般建議有3-5台server。這是基於有故障情況下的可用性和性能之間的權衡結果,因為越多的機器加入達成共識越慢。然而,並不限制client的數量,它們可以很容易的擴展到數千或者數萬台。

  同一個數據中心的所有節點都必須加入gossip協議。這意味着gossip協議包含一個給定數據中心的所有節點。這服務於幾個目的:第一,不需要在client上配置server地址。發現都是自動完成的。第二,檢測節點故障的工作不是放在server上,而是分布式的。這是的故障檢測相比心跳機制有更高的可擴展性。第三:它用來作為一個消息層來通知事件,比如leader選舉發生時。

  每個數據中心的server都是Raft節點集合的一部分。這意味着它們一起工作並選出一個leader,一個有額外工作的server。leader負責處理所有的查詢和事務。作為一致性協議的一部分,事務也必須被復制到所有其他的節點。因為這一要求,當一個非leader得server收到一個RPC請求時,它將請求轉發給集群leader。

  server節點也作為WAN gossip Pool的一部分。這個Pool不同於LAN Pool,因為它是為了優化互聯網更高的延遲,並且它只包含其他Consul server節點。這個Pool的目的是為了允許數據中心能夠以low-touch的方式發現彼此。這使得一個新的數據中心可以很容易的加入現存的WAN gossip。因為server都運行在這個pool中,它也支持跨數據中心請求。當一個server收到來自另一個數據中心的請求時,它隨即轉發給正確數據中想一個server。該server再轉發給本地leader。

  這使得數據中心之間只有一個很低的耦合,但是由於故障檢測,連接緩存和復用,跨數據中心的請求都是相對快速和可靠的。

 

consul 基本使用

自己就一台機子,所以這里就演示下docker下部署使用consul。容器與宿主機的端口映射忽略,正常生產環境每個宿主機一個consul,端口需要映射到宿主機

部署

拉取鏡像

    docker search consul

咱們用官方的鏡像玩玩

    docker pull consul

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

啟動consul

  • 啟動節點1(server模式)
      docker run -d -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node1 consul agent -server -bind=172.17.0.2 -bootstrap-expect=3 -node=node1 

-node:節點的名稱, 每個結點ID, 必須是唯一的
-bind:綁定的一個地址,用於server節點之間通信的地址,可以是內外網,必須是可以訪問到的地址

-client: 客戶端的ip地址
-server:以server身份啟動
-bootstrap-expect:這個就是表示期望提供的SERVER節點數目,集群要求的最少server數量,當低於這個數量,集群即失效。

-data-dir:data存放的目錄,更多信息請參閱consul數據同步機制

  • 啟動節點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。

docker exec -t node1 consul  info | grep  leader

 

其中,ip 地址172.17.0.2是leader, 也就是node1.

 

  • 節點異常consul的處理

  • LEADER 掛了
    leader掛了,consul會重新選取出新的leader,只要超過一半的SERVER還活着,集群是可以正常工作的。node1是leader,所以把這個容器停了。

     docker stop node1
    

    看看其他節點的日志(node2):

    docker attach node3
     

     

     
     

    日志打印,心跳檢查node1的ip超時,接着開始選舉。node3被選舉為新的leader。我們查看下現在的leader:

      curl http://172.17.0.3:8500/v1/status/leader

返回的內容:

      "172.17.0.4:8300" 

172.17.0.4 就是 node3節點的IP

(補充內容)節點與宿主機的端口映射

指定node1與宿主機(192.168.99.100)的端口映射

    docker run -d --net=host -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' --name=node1 -p 8300:8300 -p 8301:8301 -p 8301:8301/udp -p 8302:8302/udp -p 8302:8302 -p 8400:8400 -p 8500:8500 -p 53:53/udp -h consul consul agent -server -bind=172.17.0.2 -bootstrap -node=node1 

node2進入的IP指定為宿主機的IP

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

正常加入,node1的日志:

    2017/04/09 05:53:08 [INFO] serf: EventMemberJoin: node2 172.17.0.3 2017/04/09 05:53:08 [INFO] consul: Adding LAN server node2 (Addr: tcp/172.17.0.3:8300) (DC: dc1) 2017/04/09 05:53:08 [INFO] raft: Updating configuration with AddStaging (172.17.0.3:8300, 172.17.0.3:8300) to [{Suffrage:Voter ID:172.17.0.2:8300 Address:172.17.0.2:8300} {Suffrage:Voter ID:172.17.0.3:8300 Address:172.17.0.3:8300}] 2017/04/09 05:53:08 [INFO] raft: Added peer 172.17.0.3:8300, starting replication 2017/04/09 05:53:08 [INFO] consul: member 'node2' joined, marking health alive 2017/04/09 05:53:08 [INFO] serf: EventMemberJoin: node2.dc1 172.17.0.3 2017/04/09 05:53:08 [INFO] consul: Handled member-join event for server "node2.dc1" in area "wan" 2017/04/09 05:53:08 [WARN] raft: AppendEntries to {Voter 172.17.0.3:8300 172.17.0.3:8300} rejected, sending older logs (next: 1) 2017/04/09 05:53:08 [INFO] raft: pipelining replication to peer {Voter 172.17.0.3:8300 172.17.0.3:8300} 
Node Address Status Type Build Protocol DC node1 172.17.0.2:8301 alive server 0.8.0 2 dc1 node2 172.17.0.3:8301 alive server 0.8.0 2 dc1 

使用

部署完了,那么可以看看怎么用這個東東了。

注冊個服務

使用HTTP API 注冊個服務,使用[接口API](https://www.consul.io/api/agent/service.htmlAPI)調用

調用 http://consul:8500/v1/agent/service/register PUT 注冊一個服務。request body:

{
  "ID": "userServiceId", //服務id "Name": "userService", //服務名 "Tags": [ //服務的tag,自定義,可以根據這個tag來區分同一個服務名的服務 "primary", "v1" ], "Address": "127.0.0.1",//服務注冊到consul的IP,服務發現,發現的就是這個IP "Port": 8000, //服務注冊consul的PORT,發現的就是這個PORT "EnableTagOverride": false, "Check": { //健康檢查部分 "DeregisterCriticalServiceAfter": "90m", "HTTP": "http://www.baidu.com", //指定健康檢查的URL,調用后只要返回20X,consul都認為是健康的 "Interval": "10s" //健康檢查間隔時間,每隔10s,調用一次上面的URL } } 

使用curl調用

curl http://172.17.0.4:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{ "ID": "userServiceId", "Name": "userService", "Tags": [ "primary", "v1" ], "Address": "127.0.0.1", "Port": 8000, "EnableTagOverride": false, "Check": { "DeregisterCriticalServiceAfter": "90m", "HTTP": "http://www.baidu.com", "Interval": "10s" } }' 

OK,注冊了一個服務

發現個服務

剛剛注冊了名為userService的服務,我們現在發現(查詢)下這個服務

curl http://172.17.0.4:8500/v1/catalog/service/userService 

返回的響應:

[
    {
        "Address": "172.17.0.4", "CreateIndex": 880, "ID": "e6e9a8cb-c47e-4be9-b13e-a24a1582e825", "ModifyIndex": 880, "Node": "node3", "NodeMeta": {}, "ServiceAddress": "127.0.0.1", "ServiceEnableTagOverride": false, "ServiceID": "userServiceId", "ServiceName": "userService", "ServicePort": 8000, "ServiceTags": [ "primary", "v1" ], "TaggedAddresses": { "lan": "172.17.0.4", "wan": "172.17.0.4" } } ] 

內容有了吧,這個就是我們剛剛注冊的服務的信息,就可以獲取到

服務的名稱是“userService”
服務地址是“127.0.0.1”
服務的端口是“8000”

存儲個K/V

設置一個值到user/config/connections 內容為5

docker exec -t node1 consul kv put user/config/connections 5 

獲取特定的值

 docker exec -t node1 consul kv get user/config/connections

詳細信息  

docker exec -t node1 consul kv get -detailed user/config/connections 
 

 

值的內容為5,還有key等相關的值

總結

服務發現以及配置共享的簡單樣例展示了下,詳細的使用還是需要看官方文檔,這里只是列舉了一些樣例,用於理解和簡單的使用consul。

Spring Cloud 結合consul使用

如果是使用spring cloud來使用consul,可以查看我的相關樣例:http://git.oschina.net/buxiaoxia/spring-demo

spring cloud 結合consul的使用,下一篇文章再進行描述吧

相關文檔連接

CONSUL:https://www.consul.io/
CONSUL HTTP API:https://www.consul.io/api/index.html
CONSUL CLI:https://www.consul.io/docs/commands/info.html
CONSUL Health Checks:https://www.consul.io/docs/agent/checks.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM