使用 Consul 作為 Python 微服務的配置中心

elfgzp發表於2019-03-14

consul

部落格原文地址: https://elfgzp.cn/2019/03/11/consul-template-python-micro-service.html

前半部分主要為 Consul 的一些介紹,若已經瞭解 Consul,可以直接跳轉到:

使用 Consul 作為 Python 微服務的配置中心

Consul 簡單介紹

Consul 是 HashiCorp 公司推出的開源工具,用於實現分散式系統的服務發現與配置。Consul 是分散式的、高可用的、 可橫向擴充套件的。它具備以下特性:

  • 服務發現: Consul 提供了通過 DNS 或者 HTTP 介面的方式來註冊服務和發現服務。一些外部的服務通過 Consul 很容易的找到它所依賴的服務。
  • 健康檢測: Consul 的 Client 提供了健康檢查的機制,可以通過用來避免流量被轉發到有故障的服務上。
  • Key/Value 儲存: 應用程式可以根據自己的需要使用 Consul 提供的 Key/Value 儲存。 Consul 提供了簡單易用的 HTTP 介面,結合其他工具可以實現動態配置、功能標記、領袖選舉等等功能。
  • 多資料中心: Consul 支援開箱即用的多資料中心. 這意味著使用者不需要擔心需要建立額外的抽象層讓業務擴充套件到多個區域。

img

Consul 架構圖解

Consul 叢集間使用了 Gossip 協議通訊和 raft 一致性演算法。上面這張圖涉及到了很多術語:

  • Gossip —— Gossip protocol 也叫 Epidemic Protocol (流行病協議),實際上它還有很多別名,比如:“流言演算法”、“疫情傳播演算法”等。 這個協議的作用就像其名字表示的意思一樣,非常容易理解,它的方式其實在我們日常生活中也很常見,比如電腦病毒的傳播,森林大火,細胞擴散等等。
  • Client —— 一個 Client 是一個轉發所有 RPC 到 server 的代理。這個 client 是相對無狀態的。client 唯一執行的後臺活動是加入 LAN gossip 池。這有一個最低的資源開銷並且僅消耗少量的網路頻寬。
  • Server —— 一個 server 是一個有一組擴充套件功能的代理,這些功能包括參與 Raft 選舉,維護叢集狀態,響應 RPC 查詢,與其他資料中心互動 WAN gossip 和轉發查詢給 leader 或者遠端資料中心。
  • DataCenter —— 雖然資料中心的定義是顯而易見的,但是有一些細微的細節必須考慮。例如,在 EC2 中,多個可用區域被認為組成一個資料中心。我們定義資料中心為一個私有的,低延遲和高頻寬的一個網路環境。這不包括訪問公共網路,但是對於我們而言,同一個 EC2 中的多個可用區域可以被認為是一個資料中心的一部分。
  • Consensus —— 一致性,使用 Consensus 來表明就 leader 選舉和事務的順序達成一致。為了以容錯方式達成一致,一般有超過半數一致則可以認為整體一致。Consul 使用 Raft 實現一致性,進行 leader 選舉,在 consul 中的使用 bootstrap 時,可以進行自選,其他 server 加入進來後 bootstrap 就可以取消。
  • LAN Gossip —— 它包含所有位於同一個區域網或者資料中心的所有節點。
  • WAN Gossip —— 它只包含 Server。這些 server 主要分佈在不同的資料中心並且通常通過因特網或者廣域網通訊。
  • RPC——遠端過程呼叫。這是一個允許 client 請求 server 的請求/響應機制。

Gossip 介紹

這裡先簡單介紹一下 Gossip 協議的執行過程:

Gossip 過程是由種子節點發起,當一個種子節點有狀態需要更新到網路中的其他節點時,它會隨機的選擇周圍幾個節點散播訊息,收到訊息的節點也會重複該過程,直至最終網路中所有的節點都收到了訊息。這個過程可能需要一定的時間,由於不能保證某個時刻所有節點都收到訊息,但是理論上最終所有節點都會收到訊息,因此它是一個最終一致性協議。

現在,我們通過一個具體的例項來深入體會一下 Gossip 傳播的完整過程: 為了表述清楚,我們先做一些前提設定 1、Gossip 是週期性的散播訊息,把週期限定為 1 秒 2、被感染節點隨機選擇 k 個鄰接節點(fan-out)散播訊息,這裡把 fan-out 設定為 3,每次最多往 3 個節點散播。 3、每次散播訊息都選擇尚未傳送過的節點進行散播 4、收到訊息的節點不再往傳送節點散播,比如 A -> B,那麼 B 進行散播的時候,不再發給 A。 這裡一共有 16 個節點,節點 1 為初始被感染節點,通過 Gossip 過程,最終所有節點都被感染:

gossip

注意要點:

  • Consul Cluster 由部署和執行了 Consul Agent 的節點組成。在 Cluster 中有兩種角色:Server 和 Client。
  • Server 和 Client 的角色和 Consul Cluster 上執行的應用服務無關, 是基於 Consul 層面的一種角色劃分.
  • Consul Server: 用於維護 Consul Cluster 的狀態資訊。 官方建議是: 至少要執行 3 個或者 3 個以上的 Consul Server。 多個 server 之中需要選舉一個 leader, 這個選舉過程 Consul 基於 Raft 協議實現. 多個 Server 節點上的 Consul 資料資訊保持強一致性。在區域網內與本地客戶端通訊,通過廣域網與其他資料中心通訊。
  • Consul Client: 只維護自身的狀態, 並將 HTTP 和 DNS 介面請求轉發給服務端。

Consul 與其他常見服務發現框架對比

名稱 優點 缺點 介面 一致性演算法
zookeeper 1.功能強大,不僅僅只是服務發現
2.提供 watcher 機制能實時獲取服務提供者的狀態
3.dubbo 等框架支援
1.沒有健康檢查
2.需在服務中整合 sdk,複雜度高
3.不支援多資料中心
sdk Paxos
consul 1.簡單易用,不需要整合 sdk
2.自帶健康檢查
3.支援多資料中心
4.提供 web 管理介面
1.不能實時獲取服務資訊的變化通知 http/dns Raft
etcd 1.簡單易用,不需要整合 sdk
2.可配置性強
1.沒有健康檢查
2.需配合第三方工具一起完成服務發現
3.不支援多資料中心
http Raft

Consul 單機環境部署

首先 clone 本專案到本地:

$ git clone git@github.com:elfgzp/python-consul-demo.git
複製程式碼

Consul 單機部署所需要用到的檔案如下:

  • docker-compose-server.yml —— Consul Server docker-compose 配置檔案
  • docker-compose-client.yml —— Consul Client docker-compose 配置檔案

可以看到兩個 docker-compose 檔案均暴露了 8 個埠。根據官方文件,這些埠的作用如下:

Ports Used

Consul requires up to 6 different ports to work properly, some on TCP, UDP, or both protocols. Below we document the requirements for each port.

  • Server RPC (Default 8300). This is used by servers to handle incoming requests from other agents. TCP only.
  • Serf LAN (Default 8301). This is used to handle gossip in the LAN. Required by all agents. TCP and UDP.
  • Serf WAN (Default 8302). This is used by servers to gossip over the WAN, to other servers. TCP and UDP. As of Consul 0.8 the WAN join flooding feature requires the Serf WAN port (TCP/UDP) to be listening on both WAN and LAN interfaces. See also: Consul 0.8.0 CHANGELOG and GH-3058
  • HTTP API (Default 8500). This is used by clients to talk to the HTTP API. TCP only.
  • DNS Interface (Default 8600). Used to resolve DNS queries. TCP and UDP.

還有一些配置引數這裡就不一一介紹了,官方文件寫的非常詳細。

Consul Server 部署

首先需要給環境變數 CONSUL_SERVER_IP_ADDR 賦值,若為 ECS 則為 ECS 的外網 IP,這裡也是演示在 ECS 部署。

$ export CONSUL_SERVER_IP_ADDR={YOUR_IP_ADDR}
複製程式碼

然後執行,這裡沒有使用 -d 引數,方便檢視日誌:

$ docker-compose -f docker-compose-server.yml up
複製程式碼

docker-compose -f docker-compose-server.yml up

Consul Client 部署

執行 docker-compose 前,設定 CONSUL_SERVER_HOST 環境變數:

$ export CONSUL_SERVER_HOST={YOUR_SERVER_HOST}
複製程式碼

然後執行:

$ docker-compose -f docker-compose-client.yml up
複製程式碼

img4

部署完成後可以通過 Consul UI 的 Nodes 看到兩個 Healthy Nodes

img4

也可以在部署好 Consul Client 的宿主機上執行:

$ consul members
複製程式碼

img5

這裡要注意

在檢視了 Consul 倉庫的 issue#1720 發現,Consul 提供的 Web UI 並沒有提供 Auth 功能,所以可能要依靠第三方服務來實現,評論中也有提到:

highlyunavailable commented on 17 Feb 2016

When I ran a Consul Web UI I just used nginx and github.com/bitly/oauth… to provide authentication.

但是 HTTP 介面的許可權,可以通過 Consul ACL 來控制,這是後話。

使用 Consul 作為 Python 微服務的配置中心

Consul 作為資料中心,提供了 k/v 儲存的功能,我們可以利用這個功能為 Python 微服務提供配置中心。

Consul 提供了 HTTP 介面,我們可以從他的介面獲取資料,當然我們不用自己去實現,python-consul 已經幫我們造好了輪子。

而且官方文件非常貼心,已經貼好了 Python 常用框架的一些 demo 程式碼:

from tornado.ioloop import IOLoop
from tornado.gen import coroutine
from consul.base import Timeout
from consul.tornado import Consul


class Config(object):
    def __init__(self, loop):
        self.foo = None
        loop.add_callback(self.watch)

    @coroutine
    def watch(self):
        c = Consul()

        # asynchronously poll for updates
        index = None
        while True:
            try:
                index, data = yield c.kv.get('foo', index=index)
                if data is not None:
                    self.foo = data['Value']
            except Timeout:
                # gracefully handle request timeout
                pass

if __name__ == '__main__':
    loop = IOLoop.instance()
    _ = Config(loop)
    loop.start()
複製程式碼

結合 consul-template 用解藕的方式去配置微服務

Consul Template 提供一個方便的方式從 Consul 獲取資料通過 consul-template 的後臺程式儲存到檔案系統。
這個後臺程式監控 Consul 示例的變化並更新任意數量的模板到檔案系統.作為一個附件功能,模板更新完成後 consul-template 可以執行任何命令.可以檢視示例部分看這個功能將會對哪些應用場景產生幫助。

首先需要在 Consul Client 所在的宿主機安裝 consul-template,由於 Demo 宿主機環境為 Mac OS,所以可以直接用 HomeBrew 進行安裝。

$ brew install consul-template
複製程式碼

安裝完成後進入倉庫的 python-web-service 路徑,這是一個用 tornado 寫的簡單的 Web 服務。執行如下命令:

$ cd python-web-service && docker-compose up -d
複製程式碼

等待命令執行完成,服務啟動後,訪問 localhost:8888 可以看到返回內容:

$ curl http://localhost:8888
Hello World
複製程式碼

然後我們回到倉庫路徑,進入 consul-template 目錄,該目錄主要包含以下兩個檔案:

$ cd ../consul-template
$ tree
.
├── config.hcl # consul-template 配置檔案
└── config.py.ctmpl # python-web-service 配置模版檔案
複製程式碼

檢視一下 config.hcl 檔案的內容:

consul {
  address = "127.0.0.1:8500"

}

template {

  source = "./config.py.ctmpl"
  destination = "../python-web-service/config.py"
  command = "docker restart python-web-service_python-web-service_1"

}
複製程式碼

先介紹一下 *.hcl 配置檔案,這個是 Consul 中非常常見的配置檔案格式,也是 HashiCorp 下的產品所用的主要配置檔案格式。配置檔案中包含了 4 個重要的引數:

  • address —— Consul Client 的訪問地址和埠
  • source —— 需要配置的服務的配置檔案模板
  • destination —— 配置檔案渲染後輸出的路徑
  • command —— 當配置變更後,需要執行的命令

再來看看模板檔案 config.py.ctmpl

# -*- coding: utf-8 -*-
__author__ = 'gzp'

GREETING = '{{ keyOrDefault "python-web-service/greeting" "Hello World" }}'
複製程式碼

模版檔案的格式非常類似 Jinja2 的語法,這裡的意思獲取 keypython-web-service/greeting 下的值,預設值為 HelloWorld

接下來執行命令使 consul-template 生效:

$ consul-template -config config.hcl
複製程式碼

我們可以訪問 Consul Web UI 的 Key/Value 來修改我們的值:

img6

Hello World 修改為 Hello Consul,配置可能沒有立即生效,若看到 consul-template 輸出,則代表配置生效,服務以及重啟:

$ consul-template -config config.hcl
python-web-service_python-web-service_1
複製程式碼

然後再次訪問一下 web 服務:

$ curl http://localhost:8888
Hello Consul
複製程式碼

可以看到配置已經生效。

參考文獻

Consul 入門 - 運維之美

P2P 網路核心技術:Gossip 協議

服務發現框架選型,Consul 還是 Zookeeper 還是 etcd

相關文章