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的幾個概念
上圖來自於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,同時也要負責各個節點的健康監測。
其它資訊其它資訊包括它們之間的通訊方式,還有一些協議資訊,演算法。它們是用於保證節點之間的資料同步,實時性要求等等一系列叢集問題的解決。這些有興趣的自己看看官方文件。
四、安裝 Consul
Consul 下載地址:https://www.consul.io/downloads.html,下載後解壓就是一個可執行的二進位制檔案consul
,配置好環境變數,檢查 consul 是否可用:
[[email protected] ~]# consul
Usage: consul [--version] [--help] <command> [<args>]
Available commands are:
agent Runs a Consul agent
catalog Interact with the catalog
event Fire a new event
exec Executes a command on Consul nodes
force-leave Forces a member of the cluster to enter the "left" state
info Provides debugging information for operators.
join Tell Consul agent to join cluster
keygen Generates a new encryption key
keyring Manages gossip layer encryption keys
kv Interact with the key-value store
leave Gracefully leaves the Consul cluster and shuts down
lock Execute a command holding a lock
maint Controls node or service maintenance mode
members Lists the members of a Consul cluster
monitor Stream logs from a Consul agent
operator Provides cluster-level tools for Consul operators
reload Triggers the agent to reload configuration files
rtt Estimates network round trip time between nodes
snapshot Saves, restores and inspects snapshots of Consul server state
validate Validate config files/directories
version Prints the Consul version
watch Watch for changes in Consul
如果出現上面這樣代表consul
是沒問題的。
五、執行 Consul Agent
Consul安裝之後,代理必須執行。 代理可以在伺服器或客戶端模式下執行。 每個資料中心都必須至少有一臺伺服器,但推薦使用3臺或5臺伺服器。 一個單一的伺服器部署是非常不推薦的,因為在故障情況下資料丟失是不可避免的。
所有其他代理以客戶端模式執行。 客戶端是一個非常輕量級的程序,它註冊服務,執行健康檢查,並將查詢轉發給伺服器。 代理程式必須在叢集中的每個節點上執行。
為了簡單起見,我們現在將以開發模式啟動Consul代理。 這種模式對於快速簡單地啟動單節點Consul環境非常有用。 它並不打算在生產中使用,因為它不會持續任何狀態。
[[email protected] consul]# consul agent -dev
==> Starting Consul agent...
==> Consul agent running!
Version: 'v1.0.1'
Node ID: '590309a6-71f6-6145-fe40-d2c5e203687f'
Node name: 'localhost.localdomain'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false
==> Log data will now stream in as it occurs:
2017/11/25 15:15:54 [DEBUG] Using random ID "590309a6-71f6-6145-fe40-d2c5e203687f" as node ID
2017/11/25 15:15:54 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:590309a6-71f6-6145-fe40-d2c5e203687f Address:127.0.0.1:8300}]
2017/11/25 15:15:54 [INFO] serf: EventMemberJoin: localhost.localdomain.dc1 127.0.0.1
2017/11/25 15:15:54 [INFO] serf: EventMemberJoin: localhost.localdomain 127.0.0.1
2017/11/25 15:15:54 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp)
2017/11/25 15:15:54 [INFO] raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
2017/11/25 15:15:54 [INFO] consul: Adding LAN server localhost.localdomain (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2017/11/25 15:15:54 [INFO] consul: Handled member-join event for server "localhost.localdomain.dc1" in area "wan"
2017/11/25 15:15:54 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp)
2017/11/25 15:15:54 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp)
2017/11/25 15:15:54 [INFO] agent: started state syncer
2017/11/25 15:15:54 [WARN] raft: Heartbeat timeout from "" reached, starting election
2017/11/25 15:15:54 [INFO] raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
2017/11/25 15:15:54 [DEBUG] raft: Votes needed: 1
2017/11/25 15:15:54 [DEBUG] raft: Vote granted from 590309a6-71f6-6145-fe40-d2c5e203687f in term 2. Tally: 1
2017/11/25 15:15:54 [INFO] raft: Election won. Tally: 1
2017/11/25 15:15:54 [INFO] raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
2017/11/25 15:15:54 [INFO] consul: cluster leadership acquired
2017/11/25 15:15:54 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
2017/11/25 15:15:54 [INFO] consul: member 'localhost.localdomain' joined, marking health alive
2017/11/25 15:15:54 [INFO] consul: New leader elected: localhost.localdomain
2017/11/25 15:15:54 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 15:15:54 [INFO] agent: Synced node info
2017/11/25 15:15:54 [DEBUG] agent: Node info in sync
2017/11/25 15:15:57 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 15:15:57 [DEBUG] agent: Node info in sync
2017/11/25 15:16:54 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
2017/11/25 15:17:51 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 15:17:51 [DEBUG] agent: Node info in sync
2017/11/25 15:17:54 [DEBUG] manager: Rebalanced 1 servers, next active server is localhost.localdomain.dc1 (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2017/11/25 15:17:54 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
如您所見,Consul代理已經啟動並輸出了一些日誌資料。 從日誌資料中,您可以看到我們的代理正在伺服器模式下執行,並聲稱擁有叢集領導權。 此外,當地成員已被標記為該叢集的健康成員。
六、叢集成員
在另一個終端執行consul members
,可以看到Consul叢集的成員。 應該只看到一個成員(你自己):
[[email protected] ~]# consul members
Node Address Status Type Build Protocol DC Segment
localhost.localdomain 127.0.0.1:8301 alive server 1.0.1 2 dc1 <all>
輸出顯示了我們自己的節點,它正在執行的地址,執行狀況,在叢集中的角色以及一些版本資訊。 額外的元資料可以通過提供-detailed
標誌來檢視。
[[email protected] ~]# consul members -detailed
Node Address Status Tags
localhost.localdomain 127.0.0.1:8301 alive build=1.0.1:9564c29,dc=dc1,id=590309a6-71f6-6145-fe40-d2c5e203687f,port=8300,raft_vsn=3,role=consul,segment=<all>,vsn=2,vsn_max=3,vsn_min=2,wan_join_port=8302
members
命令的輸出基於gossip協議,並最終一致。 也就是說,在任何時候,當地代理所看到的可能與伺服器上的狀態不完全一致。 要獲得完全一致,請使用HTTP API再將HTTP請求轉發給Consul伺服器:
[[email protected] ~]# curl localhost:8500/v1/catalog/nodes
[
{
"ID": "590309a6-71f6-6145-fe40-d2c5e203687f",
"Node": "localhost.localdomain",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"Meta": {
"consul-network-segment": ""
},
"CreateIndex": 5,
"ModifyIndex": 6
}
]
除了HTTP API之外,還可以使用DNS介面查詢節點。 請注意,必須確保將DNS查詢預設情況下指向在埠8600上執行的Consul代理的DNS伺服器。 DNS條目的格式(如“Armons-MacBook-Air.node.consul”)將在後面詳細介紹。
[[email protected] ~]# dig @127.0.0.1 -p 8600 localhost.localdomain.node.consul
'
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 localhost.localdomain.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43915
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;localhost.localdomain.node.consul. IN A
;; ANSWER SECTION:
localhost.localdomain.node.consul. 0 IN A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 15:39:49 CST 2017
;; MSG SIZE rcvd: 78
七、停止 Agent
可以使用Ctrl-C(中斷訊號)正常停止代理。 中斷代理之後,您應該看到它離開叢集並關閉。
通過優雅地離開,Consul通知其他叢集成員該節點離開。 如果強行殺死了代理程序,則叢集的其他成員將檢測到該節點失敗。 成員離開時,其服務和檢查將從目錄中刪除。 當一個成員失敗時,其健康被簡單地標記為關鍵,但不會從目錄中刪除。 Consul將自動嘗試重新連線到失敗的節點,使其能夠從特定的網路條件恢復,而不再聯絡離開的節點。
此外,如果代理正在作為伺服器執行,那麼優雅的離開對於避免造成影響一致協議的潛在可用性中斷很重要。 有關如何安全地新增和刪除伺服器的詳細資訊,請參閱指南部分。
八、註冊服務
1、服務定義
服務可以通過提供服務定義或通過對HTTP API進行適當的呼叫來註冊。
服務定義是註冊服務最常用的方式,所以我們將在這一步中使用這種方法。 我們將建立在上一步中介紹的代理配置。
首先,為Consul配置建立一個目錄。 Consul將所有配置檔案載入到配置目錄中,因此Unix系統上的一個通用約定是將目錄命名為/etc/consul.d(.d字尾意味著“該目錄包含一組配置檔案”)。
[[email protected] etc]# sudo mkdir /etc/consul.d
接下來,我們將編寫一個服務定義配置檔案。 假設我們有一個名為“web”的服務在埠80上執行。另外,我們給它一個標籤,我們可以使用它作為查詢服務的附加方式:
[[email protected] ~]# echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' | sudo tee /etc/consul.d/web.json
現在,重新啟動代理程式,提供配置目錄:
[[email protected] ~]# consul agent -dev -config-dir=/etc/consul.d
==> Starting Consul agent...
==> Consul agent running!
Version: 'v1.0.1'
Node ID: '94236f1c-2a29-85c5-b235-dd916485be5b'
Node name: 'localhost.localdomain'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false
==> Log data will now stream in as it occurs:
2017/11/25 16:16:51 [DEBUG] Using random ID "94236f1c-2a29-85c5-b235-dd916485be5b" as node ID
2017/11/25 16:16:51 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:94236f1c-2a29-85c5-b235-dd916485be5b Address:127.0.0.1:8300}]
2017/11/25 16:16:51 [INFO] serf: EventMemberJoin: localhost.localdomain.dc1 127.0.0.1
2017/11/25 16:16:51 [INFO] serf: EventMemberJoin: localhost.localdomain 127.0.0.1
2017/11/25 16:16:51 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp)
2017/11/25 16:16:51 [INFO] raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
2017/11/25 16:16:51 [INFO] consul: Adding LAN server localhost.localdomain (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2017/11/25 16:16:51 [INFO] consul: Handled member-join event for server "localhost.localdomain.dc1" in area "wan"
2017/11/25 16:16:51 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp)
2017/11/25 16:16:51 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp)
2017/11/25 16:16:51 [INFO] agent: started state syncer
2017/11/25 16:16:52 [WARN] raft: Heartbeat timeout from "" reached, starting election
2017/11/25 16:16:52 [INFO] raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
2017/11/25 16:16:52 [DEBUG] raft: Votes needed: 1
2017/11/25 16:16:52 [DEBUG] raft: Vote granted from 94236f1c-2a29-85c5-b235-dd916485be5b in term 2. Tally: 1
2017/11/25 16:16:52 [INFO] raft: Election won. Tally: 1
2017/11/25 16:16:52 [INFO] raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
2017/11/25 16:16:52 [INFO] consul: cluster leadership acquired
2017/11/25 16:16:52 [DEBUG] consul: Skipping self join check for "localhost.localdomain" since the cluster is too small
2017/11/25 16:16:52 [INFO] consul: member 'localhost.localdomain' joined, marking health alive
2017/11/25 16:16:52 [INFO] consul: New leader elected: localhost.localdomain
2017/11/25 16:16:52 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 16:16:52 [INFO] agent: Synced service "web"
2017/11/25 16:16:52 [DEBUG] agent: Node info in sync
2017/11/25 16:16:52 [DEBUG] agent: Service "web" in sync
2017/11/25 16:16:52 [DEBUG] agent: Node info in sync
2017/11/25 16:16:52 [DEBUG] Skipping remote check "serfHealth" since it is managed automatically
2017/11/25 16:16:52 [DEBUG] agent: Service "web" in sync
2017/11/25 16:16:52 [DEBUG] agent: Node info in sync
您會注意到它在輸出中“同步”了Web服務。 這意味著代理程式從配置檔案載入了服務定義,並已成功將其註冊到服務目錄中。
如果您想註冊多個服務,您可以在Consul配置目錄中建立多個服務定義檔案。
2、查詢服務
一旦代理啟動並且服務同步,我們可以使用DNS或HTTP API來查詢服務。
DNS API
我們首先使用DNS API來查詢我們的服務。 對於DNS API,服務的DNS名稱是NAME.service.consul。 預設情況下,所有DNS名稱始終在consul名稱空間中,儘管這是可配置的。 服務子域告訴Consul我們正在查詢服務,NAME是服務的名稱。
對於我們註冊的Web服務,這些約定和設定會生成web.service.consul的完全限定的域名:
[[email protected] ~]# dig @127.0.0.1 -p 8600 web.service.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58483
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN A
;; ANSWER SECTION:
web.service.consul. 0 IN A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 16:22:29 CST 2017
;; MSG SIZE rcvd: 63
正如你所看到的,一個A記錄返回了服務可用的節點的IP地址。 A記錄只能儲存IP地址。
您也可以使用DNS API來檢索整個地址/埠對作為SRV記錄:
[[email protected] ~]# dig @127.0.0.1 -p 8600 web.service.consul SRV
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65288
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN SRV
;; ANSWER SECTION:
web.service.consul. 0 IN SRV 1 1 80 localhost.localdomain.node.dc1.consul.
;; ADDITIONAL SECTION:
localhost.localdomain.node.dc1.consul. 0 IN A 127.0.0.1
localhost.localdomain.node.dc1.consul. 0 IN TXT "consul-network-segment="
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 16:25:21 CST 2017
;; MSG SIZE rcvd: 156
SRV記錄表示Web服務正在埠80上執行,並且存在於節點localhost.localdomain.node.dc1.consul.上。DNS使用該記錄的A記錄返回附加部分。
最後,我們也可以使用DNS API來按標籤過濾服務。 基於標記的服務查詢的格式是TAG.NAME.service.consul。 在下面的例子中,我們向Consul詢問所有帶有“rails”標籤的web服務。 自從我們使用該標籤註冊我們的服務後,我們得到了成功的迴應:
[[email protected] ~]# dig @127.0.0.1 -p 8600 rails.web.service.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 rails.web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41016
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;rails.web.service.consul. IN A
;; ANSWER SECTION:
rails.web.service.consul. 0 IN A 127.0.0.1
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 16:29:10 CST 2017
;; MSG SIZE rcvd: 69
HTTP API
除了DNS API之外,HTTP API還可以用來查詢服務:
[[email protected] ~]# curl http://localhost:8500/v1/catalog/service/web
[
{
"ID": "94236f1c-2a29-85c5-b235-dd916485be5b",
"Node": "localhost.localdomain",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"NodeMeta": {
"consul-network-segment": ""
},
"ServiceID": "web",
"ServiceName": "web",
"ServiceTags": [
"rails"
],
"ServiceAddress": "",
"ServicePort": 80,
"ServiceEnableTagOverride": false,
"CreateIndex": 6,
"ModifyIndex": 6
}
]
目錄API提供了託管給定服務的所有節點。 正如我們稍後將看到的健康檢查一樣,您通常只需要查詢檢查通過的健康例項。 這是DNS正在做的事情。 這是一個查詢只查詢健康的例項:
[[email protected] ~]# curl 'http://localhost:8500/v1/health/service/web?passing'
[
{
"Node": {
"ID": "94236f1c-2a29-85c5-b235-dd916485be5b",
"Node": "localhost.localdomain",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"wan": "127.0.0.1"
},
"Meta": {
"consul-network-segment": ""
},
"CreateIndex": 5,
"ModifyIndex": 6
},
"Service": {
"ID": "web",
"Service": "web",
"Tags": [
"rails"
],
"Address": "",
"Port": 80,
"EnableTagOverride": false,
"CreateIndex": 6,
"ModifyIndex": 6
},
"Checks": [
{
"Node": "localhost.localdomain",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
"Notes": "",
"Output": "Agent alive and reachable",
"ServiceID": "",
"ServiceName": "",
"ServiceTags": [],
"Definition": {},
"CreateIndex": 5,
"ModifyIndex": 5
}
]
}
]
3、更新服務
服務定義可以通過更改配置檔案並向代理髮送SIGHUP來更新。 這使您可以更新服務,而不會出現任何停機或無法提供服務查詢的情況。
或者,可以使用HTTP API動態地新增,刪除和修改服務。
九、Consul 叢集
我們已經開始了我們的第一個代理,並註冊和查詢該代理的服務。 這顯示了使用Consul是多麼的容易,但並沒有表明如何將其擴充套件到可擴充套件的生產級服務發現基礎設施。 在這一步中,我們將建立我們的第一個真正的叢集與多個成員。
當一個Consul代理啟動時,它不知道任何其他節點:它是一個孤立的叢集。 要了解其他叢集成員,代理必須加入現有叢集。 要加入現有的叢集,只需要知道一個現有的成員。 代理加入後,會與該成員通訊,並迅速發現叢集中的其他成員。 Consul代理可以加入任何其他代理,而不僅僅是伺服器模式下的代理。
1、啟動代理
在我們之前的例子中,我們使用了-dev標誌來快速設定一個開發伺服器。 但是,這不足以在叢集環境中使用。 我們將從這裡省略-dev標誌,而是指定我們的叢集標誌。
叢集中的每個節點都必須具有唯一的名稱。 預設情況下,Consul使用機器的主機名,但我們將使用-node
命令列選項手動覆蓋它。
我們還將指定一個-bind
:這是Consul偵聽的地址,它必須可以被叢集中的所有其他節點訪問。 雖然繫結地址不是絕對必要的,但最好提供一個。 Consul將預設嘗試偵聽系統上的所有IPv4介面,但如果找到多個私有IP,將無法啟動錯誤。 由於生產伺服器通常具有多個介面,因此指定一個繫結地址可確保您永遠不會將Consul繫結到錯誤的介面。
第一個節點將作為我們在這個叢集中唯一的伺服器,我們用-server
來指明這一點。
-bootstrap-expect
選項向Consul伺服器提示我們期望加入的其他伺服器節點的數量。 此選項的用途是延遲複製日誌的引導,直到預期數量的伺服器成功加入。
我們已經將-enable_script_checks
選項設定為true,以啟用可以執行外部指令碼的執行狀況檢查。 這將在後面的例子中使用。 對於生產用途,您希望將ACL配置為與此配合使用,以控制註冊任意指令碼的能力。
最後,我們新增-config-dir
選項,標記可以找到服務和檢查定義的位置。
總而言之,這些設定產生一個這樣的consul代理命令:
[[email protected] ~]# consul agent -server -bootstrap-expect=1 -data-dir=/tmp/consul -node=agent-one -bind=192.168.100.101 -enable-script-checks=true -config-dir=/etc/consul.d
現在,在另一個終端中,我們將連線到第二個節點。
這次,我們將-bind
設定為第二個節點的IP,並將-node
設定為agent-two。 由於這個節點不會是Consul伺服器,所以我們不提供-server
。
總而言之,這些設定產生一個這樣的consul代理命令:
[[email protected] ~]# consul agent -data-dir=/tmp/consul -node=agent-two -bind=192.168.100.102 -enable-script-checks=true -config-dir=/etc/consul.d
此時,您有兩個Consul代理正在執行:一個伺服器和一個客戶端。 兩個Consul代理人對彼此還是一無所知,都是他們自己的單節點叢集的一部分。 您可以通過對每個代理執行consul members
來驗證這一點,並注意到每個代理只能看到一個成員。
2、加入叢集
現在,我們將通過在新終端中執行以下命令來告訴第一個代理加入第二個代理:
[[email protected] ~]# consul join 192.168.100.101
Successfully joined cluster by contacting 1 nodes.
如果在虛擬機器裡面執行上面的命令提示下面的失敗的話,在每臺虛擬機器上執行下這個命令:
[[email protected] ~]# consul join 192.168.100.101
Error joining address '192.168.100.101': Unexpected response code: 500 (1 error(s) occurred:
* Failed to join 192.168.100.101: dial tcp 192.168.100.101:8301: getsockopt: no route to host)
Failed to join any nodes.
[[email protected] ~]# sudo iptables -F
您應該在每個代理日誌中看到一些日誌輸出。 如果仔細閱讀,您會看到他們收到了加入資訊。 如果你對每個代理執行consul members
,你會看到兩個代理人現在彼此瞭解:
[[email protected] ~]# consul members
Node Address Status Type Build Protocol DC Segment
agent-one 192.168.100.101:8301 alive server 1.0.1 2 dc1 <all>
agent-two 192.168.100.102:8301 alive client 1.0.1 2 dc1 <default>
記住:要加入叢集,Consul代理只需要瞭解一個現有的成員。 加入集群后,代理人互相傳播完整的會員資訊。
3、在啟動時自動加入叢集
理想情況下,每當新節點出現在您的資料中心時,它就會自動加入Consul叢集,無需人工干預。 Consul通過啟用AWS,Google Cloud或Azure中的例項的自動發現功能,使用給定的標籤 key/value來促進自動加入。 要使用整合,請將retry_join_ec2
,retry_join_gce
或retry_join_azure
巢狀物件新增到您的Consul配置檔案。 這將允許新的節點加入叢集,而不需要任何硬編碼的配置。 或者,您可以在啟動時使用-join
選項或start_join
設定以及其他已知Consul代理的硬編碼地址加入叢集。
4、查詢節點
就像查詢服務一樣,Consul也有查詢節點的API。 您可以通過DNS或HTTP API執行此操作。
對於DNS API,名稱的結構是NAME.node.consul或NAME.node.DATACENTER.consul。 如果資料中心被省略,Consul將僅搜尋本地資料中心。
例如,從“agent-one”中,我們可以查詢節點“agent-two”的地址:
[[email protected] etc]# dig @127.0.0.1 -p 8600 agent-two.node.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 agent-two.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57127
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;agent-two.node.consul. IN A
;; ANSWER SECTION:
agent-two.node.consul. 0 IN A 192.168.100.102
;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 六 11月 25 17:31:29 CST 2017
;; MSG SIZE rcvd: 66
除了服務之外,查詢節點的能力對於系統管理任務來說是非常有用的。 例如,知道要通過SSH連線的節點的地址與將節點作為Consul叢集的一部分並查詢它一樣簡單。
5、離開叢集
要離開叢集,可以正常退出代理(使用Ctrl-C)或強制終止其中一個代理。 優雅地離開允許節點轉換到離開狀態; 否則,其他節點將檢測到它失敗。
十、健康檢查
現在我們已經看到了執行Consul,新增節點和服務以及查詢這些節點和服務的簡單性。 在本節中,我們將介紹為節點和服務新增健康檢查。 健康檢查是服務發現的關鍵元件,可以防止使用不健康的服務。
此步驟建立在之前建立的Consul叢集上。 此時,您應該執行一個雙節點叢集。
1、檢查定義
與服務類似,可以通過提供檢查定義或通過對HTTP API進行適當的呼叫來註冊檢查。
我們將使用檢查定義方法,因為就像服務一樣,定義是設定檢查最常用的方法。
在第二個節點的Consul配置目錄中建立兩個定義檔案:
[[email protected] ~]# echo '{"check": {"name": "ping", "script": "ping -c1 google.com >/dev/null", "interval": "30s"}}' >/etc/consul.d/ping.json
[[email protected] ~]# echo '{"service": {"name": "web", "tags": ["rails"], "port": 80, "check": {"script": "curl localhost >/dev/null 2>&1", "interval": "10s"}}}' >/etc/consul.d/web.json
第一個定義添加了一個名為“ping”的主機級別的檢查。 此檢查執行間隔30秒,呼叫ping -c1 google.com
。 在基於指令碼的執行狀況檢查上,檢查以與啟動Consul程序相同的使用者身份執行。 如果該命令以非零退出碼退出,則該節點將被標記為不健康。 這是任何基於指令碼的健康檢查的約定。
第二個命令修改名為web的服務,新增一個檢查,每隔10秒通過curl傳送一個請求,以驗證Web伺服器是否可訪問。 與主機級執行狀況檢查一樣,如果指令碼以非零退出程式碼退出,服務將被標記為不健康。
現在,重新啟動第二個代理,用consul reload
載入它,或者傳送一個SIGHUP訊號。 您應該看到以下日誌行:
[[email protected] ~]# consul reload
Configuration reload triggered
2017/11/26 10:47:41 [INFO] Reloading configuration...
2017/11/26 10:47:41 [WARN] agent: check "service:web" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [WARN] agent: check "service:web" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [WARN] agent: check "ping" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [WARN] agent: check "ping" has the 'script' field, which has been deprecated and replaced with the 'args' field. See https://www.consul.io/docs/agent/checks.html
2017/11/26 10:47:41 [INFO] agent: Synced service "web"
2017/11/26 10:47:41 [INFO] agent: Synced check "ping"
2017/11/26 10:47:47 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:47:57 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:48:04 [WARN] agent: Check "ping" is now warning
2017/11/26 10:48:04 [INFO] agent: Synced check "ping"
2017/11/26 10:48:07 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:48:17 [WARN] agent: Check "service:web" is now critical
2017/11/26 10:48:27 [WARN] agent: Check "service:web" is now critical
前幾行表示代理已經同步了新的定義。 最後一行表明我們為Web服務新增的檢查是至關重要的。 這是因為我們實際上沒有執行Web伺服器,所以curl測試失敗了!
2、檢查健康狀態
現在我們已經添加了一些簡單的檢查,我們可以使用HTTP API來檢查它們。 首先,我們可以使用這個命令查詢任何失敗的檢查(注意,這可以在任一節點上執行):
[[email protected] etc]# curl http://localhost:8500/v1/health/state/critical
[{"Node":"agent-two","CheckID":"service:web","Name":"Service 'web' check","Status":"critical","Notes":"","Output":"","ServiceID":"web","ServiceName":"web","ServiceTags":["rails"],"Definition":{},"CreateIndex":230,"ModifyIndex":262}]
我們可以看到,只有一個檢查,我們的Web服務檢查,在危險(critical)狀態。
另外,我們可以嘗試使用DNS查詢Web服務。 由於服務不健康,Consul不會返回任何結果:
[[email protected] ~]# dig @127.0.0.1 -p 8600 web.service.consul
; <<>> DiG 9.9.4-RedHat-9.9.4-51.el7 <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 38998
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul. IN A
;; AUTHORITY SECTION:
consul. 0 IN SOA ns.consul. hostmaster.consul. 1511666146 3600 600 86400 0
;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: 日 11月 26 11:15:46 CST 2017
;; MSG SIZE rcvd: 97
十一、KV Data
Consul除了提供服務發現和綜合健康檢查之外,還提供一個易於使用的KV儲存。 這可以用來儲存動態配置,協助服務協調,建立leader選舉,並使開發人員可以考慮構建的任何東西。
1、簡單使用
為了演示有多簡單,我們將操作K / V儲存中的幾個鍵。 有兩種方式與Consul KV儲存進行互動:通過HTTP API和Consul KV CLI。 以下示例顯示使用Consul KV CLI,因為它是最容易入門的。 對於更高階的整合,您可能需要使用Consul KV HTTP API。
首先讓我們探索KV儲存。 我們可以向Consul詢問名為redis / config / minconns
的路徑上的key的值:
[[email protected] ~]# consul kv get redis/config/minconns
Error! No key exists at: redis/config/minconns
正如你所看到的,我們沒有得到任何結果,這是合理的,因為KV儲存沒有資料。 接下來,我們可以insert或put 值到 KV 儲存中。
[[email protected] ~]# consul kv put redis/config/minconns 1
Success! Data written to: redis/config/minconns
[[email protected] ~]# consul kv put redis/config/maxconns 25
Success! Data written to: redis/config/maxconns
[[email protected] ~]# consul kv put -flags=42 redis/config/users/admin abcd1234
Success! Data written to: redis/config/users/admin
現在我們在儲存中有key,我們可以查詢單個 key 的 value:
[[email protected] ~]# consul kv get redis/config/minconns
1
Consul保留有關使用-detailed
標誌檢索的欄位的其他元資料:
[[email protected] ~]# consul kv get -detailed redis/config/minconns
CreateIndex 517
Flags 0
Key redis/config/minconns
LockIndex 0
ModifyIndex 517
Session -
Value 1
對於“redis / config / users / admin”這個鍵,我們設定了一個標誌值42。所有的鍵都支援設定一個64位的整數標誌值。 這不是Consul在內部使用的,但客戶可以使用它為任何KV新增有意義的元資料。
可以使用遞迴選項列出儲存的所有 key和 value。 結果將以字典順序返回:
[[email protected] ~]# consul kv get -recurse
redis/config/maxconns:25
redis/config/minconns:1
redis/config/users/admin:abcd1234
要從Consul KV中刪除一個 key,發出“刪除” 命令:
[[email protected] ~]# consul kv delete redis/config/minconns
Success! Deleted key: redis/config/minconns
也可以使用遞迴選項刪除整個字首:
[[email protected] ~]# consul kv delete -recurse redis
Success! Deleted keys with prefix: redis
要更新現有key的值,請在相同路徑上put一個值:
[[email protected] ~]# consul kv put foo bar
Success! Data written to: foo
[[email protected] ~]# consul kv get foo
bar
[[email protected] ~]# consul kv put foo zip
Success! Data written to: foo
[[email protected] ~]# consul kv get foo
zip
Consul可以使用Check-And-Set操作提供原子鍵更新。 要執行CAS操作,請指定-cas選項:
[[email protected] ~]# consul kv get -detailed foo
CreateIndex 710
Flags 0
Key foo
LockIndex 0
ModifyIndex 716
Session -
Value bar
[[email protected] ~]# consul kv put -cas -modify-index=716 foo bar
Success! Data written to: foo
[[email protected] ~]# consul kv put -cas -modify-index=716 foo bar
Error! Did not write to foo: CAS failed
在這種情況下,第一個CAS更新成功,因為索引是716。第二個操作失敗,因為索引不再是716。
十二、Consul Web UI
Consul支援web ui介面。UI可用於檢視所有服務和節點,檢視所有執行狀況檢查及其當前狀態,以及讀取和設定鍵/值資料。 使用者介面自動支援多資料中心。
要設定自帶的UI,請使用-ui
引數啟動Consul代理:
consul agent -ui
UI可以在與HTTP API相同的埠上的/ui
路徑中使用。 預設情況下,這是http://localhost:8500/ui
。
您可以在這裡檢視Consul Web UI的現場演示。
十三、Docker下安裝consul
1、拉取映象
docker search consul
2、啟動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:節點的名稱
-bind:繫結的一個地址,用於節點之間通訊的地址,可以是內外網,必須是可以訪問到的地址
-server:這個就是表示這個節點是個SERVER
-bootstrap-expect:這個就是表示期望提供的SERVER節點數目,數目一達到,它就會被啟用,然後就是LEADER了 -
啟動節點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的處理
-
LEADER 掛了
leader掛了,consul會重新選取出新的leader,只要超過一半的SERVER還活著,叢集是可以正常工作的。node1是leader,所以把這個容器停了。docker stop node1
看看其他節點的日誌(node2):
日誌列印,心跳檢查node1的ip超時,接著開始選舉。node2被選舉為新的leader。我們檢視下現在的leader:curl http://172.17.0.4:8500/v1/status/leader
返回的內容:
"172.17.0.3:8300"
172.17.0.3 就是 node2節點的IP
-
3、註冊個服務
{
"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"
}
}'
4、發現個服務
剛剛註冊了名為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”
5、儲存個K/V
設定一個值到user/config/connections 內容為5
docker exec -t node1 consul kv put user/config/connections 5
6、獲取特定的值
docker exec -t node1 consul kv get -detailed user/config/connections
值的內容為5,還有key等相關的值。
參考文章