https://deepzz.com/post/the-consul-of-discovery-and-configure-services.html
consul 是一個(gè)支持多數(shù)據(jù)中心分布式高可用,用于服務(wù)發(fā)現(xiàn)和配置共享的工具。那么,之前已經(jīng)有了眾多的用于服務(wù)發(fā)現(xiàn)和配置的工具,如:etcd、zookeeper 等,為什么還冒出來(lái)個(gè) consul?閱讀 Consul vs. Other Software 或許你可以找到答案。
consul 關(guān)鍵特性
- 服務(wù)發(fā)現(xiàn):支持服務(wù)發(fā)現(xiàn)。你可以通過(guò) DNS 或 HTTP 的方式獲取服務(wù)信息。
- 健康檢查:支持健康檢查。可以提供與給定服務(wù)相關(guān)聯(lián)的任何數(shù)量的健康檢查(如 web 狀態(tài)碼或 cpu 使用率)。
- K/V 存儲(chǔ):鍵/值對(duì)存儲(chǔ)。你可用通過(guò) consul 存儲(chǔ)如動(dòng)態(tài)配置之類(lèi)的相關(guān)信息。
- 多數(shù)據(jù)中心:支持多數(shù)據(jù)中心,開(kāi)箱即用。
-
WEB UI:支持 WEB UI。點(diǎn)點(diǎn)點(diǎn),你就能夠了解你的服務(wù)現(xiàn)在的運(yùn)行情況,一目了然,對(duì)開(kāi)發(fā)運(yùn)維是非常友好的。
consul_web_ui
consul 相較與 etcd、zookeeper 最大的特點(diǎn)就是:它整合了用戶普遍的需求,開(kāi)箱即用,降低了使用的門(mén)檻。
consul 術(shù)語(yǔ)
首先介紹下在 consul 中會(huì)經(jīng)常見(jiàn)到的術(shù)語(yǔ):
-
node
:節(jié)點(diǎn),需要 consul 注冊(cè)發(fā)現(xiàn)或配置管理的服務(wù)器。 -
agent
:consul 中的核心程序,它將以守護(hù)進(jìn)程的方式在各個(gè)節(jié)點(diǎn)運(yùn)行,有 client 和 server 啟動(dòng)模式。每個(gè) agent 維護(hù)一套服務(wù)和注冊(cè)發(fā)現(xiàn)以及健康信息。 -
client
:agent 以 client 模式啟動(dòng)的節(jié)點(diǎn)。在該模式下,該節(jié)點(diǎn)會(huì)采集相關(guān)信息,通過(guò) RPC 的方式向 server 發(fā)送。 -
server
:agent 以 server 模式啟動(dòng)的節(jié)點(diǎn)。一個(gè)數(shù)據(jù)中心中至少包含 1 個(gè) server 節(jié)點(diǎn)。不過(guò)官方建議使用 3 或 5 個(gè) server 節(jié)點(diǎn)組建成集群,以保證高可用且不失效率。server 節(jié)點(diǎn)參與 Raft、維護(hù)會(huì)員信息、注冊(cè)服務(wù)、健康檢查等功能。 -
datacenter
:數(shù)據(jù)中心,私有的,低延遲的和高帶寬的網(wǎng)絡(luò)環(huán)境。一般的多個(gè)數(shù)據(jù)中心之間的數(shù)據(jù)是不會(huì)被復(fù)制的,但可用過(guò) ACL replication 或使用外部工具 onsul-replicate。 -
Consensus
,共識(shí)協(xié)議,使用它來(lái)協(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ù)中心,通常通過(guò)互聯(lián)網(wǎng)或廣域網(wǎng)進(jìn)行通信。 -
members
:成員,對(duì) consul 成員的稱(chēng)呼。提供會(huì)員資格,故障檢測(cè)和事件廣播。有興趣的朋友可以深入研究下。
consul 架構(gòu)
consul 的架構(gòu)是什么,官方給出了一個(gè)很直觀的圖片:
這里存在兩個(gè)數(shù)據(jù)中心:DATACENTER1、DATACENTER2。每個(gè)數(shù)據(jù)中心有著 3 到 5 臺(tái) server(該數(shù)量使得在故障轉(zhuǎn)移和性能之間達(dá)到平衡)。
單個(gè)數(shù)據(jù)中心的所有節(jié)點(diǎn)都參與 LAN Gossip
池,也就是說(shuō)該池包含了這個(gè)數(shù)據(jù)中心的所有節(jié)點(diǎn)。這有幾個(gè)目的:
- 不需要給客戶端配置服務(wù)器地址,發(fā)現(xiàn)自動(dòng)完成。
- 檢測(cè)節(jié)點(diǎn)故障的工作不是放在服務(wù)器上,而是分布式的。這使得故障檢測(cè)比心跳方案更具可擴(kuò)展性。
- 事件廣播,以便在諸如領(lǐng)導(dǎo)選舉等重要事件發(fā)生時(shí)通知。
所有 server 節(jié)點(diǎn)也單獨(dú)加入 WAN Gossip
池,因?yàn)樗槍?duì)互聯(lián)網(wǎng)的高延遲進(jìn)行了優(yōu)化。這個(gè)池的目的是允許數(shù)據(jù)中心以低調(diào)的方式發(fā)現(xiàn)對(duì)方。在線啟動(dòng)新的數(shù)據(jù)中心與加入現(xiàn)有的 WAN Gossip
一樣簡(jiǎn)單。因?yàn)檫@些服務(wù)器都在這個(gè)池中運(yùn)行,所以它也支持跨數(shù)據(jù)中心的請(qǐng)求。當(dāng)服務(wù)器收到對(duì)不同數(shù)據(jù)中心的請(qǐng)求時(shí),它會(huì)將其轉(zhuǎn)發(fā)到正確數(shù)據(jù)中心中的隨機(jī)服務(wù)器。那個(gè)服務(wù)器可能會(huì)轉(zhuǎn)發(fā)給當(dāng)?shù)氐念I(lǐng)導(dǎo)。
這導(dǎo)致數(shù)據(jù)中心之間的耦合非常低,但是由于故障檢測(cè),連接緩存和復(fù)用,跨數(shù)據(jù)中心請(qǐng)求相對(duì)快速可靠。
一般來(lái)說(shuō),數(shù)據(jù)不會(huì)在不同的領(lǐng)事數(shù)據(jù)中心之間復(fù)制。當(dāng)對(duì)另一數(shù)據(jù)中心的資源進(jìn)行請(qǐng)求時(shí),本地 consul 服務(wù)器將
RPC 請(qǐng)求轉(zhuǎn)發(fā)給該資源的遠(yuǎn)程 consul 服務(wù)器并返回結(jié)果。如果遠(yuǎn)程數(shù)據(jù)中心不可用,那么這些資源也將不可用,但這不會(huì)影響本地?cái)?shù)據(jù)中心。有一些特殊情況可以復(fù)制有限的數(shù)據(jù)子集,例如使用 consul 內(nèi)置的 ACL replication 功能,或外部工具如 consul-replicate。
更多協(xié)議詳情,你可以 Consensus Protocol 和 Gossip Protocol。
consul 端口說(shuō)明
consul 內(nèi)使用了很多端口,理解這些端口的用處對(duì)你理解 consul 架構(gòu)很有幫助:
端口 | 說(shuō)明 |
---|---|
TCP/8300 | 8300 端口用于服務(wù)器節(jié)點(diǎn)。客戶端通過(guò)該端口 RPC 協(xié)議調(diào)用服務(wù)端節(jié)點(diǎn)。 |
TCP/UDP/8301 | 8301 端口用于單個(gè)數(shù)據(jù)中心所有節(jié)點(diǎn)之間的互相通信,即對(duì) LAN 池信息的同步。它使得整個(gè)數(shù)據(jù)中心能夠自動(dòng)發(fā)現(xiàn)服務(wù)器地址,分布式檢測(cè)節(jié)點(diǎn)故障,事件廣播(如領(lǐng)導(dǎo)選舉事件)。 |
TCP/UDP/8302 | 8302 端口用于單個(gè)或多個(gè)數(shù)據(jù)中心之間的服務(wù)器節(jié)點(diǎn)的信息同步,即對(duì) WAN 池信息的同步。它針對(duì)互聯(lián)網(wǎng)的高延遲進(jìn)行了優(yōu)化,能夠?qū)崿F(xiàn)跨數(shù)據(jù)中心請(qǐng)求。 |
8500 | 8500 端口基于 HTTP 協(xié)議,用于 API 接口或 WEB UI 訪問(wèn)。 |
8600 | 8600 端口作為 DNS 服務(wù)器,它使得我們可以通過(guò)節(jié)點(diǎn)名查詢節(jié)點(diǎn)信息。 |
consul 安全
consul 由于采用了 gossip 機(jī)制和 RPC 系統(tǒng)來(lái)提供功能。這兩種系統(tǒng)各自采用了不同的安全機(jī)制。其中 gossip 使用對(duì)稱(chēng)密鑰提供加密,RPC 則可以使用客戶端認(rèn)證的端到端 TLS。
Gossip 加密
要使用 Gossip 加密需要在啟動(dòng) agent 的時(shí)候設(shè)置加密密鑰??梢酝ㄟ^(guò)配置文件中的 encrypt
參數(shù)進(jìn)行設(shè)置。
密鑰必須是 16 字節(jié),Base64 編碼。consul 為我們方便的提供了一鍵生成的命令:
$ consul keygen
cg8StVXbQJ0gPvMd9o7yrg==
如何使用?在啟動(dòng) agent 的時(shí)候,我們需要指定一個(gè)配置文件,配置文件中填入上面生成好的加密參數(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
的打印,說(shuō)明加密成功。
注意,consul 集群中的所有節(jié)點(diǎn)必須共享相同的加密密鑰才能發(fā)送和接收集群消息。
如何在已有的集群上配置 Gossip?
該功能需要在版本 0.8.4 之后才能夠使用,請(qǐng)悉知。
- 生成加密密鑰
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 來(lái)驗(yàn)證服務(wù)器和客戶端的真實(shí)性。推薦使用私有 CA,僅在內(nèi)部使用。CA 的建立與 SSL 證書(shū)的辦法可以參考:基于 OpenSSL 自建 CA 和頒發(fā) SSL 證書(shū)。
客戶端證書(shū)必須啟用客戶端和服務(wù)器鑒定的 Extended Key Usage.。
TLS 可用于驗(yàn)證服務(wù)器和客戶端的真實(shí)性。這些模式由 verify_outgoing
,verify_server_hostname
以及 verify_incoming
控制。
verify_outgoing
,設(shè)置該參數(shù),agent 會(huì)驗(yàn)證出口連接。服務(wù)器節(jié)點(diǎn)必須提供由所有代理上存在的公共證書(shū)頒發(fā)機(jī)構(gòu)簽名的證書(shū),通過(guò) ca_file
和 ca_path
選項(xiàng)盡心設(shè)置。所有服務(wù)器節(jié)點(diǎn)都必須使用 cert_file
和使用適當(dāng)?shù)拿荑€對(duì)集合 key_file
。
verify_server_hostname
,則會(huì)進(jìn)行主機(jī)名驗(yàn)證。所有服務(wù)器必須具有有效的證書(shū) 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)之間的通信通過(guò) UDP 完成并使用對(duì)稱(chēng)密鑰進(jìn)行保護(hù)。具體請(qǐng)參閱上節(jié)。
如何在現(xiàn)有集群上配置 TLS?
- 為每個(gè) agent 生成必要的私鑰和證書(shū)以配置 ca_file/ca_path,cert_file 和 key_file。確保
verify_outgoing
和verify_incoming
選項(xiàng)設(shè)置為 false。此時(shí)可以通過(guò)設(shè)置https
端口來(lái)啟用 API 的 HTTPS。 - 重新啟動(dòng)集群中的每個(gè) agent。此時(shí),TLS 應(yīng)該在每個(gè) agent 上配置,但尚為啟用 TLS。
- (可選,僅限 Enterprise)
- 更改
verify_incoming
和verify_outgoing
(verify_server_hostname
如果啟用)設(shè)置 true。 - 重新啟動(dòng)集群中的每個(gè) agent。
此時(shí),RPC 通信均用過(guò) TLS 加密。
初體驗(yàn)
本文的重點(diǎn)來(lái)了,讓我們一起實(shí)際動(dòng)手搭建多數(shù)據(jù)中心的 consul 集群。
我們先來(lái)看一下我們需要搭建的集群架構(gòu):
首先準(zhǔn)備 5 臺(tái)相對(duì)獨(dú)立的服務(wù)器。這里測(cè)試,我通過(guò) docker-machine
創(chuàng)建了 5 臺(tái):
$ 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 臺(tái)機(jī)器的名稱(chēng)與 IP。由于虛擬機(jī)內(nèi)部少了很多動(dòng)態(tài)鏈接庫(kù),因此采用運(yùn)行 docker 容器的方式,我們分別在每臺(tái)機(jī)器上 $ docker pull consul
。
consul
相關(guān)命令指南請(qǐng)到:Consul Commands (CLI)。
準(zhǔn)備我們需要的證書(shū)和加密密鑰并配置為配置文件:
# 來(lái)看看 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"
}
需要說(shuō)明的是,在 config.json -> ports
中端口設(shè)置為 -1
即為禁用:
"ports": {
"https": 8080,
"http": -1
}
這樣我們就不能夠訪問(wèn) HTTP API
了。當(dāng)然開(kāi)啟也是沒(méi)有問(wèn)題的,因?yàn)樗J(rèn)監(jiān)聽(tīng)的是 127.0.0.1:8500
,只有本地能夠連接使用。
首先,我們先來(lái)驗(yàn)證共識(shí)協(xié)議,看一看選舉 leader 的過(guò)程。
# 進(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
期待三個(gè) 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。通過(guò) $ 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
# 看來(lái)需要指定證書(shū)才行
$ 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 臺(tái)服務(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 也是通過(guò) consul agent
啟動(dòng),只需要再啟動(dòng)的時(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
啟動(dòng)成功后,就可以通過(guò) https://192.168.99.109:8500/ui
進(jìn)行訪問(wèn)了:
服務(wù)發(fā)現(xiàn)
consul 支持兩種服務(wù)發(fā)現(xiàn)的方式:
- 通過(guò) HTTP API 方式,這種方式需要額外編程,適用于不安裝 consul agent 的情況,文檔地址。
- 通過(guò) consul agent 配置的方式,agent 啟動(dòng)的時(shí)候會(huì)讀取一個(gè)配置文件目錄,通過(guò)配置進(jìn)行服務(wù)的發(fā)現(xiàn),文檔地址。
這里介紹第二種方式,通過(guò)配置文件來(lái)進(jìn)行服務(wù)發(fā)現(xiàn)。這里就需要用到我們的 client 服務(wù)器啦。
首先,用 Go 寫(xiě)一個(gè)簡(jiǎn)單的 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)
}
然后編輯一個(gè)配置文件 $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 的文檔在 這里。
啟動(dòng)一個(gè)客戶端 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
啟動(dòng)之后,我們可以在服務(wù)器 server 上查看是否進(jìn)行同步了。有兩種方式:
HTTP API 查看,可通過(guò) List Nodes for Service。
-
DNS 查詢,每個(gè) agent 都會(huì)啟動(dòng)一個(gè) DNS 服務(wù)器,通過(guò)該 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ǎn)單。
在任意節(jié)點(diǎn)更新配置數(shù)據(jù):
$ consul kv put redis/config 192.168.99.133
Success! Data written to: redis/config
整個(gè)集群均會(huì)自動(dòng)更新,在 server1 節(jié)點(diǎn)查看數(shù)據(jù):
$ consul kv get redis/config
192.168.99.133
ok,沒(méi)有問(wèn)題。更多更好的應(yīng)用場(chǎng)景等你發(fā)掘。