https://deepzz.com/post/the-consul-of-discovery-and-configure-services.html
consul 是一個支持多數(shù)據(jù)中心分布式高可用,用于服務(wù)發(fā)現(xiàn)和配置共享的工具。那么,之前已經(jīng)有了眾多的用于服務(wù)發(fā)現(xiàn)和配置的工具,如:etcd、zookeeper 等,為什么還冒出來個 consul?閱讀 Consul vs. Other Software 或許你可以找到答案。
consul 關(guān)鍵特性
- 服務(wù)發(fā)現(xiàn):支持服務(wù)發(fā)現(xiàn)。你可以通過 DNS 或 HTTP 的方式獲取服務(wù)信息。
- 健康檢查:支持健康檢查??梢蕴峁┡c給定服務(wù)相關(guān)聯(lián)的任何數(shù)量的健康檢查(如 web 狀態(tài)碼或 cpu 使用率)。
- K/V 存儲:鍵/值對存儲。你可用通過 consul 存儲如動態(tài)配置之類的相關(guān)信息。
- 多數(shù)據(jù)中心:支持多數(shù)據(jù)中心,開箱即用。
-
WEB UI:支持 WEB UI。點(diǎn)點(diǎn)點(diǎn),你就能夠了解你的服務(wù)現(xiàn)在的運(yùn)行情況,一目了然,對開發(fā)運(yùn)維是非常友好的。
consul_web_ui
consul 相較與 etcd、zookeeper 最大的特點(diǎn)就是:它整合了用戶普遍的需求,開箱即用,降低了使用的門檻。
consul 術(shù)語
首先介紹下在 consul 中會經(jīng)常見到的術(shù)語:
-
node
:節(jié)點(diǎn),需要 consul 注冊發(fā)現(xiàn)或配置管理的服務(wù)器。 -
agent
:consul 中的核心程序,它將以守護(hù)進(jìn)程的方式在各個節(jié)點(diǎn)運(yùn)行,有 client 和 server 啟動模式。每個 agent 維護(hù)一套服務(wù)和注冊發(fā)現(xiàn)以及健康信息。 -
client
:agent 以 client 模式啟動的節(jié)點(diǎn)。在該模式下,該節(jié)點(diǎn)會采集相關(guān)信息,通過 RPC 的方式向 server 發(fā)送。 -
server
:agent 以 server 模式啟動的節(jié)點(diǎn)。一個數(shù)據(jù)中心中至少包含 1 個 server 節(jié)點(diǎn)。不過官方建議使用 3 或 5 個 server 節(jié)點(diǎn)組建成集群,以保證高可用且不失效率。server 節(jié)點(diǎn)參與 Raft、維護(hù)會員信息、注冊服務(wù)、健康檢查等功能。 -
datacenter
:數(shù)據(jù)中心,私有的,低延遲的和高帶寬的網(wǎng)絡(luò)環(huán)境。一般的多個數(shù)據(jù)中心之間的數(shù)據(jù)是不會被復(fù)制的,但可用過 ACL replication 或使用外部工具 onsul-replicate。 -
Consensus
,共識協(xié)議,使用它來協(xié)商選出 leader。 -
Gossip
:consul 是建立在 Serf,它提供完整的 gossip protocol,維基百科。 -
LAN Gossip
,Lan gossip 池,包含位于同一局域網(wǎng)或數(shù)據(jù)中心上的節(jié)點(diǎn)。 -
WAN Gossip
,只包含 server 的 WAN Gossip 池,這些服務(wù)器主要位于不同的數(shù)據(jù)中心,通常通過互聯(lián)網(wǎng)或廣域網(wǎng)進(jìn)行通信。 -
members
:成員,對 consul 成員的稱呼。提供會員資格,故障檢測和事件廣播。有興趣的朋友可以深入研究下。
consul 架構(gòu)
consul 的架構(gòu)是什么,官方給出了一個很直觀的圖片:
這里存在兩個數(shù)據(jù)中心:DATACENTER1、DATACENTER2。每個數(shù)據(jù)中心有著 3 到 5 臺 server(該數(shù)量使得在故障轉(zhuǎn)移和性能之間達(dá)到平衡)。
單個數(shù)據(jù)中心的所有節(jié)點(diǎn)都參與 LAN Gossip
池,也就是說該池包含了這個數(shù)據(jù)中心的所有節(jié)點(diǎn)。這有幾個目的:
- 不需要給客戶端配置服務(wù)器地址,發(fā)現(xiàn)自動完成。
- 檢測節(jié)點(diǎn)故障的工作不是放在服務(wù)器上,而是分布式的。這使得故障檢測比心跳方案更具可擴(kuò)展性。
- 事件廣播,以便在諸如領(lǐng)導(dǎo)選舉等重要事件發(fā)生時(shí)通知。
所有 server 節(jié)點(diǎn)也單獨(dú)加入 WAN Gossip
池,因?yàn)樗槍ヂ?lián)網(wǎng)的高延遲進(jìn)行了優(yōu)化。這個池的目的是允許數(shù)據(jù)中心以低調(diào)的方式發(fā)現(xiàn)對方。在線啟動新的數(shù)據(jù)中心與加入現(xiàn)有的 WAN Gossip
一樣簡單。因?yàn)檫@些服務(wù)器都在這個池中運(yùn)行,所以它也支持跨數(shù)據(jù)中心的請求。當(dāng)服務(wù)器收到對不同數(shù)據(jù)中心的請求時(shí),它會將其轉(zhuǎn)發(fā)到正確數(shù)據(jù)中心中的隨機(jī)服務(wù)器。那個服務(wù)器可能會轉(zhuǎn)發(fā)給當(dāng)?shù)氐念I(lǐng)導(dǎo)。
這導(dǎo)致數(shù)據(jù)中心之間的耦合非常低,但是由于故障檢測,連接緩存和復(fù)用,跨數(shù)據(jù)中心請求相對快速可靠。
一般來說,數(shù)據(jù)不會在不同的領(lǐng)事數(shù)據(jù)中心之間復(fù)制。當(dāng)對另一數(shù)據(jù)中心的資源進(jìn)行請求時(shí),本地 consul 服務(wù)器將
RPC 請求轉(zhuǎn)發(fā)給該資源的遠(yuǎn)程 consul 服務(wù)器并返回結(jié)果。如果遠(yuǎn)程數(shù)據(jù)中心不可用,那么這些資源也將不可用,但這不會影響本地?cái)?shù)據(jù)中心。有一些特殊情況可以復(fù)制有限的數(shù)據(jù)子集,例如使用 consul 內(nèi)置的 ACL replication 功能,或外部工具如 consul-replicate。
更多協(xié)議詳情,你可以 Consensus Protocol 和 Gossip Protocol。
consul 端口說明
consul 內(nèi)使用了很多端口,理解這些端口的用處對你理解 consul 架構(gòu)很有幫助:
端口 | 說明 |
---|---|
TCP/8300 | 8300 端口用于服務(wù)器節(jié)點(diǎn)??蛻舳送ㄟ^該端口 RPC 協(xié)議調(diào)用服務(wù)端節(jié)點(diǎn)。 |
TCP/UDP/8301 | 8301 端口用于單個數(shù)據(jù)中心所有節(jié)點(diǎn)之間的互相通信,即對 LAN 池信息的同步。它使得整個數(shù)據(jù)中心能夠自動發(fā)現(xiàn)服務(wù)器地址,分布式檢測節(jié)點(diǎn)故障,事件廣播(如領(lǐng)導(dǎo)選舉事件)。 |
TCP/UDP/8302 | 8302 端口用于單個或多個數(shù)據(jù)中心之間的服務(wù)器節(jié)點(diǎn)的信息同步,即對 WAN 池信息的同步。它針對互聯(lián)網(wǎng)的高延遲進(jìn)行了優(yōu)化,能夠?qū)崿F(xiàn)跨數(shù)據(jù)中心請求。 |
8500 | 8500 端口基于 HTTP 協(xié)議,用于 API 接口或 WEB UI 訪問。 |
8600 | 8600 端口作為 DNS 服務(wù)器,它使得我們可以通過節(jié)點(diǎn)名查詢節(jié)點(diǎn)信息。 |
consul 安全
consul 由于采用了 gossip 機(jī)制和 RPC 系統(tǒng)來提供功能。這兩種系統(tǒng)各自采用了不同的安全機(jī)制。其中 gossip 使用對稱密鑰提供加密,RPC 則可以使用客戶端認(rèn)證的端到端 TLS。
Gossip 加密
要使用 Gossip 加密需要在啟動 agent 的時(shí)候設(shè)置加密密鑰。可以通過配置文件中的 encrypt
參數(shù)進(jìn)行設(shè)置。
密鑰必須是 16 字節(jié),Base64 編碼。consul 為我們方便的提供了一鍵生成的命令:
$ consul keygen
cg8StVXbQJ0gPvMd9o7yrg==
如何使用?在啟動 agent 的時(shí)候,我們需要指定一個配置文件,配置文件中填入上面生成好的加密參數(shù):
$ cat config.json
{"encrypt": "cg8StVXbQJ0gPvMd9o7yrg=="}
$ consul agent -data-dir=/tmp/consul -config-file=config.json
==> WARNING: LAN keyring exists but -encrypt given, using keyring
==> WARNING: WAN keyring exists but -encrypt given, using keyring
==> Starting Consul agent...
==> Starting Consul agent RPC...
==> Consul agent running!
Node name: 'Armons-MacBook-Air.local'
Datacenter: 'dc1'
Server: false (bootstrap: false)
Client Addr: 127.0.0.1 (HTTP: 8500, HTTPS: -1, DNS: 8600, RPC: 8400)
Cluster Addr: 10.1.10.12 (LAN: 8301, WAN: 8302)
Gossip encrypt: true, RPC-TLS: false, TLS-Incoming: false
...
我們看到 Gossip encrypt: true
的打印,說明加密成功。
注意,consul 集群中的所有節(jié)點(diǎn)必須共享相同的加密密鑰才能發(fā)送和接收集群消息。
如何在已有的集群上配置 Gossip?
該功能需要在版本 0.8.4 之后才能夠使用,請悉知。
- 生成加密密鑰
consul keygen
。 - 操作集群中所有的節(jié)點(diǎn),在配置中設(shè)置
encrypt
鍵和encrypt_verify_incoming
,并將encrypt_verify_outgoing
設(shè)置為 false。如此,代理將能夠解密 gossip,但尚未發(fā)送加密流量。 - 操作集群中所有的節(jié)點(diǎn),刪除
encrypt_verify_outgoing
設(shè)置(恢復(fù)為 true)。agent 將發(fā)送加密的 gossip 消息,但任然允許傳入未加密的流量。 - 操作集群中所有的節(jié)點(diǎn),刪除
encrypt_verify_incoming
設(shè)置(恢復(fù)為 true)。所有的 agent 將嚴(yán)格執(zhí)行加密的 gossip。
RPC 加密
consul 支持使用 TLS 來驗(yàn)證服務(wù)器和客戶端的真實(shí)性。推薦使用私有 CA,僅在內(nèi)部使用。CA 的建立與 SSL 證書的辦法可以參考:基于 OpenSSL 自建 CA 和頒發(fā) SSL 證書。
客戶端證書必須啟用客戶端和服務(wù)器鑒定的 Extended Key Usage.。
TLS 可用于驗(yàn)證服務(wù)器和客戶端的真實(shí)性。這些模式由 verify_outgoing
,verify_server_hostname
以及 verify_incoming
控制。
verify_outgoing
,設(shè)置該參數(shù),agent 會驗(yàn)證出口連接。服務(wù)器節(jié)點(diǎn)必須提供由所有代理上存在的公共證書頒發(fā)機(jī)構(gòu)簽名的證書,通過 ca_file
和 ca_path
選項(xiàng)盡心設(shè)置。所有服務(wù)器節(jié)點(diǎn)都必須使用 cert_file
和使用適當(dāng)?shù)拿荑€對集合 key_file
。
verify_server_hostname
,則會進(jìn)行主機(jī)名驗(yàn)證。所有服務(wù)器必須具有有效的證書 server.<datacenter>.<domain>
,否則客戶端將拒絕握手。該配置在 0.5.1 后有效,可以有效杜絕中間人攻擊,新部署推薦為 true,舊部署依舊為 false。
verify_incoming
,服務(wù)器將驗(yàn)證傳入連接的真實(shí)性。所有客戶端必須使用 cert_file
和 key_file
。服務(wù)器也將禁止任何非 TLS 連接。要槍支客戶端使用 TLS,verify_outgoing
也必須設(shè)置。
TLS 用于保護(hù) agent 之間的 RPC 調(diào)用,但節(jié)點(diǎn)與節(jié)點(diǎn)之間的通信通過 UDP 完成并使用對稱密鑰進(jìn)行保護(hù)。具體請參閱上節(jié)。
如何在現(xiàn)有集群上配置 TLS?
- 為每個 agent 生成必要的私鑰和證書以配置 ca_file/ca_path,cert_file 和 key_file。確保
verify_outgoing
和verify_incoming
選項(xiàng)設(shè)置為 false。此時(shí)可以通過設(shè)置https
端口來啟用 API 的 HTTPS。 - 重新啟動集群中的每個 agent。此時(shí),TLS 應(yīng)該在每個 agent 上配置,但尚為啟用 TLS。
- (可選,僅限 Enterprise)
- 更改
verify_incoming
和verify_outgoing
(verify_server_hostname
如果啟用)設(shè)置 true。 - 重新啟動集群中的每個 agent。
此時(shí),RPC 通信均用過 TLS 加密。
初體驗(yàn)
本文的重點(diǎn)來了,讓我們一起實(shí)際動手搭建多數(shù)據(jù)中心的 consul 集群。
我們先來看一下我們需要搭建的集群架構(gòu):
首先準(zhǔn)備 5 臺相對獨(dú)立的服務(wù)器。這里測試,我通過 docker-machine
創(chuàng)建了 5 臺:
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
consul-client1 - virtualbox Running tcp://192.168.99.112:2376 v17.07.0-ce
consul-client2 - virtualbox Running tcp://192.168.99.113:2376 v17.07.0-ce
consul-server1 - virtualbox Running tcp://192.168.99.109:2376 v17.07.0-ce
consul-server2 - virtualbox Running tcp://192.168.99.110:2376 v17.07.0-ce
consul-server3 - virtualbox Running tcp://192.168.99.111:2376 v17.07.0-ce
可以看到創(chuàng)建的 5 臺機(jī)器的名稱與 IP。由于虛擬機(jī)內(nèi)部少了很多動態(tài)鏈接庫,因此采用運(yùn)行 docker 容器的方式,我們分別在每臺機(jī)器上 $ docker pull consul
。
consul
相關(guān)命令指南請到:Consul Commands (CLI)。
準(zhǔn)備我們需要的證書和加密密鑰并配置為配置文件:
# 來看看 server1 的目錄結(jié)構(gòu):
$ tree certs
├── certs
│ ├── ca.pem
│ ├── server1.key
│ └── server1.pem
├── config
│ └── config.json
├── data
└── run.sh
# server1 配置文件內(nèi)容
$ consul keygen
KizkyBSFh+Q03ATW0Nwcgg==
$ cat config/config.json
{
"datacenter": "dc1",
"data_dir": "/consul/data",
"server": true,
"node_name": "server1",
"log_level": "INFO",
"bind_addr": "192.168.99.109",
"bootstrap_expect": 3,
"addresses": {
"https": "0.0.0.0"
},
"ports": {
"https": 8080
},
"encrypt": "KizkyBSFh+Q03ATW0Nwcgg==",
"verify_incoming": true,
"verify_outgoing": true,
"ca_file": "/consul/certs/ca.pem",
"cert_file": "/consul/certs/server1.pem",
"key_file": "/consul/certs/server1.key"
}
需要說明的是,在 config.json -> ports
中端口設(shè)置為 -1
即為禁用:
"ports": {
"https": 8080,
"http": -1
}
這樣我們就不能夠訪問 HTTP API
了。當(dāng)然開啟也是沒有問題的,因?yàn)樗J(rèn)監(jiān)聽的是 127.0.0.1:8500
,只有本地能夠連接使用。
首先,我們先來驗(yàn)證共識協(xié)議,看一看選舉 leader 的過程。
# 進(jìn)入服務(wù)器 server1
$ docker-machine ssh consul-server1
# 創(chuàng)建 server1
$ docker run -d --net=host \
--name server1 \
-v $PWD/data:/consul/data \
-v $PWD/config:/consul/config \
-v $PWD/certs:/consul/certs \
consul \
consul agent -config-file=/consul/config
這種方式,-bootstrap-expect 3
期待三個 server 加入才能完成 consul 的引導(dǎo)。繼續(xù)添加 server2、server3:
# server2
docker run -d --net=host \
--name server2 \
-v $PWD/data:/consul/data \
-v $PWD/config:/consul/config \
-v $PWD/certs:/consul/certs \
consul \
consul agent -config-file=/consul/config \
-join=192.168.99.109
# server3
docker run -d --net=host \
--name server3 \
-v $PWD/data:/consul/data \
-v $PWD/config:/consul/config \
-v $PWD/certs:/consul/certs \
consul \
consul agent -config-file=/consul/config \
-join=192.168.99.109
再次查看信息,發(fā)現(xiàn) server1 成為了 leader。停掉 leader,server2 成為了 leader。通過 $ consul info
獲取相關(guān)信息:
$ docker run consul info -http-addr=192.168.99.109
Error querying agent: Get https://192.168.252.135:8501/v1/agent/self: remote error: tls: bad certificate
# 看來需要指定證書才行
$ docker run --rm --net=host consul \
consul info -http-addr=192.168.99.109 \
-ca-file=/consul/certs/ca.pem \
-client-cert=/consul/certs/server1.pem \
-client-key=/consul/certs/server1.key
我們還看到所有節(jié)點(diǎn)信息加入到了 LAN
池和 WAN
池:
2017/09/06 09:13:25 [INFO] consul: Adding LAN server server3 (Addr: tcp/192.168.99.111:8300) (DC: dc1)
...
2017/09/06 09:13:25 [INFO] consul: Handled member-join event for server "server2.dc1" in area "wan"
...
現(xiàn)在我們有了 3 臺服務(wù)器節(jié)點(diǎn):
$ docker run --rm --net=host consul \
consul members \
-ca-file=/consul/certs/ca.pem \
-client-cert=/consul/certs/server1.pem \
-client-key=/consul/certs/server1.key
Node Address Status Type Build Protocol DC
server1 192.168.99.109:8301 alive server 0.9.2 2 dc1
server2 192.168.99.110:8301 alive server 0.9.2 2 dc1
server3 192.168.99.111:8301 alive server 0.9.2 2 dc1
WEB UI
WEB UI 也是通過 consul agent
啟動,只需要再啟動的時(shí)候加上 -ui
,所以我們可以直接 下載 二進(jìn)制文件:
$ docker run -d --net=host \
--name server1 \
-v $PWD/data:/consul/data \
-v $PWD/config:/consul/config \
-v $PWD/certs:/consul/certs \
consul \
consul agent -ui -config-file=/consul/config
啟動成功后,就可以通過 https://192.168.99.109:8500/ui
進(jìn)行訪問了:
服務(wù)發(fā)現(xiàn)
consul 支持兩種服務(wù)發(fā)現(xiàn)的方式:
- 通過 HTTP API 方式,這種方式需要額外編程,適用于不安裝 consul agent 的情況,文檔地址。
- 通過 consul agent 配置的方式,agent 啟動的時(shí)候會讀取一個配置文件目錄,通過配置進(jìn)行服務(wù)的發(fā)現(xiàn),文檔地址。
這里介紹第二種方式,通過配置文件來進(jìn)行服務(wù)發(fā)現(xiàn)。這里就需要用到我們的 client 服務(wù)器啦。
首先,用 Go 寫一個簡單的 HTTP 服務(wù)器:
package main
import (
"fmt"
"net/http"
)
func HandleExample(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello man"))
}
func HandleHealth(w http.ResponseWriter, r *http.Request) {
fmt.Println("health check!")
}
func main() {
http.HandleFunc("/", HandleExample)
http.HandleFunc("/health", HandleHealth)
fmt.Println("listen on :9000")
http.ListenAndServe(":9000", nil)
}
然后編輯一個配置文件 $PWD/config/web.json
:
{
"service":
{
"name": "web",
"tags": ["primary"],
"address": "192.168.99.112",
"port": 9000,
"checks": [
{
"http": "http://localhost:9000/health",
"interval": "10s"
}]
}
}
checks 的文檔在 這里。
啟動一個客戶端 agent:
docker run -d --net=host \
--name clent1 \
-v $PWD/data:/consul/data \
-v $PWD/config:/consul/config \
-v $PWD/certs:/consul/certs \
consul \
consul agent -config-dir=/consul/config \
-join=192.168.99.109
啟動之后,我們可以在服務(wù)器 server 上查看是否進(jìn)行同步了。有兩種方式:
HTTP API 查看,可通過 List Nodes for Service。
-
DNS 查詢,每個 agent 都會啟動一個 DNS 服務(wù)器,通過該 DNS 服務(wù)器我們可以查詢到節(jié)點(diǎn)信息。如:
$ dig @127.0.0.1 -p 8600 web3.service.consul SRV ... ;; ANSWER SECTION: web.service.consul. 0 IN A 192.168.99.112 ...
配置共享
由與有了 agent client 和 server 模式的提供,配置共享也變得異常的簡單。
在任意節(jié)點(diǎn)更新配置數(shù)據(jù):
$ consul kv put redis/config 192.168.99.133
Success! Data written to: redis/config
整個集群均會自動更新,在 server1 節(jié)點(diǎn)查看數(shù)據(jù):
$ consul kv get redis/config
192.168.99.133
ok,沒有問題。更多更好的應(yīng)用場景等你發(fā)掘。