Consul模板
Consul模板工具可以使用編程的方式從包括Consul KV數(shù)據(jù)集在內(nèi)的各種位置渲染配置文件,模板工具基于Go模板,并有許多相同的屬性。
Consul模板具有多種用途,我們將重點(diǎn)介紹其中的兩個:
-
更新配置文件: Consul模板工具可用于更新服務(wù)配置文件,一種常見的用法是管理負(fù)載均衡配置文件,這些文件需要在許多無法直接連接到Consul群集的計(jì)算機(jī)上定期動態(tài)更新; -
發(fā)現(xiàn)有關(guān)Consul群集和服務(wù)的數(shù)據(jù):可以收集有關(guān)Consul群集中服務(wù)的信息。例如,用戶可以收集群集上運(yùn)行的所有服務(wù)的列表,或者可以發(fā)現(xiàn)Redis所有服務(wù)的地址。注意,這個操作在生產(chǎn)環(huán)境有所限制。
在本章節(jié)中,我們將簡要討論Consul模板的工作原理,安裝方法以及兩個用例,在閱讀本章節(jié)前,我們假設(shè)您對Consul KV和Go模板有所了解。
Consul模板簡介
Consul模板是一個簡單但功能強(qiáng)大的工具。啟動后,它將讀取一個到多個模板文件,并向Consul發(fā)出查詢,加載它們所需的所有數(shù)據(jù)。通常,consul-template作為守護(hù)程序運(yùn)行,一開始獲取初始值,之后持續(xù)監(jiān)控更新,在集群中發(fā)生任何相關(guān)更改時都會重新加載模板。用戶也可以使用-once標(biāo)志僅加載一次模板,這在測試或者由其他腳本觸發(fā)時比較有用。最后,更新過程完成后,模板還可以運(yùn)行后置命令。例如,它可以在進(jìn)行配置更改后將HUP信號發(fā)送到負(fù)載均衡器服務(wù)。
Consul模板工具非常靈活,可以適應(yīng)許多不同的環(huán)境和工作流程。根據(jù)使用情況,用戶可能在少數(shù)幾個主機(jī)上只有一個consul-template實(shí)例,或者也可能需要在每個主機(jī)上運(yùn)行多個實(shí)例。每個Consul模板過程都可以管理多個不相關(guān)的文件,如果這些文件共享數(shù)據(jù)依賴項(xiàng),則將根據(jù)需要對提取的內(nèi)容進(jìn)行重復(fù)數(shù)據(jù)刪除,這樣可以減少Consul服務(wù)器上可能共享的負(fù)載。
安裝Consul模板
在本章節(jié)中,我們在開發(fā)者模式下使用本地Consul代理,先執(zhí)行consul agent -dev,Consul代理正常運(yùn)行后才能進(jìn)行以下其他步驟。
Consul模板工具本身不包含在Consul二進(jìn)制文件中,需要單獨(dú)安裝。用戶可以直接安裝預(yù)編譯的二進(jìn)制文件,也可以下載源代碼自行編譯,我們將安裝預(yù)編譯的二進(jìn)制文件,首先,先下載consul-template二進(jìn)制文件:
$ curl -O https://releases.hashicorp.com/consul-template/0.19.5/consul-template<_version_OS>.tgz
接下來,將二進(jìn)制文件加入$PATH路徑中:
$ tar -zxf consul-template<_version_OS>.tgz
用例:Consul KV數(shù)據(jù)集
在第一個用例中,我們將渲染一個模板,該模板會從Consul KV數(shù)據(jù)集中提取HashiCorp地址。我們需要創(chuàng)建一個包含HashiCorp地址的簡單模板,運(yùn)行consul-template,為Consul KV數(shù)據(jù)集添加一個HashiCorp地址的值,最后查看渲染的文件。
首先,我們需要創(chuàng)建一個模板文件find_address.tpl來查詢Consul KV數(shù)據(jù)集:
{{ key "/hashicorp/street_address" }}
運(yùn)行consul-template命令,同時指定要使用的模板和需要更新的文件:
$ consul-template -template "find_address.tpl:hashicorp_address.txt"
consul-template命令會持續(xù)運(yùn)行,用戶使用CTRL+c可以停止其運(yùn)行,然后我們打開一個新終端,使用命令行指令將數(shù)據(jù)寫入Consul:
$ consul kv put hashicorp/street_address "101 2nd St"
Success! Data written to: hashicorp/street_address
我們可以通過查看hashicorp_address.txt文件來確保有數(shù)據(jù)寫入文件:
$ cat hashicorp_address.txt
101 2nd St
如果用戶執(zhí)行命令consul kv put hashicorp/street_address "22b Baker ST"更新hashicorp/street_address值,可以看到該文件立即更新。這個過程雖然很簡單,但是意義很強(qiáng)大,比如用戶可以用相同的過程來更新HAProxy負(fù)載均衡器配置。
用例:發(fā)現(xiàn)所有服務(wù)
在此示例中,我們將發(fā)現(xiàn)Consul群集中運(yùn)行的所有服務(wù),我們使用上一個示例中的開發(fā)環(huán)境接著開發(fā)。
首先,我們將需要創(chuàng)建一個新模板all-services.tpl來查詢所有服務(wù)。
{{range services}}# {{.Name}}{{range service .Name}}
{{.Address}}{{end}}
{{end}}
接下來,運(yùn)行consul-template命令指定我們剛剛創(chuàng)建的模板,并使用-once標(biāo)志,表示僅運(yùn)行一次:
$ consul-template -template="all-services.tpl:all-services.txt" -once
如果您在本地代理上完成此操作,則可以通過all-services.txt文件查看consul服務(wù):
# consul
127.0.0.7
在開發(fā)或生產(chǎn)集群上,用戶會看到所有服務(wù)的列表:
# consul
104.131.121.232
# redis
104.131.86.92
104.131.109.224
104.131.59.59
# web
104.131.86.92
104.131.109.224
104.131.59.59
使用會話實(shí)現(xiàn)應(yīng)用程序領(lǐng)導(dǎo)者選舉
對于某些應(yīng)用程序,例如HDFS,有必要將一個實(shí)例設(shè)置為領(lǐng)導(dǎo)者,這樣可以確保應(yīng)用程序數(shù)據(jù)是最新且穩(wěn)定的,本章節(jié)介紹了如何使用Consul為服務(wù)實(shí)例構(gòu)建面向客戶端(client-side)的領(lǐng)導(dǎo)者選舉,Consul對會話的支持可幫助用戶構(gòu)建一個能夠正常處理故障的系統(tǒng)。
這篇章節(jié)的內(nèi)容與Consul本身的領(lǐng)導(dǎo)者選舉無關(guān),如果對該部分內(nèi)容感興趣,可以參考consensus協(xié)議。
競爭服務(wù)實(shí)例
想象一下,用戶有一組服務(wù)實(shí)例正在爭奪給定服務(wù)的領(lǐng)導(dǎo)地位,所有參與的服務(wù)實(shí)例都應(yīng)商定一個給定的密鑰進(jìn)行協(xié)調(diào),一個好的模式就是:
service/<service name>/leader
在本章節(jié)中,我們的完整密鑰可能是service/dbservice/leader,為了簡化行文,我們將密鑰定義為lead。
創(chuàng)建會話
首先使用HTTP API創(chuàng)建一個會話:
$ curl -X PUT -d '{"Name": "dbservice"}' http://localhost:8500/v1/session/create
這將返回一個包含會話ID的JSON對象:
{
"ID": "4ca8e74b-6350-7587-addf-a18084928f3c"
}
獲取會話
下一步是某個服務(wù)實(shí)例使用PUT方法獲取給定密鑰的會話,請求需要拼接?acquire=<session>查詢參數(shù)。
PUT方法中的<body>應(yīng)該是代表本地實(shí)例的JSON對象。該值對Consul不透明,但應(yīng)包含客戶端與用戶應(yīng)用程序進(jìn)行通信所需的必要信息(例如,它可以是包含節(jié)點(diǎn)名稱和應(yīng)用程序端口的JSON對象)。
$ curl -X PUT -d <body> http://localhost:8500/v1/kv/lead?acquire=4ca8e74b-6350-7587-addf-a18084928f3c
響應(yīng)結(jié)果為true或false,如果為true,則表示已經(jīng)獲取到鎖,本地服務(wù)實(shí)例現(xiàn)在為領(lǐng)導(dǎo)者,如果返回值為false,則說明其他某個節(jié)點(diǎn)獲得了鎖。
監(jiān)聽會話
現(xiàn)在,所有其他實(shí)例都處于空閑等待狀態(tài)。在這種狀態(tài)下,它們會監(jiān)聽密鑰lead的變化,鎖可能隨時會被釋放,實(shí)例也可能失效。領(lǐng)導(dǎo)者也必須注意變化,因?yàn)檫\(yùn)維人員可能手動釋放鎖,或者系統(tǒng)故障檢查誤報(bào)自動釋放鎖。
默認(rèn)情況下,會話僅使用Gossip故障檢測器,也就是說,只要默認(rèn)的Serf健康檢查未聲明該節(jié)點(diǎn)不健康,就認(rèn)為該節(jié)點(diǎn)保留著會話。如果需要,也可以指定其他檢查。
通過對<key>使用阻塞查詢(blocking query)來監(jiān)聽變化。如果它們察覺到<key>的會話為空,則說明此時沒有領(lǐng)導(dǎo)者,然后會重試獲取鎖。每次嘗試獲取密鑰都需要有一段時間間隔,Consul強(qiáng)制使用了lock-delay。
釋放會話
領(lǐng)導(dǎo)者可以解除鎖來釋放資源:
$ curl -X PUT http://localhost:8500/v1/kv/lead?release=4ca8e74b-6350-7587-addf-a18084928f3c
發(fā)現(xiàn)領(lǐng)導(dǎo)者
關(guān)于領(lǐng)導(dǎo)者選舉,另一種常見的場景是,非領(lǐng)導(dǎo)者實(shí)例希望在實(shí)例集群中辨認(rèn)出哪個實(shí)例是領(lǐng)導(dǎo)者,與選舉領(lǐng)導(dǎo)者一樣,所有參與的實(shí)例都應(yīng)就用于協(xié)調(diào)的密鑰達(dá)成一致,該密鑰將被稱為公正密鑰(just key)。
獲取密鑰
服務(wù)實(shí)例需要做的非常簡單,它們僅需讀取密鑰就能找到當(dāng)前的領(lǐng)導(dǎo)者,如果密鑰有關(guān)聯(lián)的會話,則說明已經(jīng)有領(lǐng)導(dǎo)者了。
$ curl -X GET http://localhost:8500/v1/kv/lead
[
{
"Session": "4ca8e74b-6350-7587-addf-a18084928f3c",
"Value": "Ym9keQ==",
"Flags": 0,
"Key": "dbservice",
"LockIndex": 1,
"ModifyIndex": 29,
"CreateIndex": 29
}
]
如果有領(lǐng)導(dǎo)者,則value字段會顯示與應(yīng)用程序相關(guān)的信息,信息使用Base64編碼。
獲取會話信息
用戶可以查詢/v1/session/info端點(diǎn)獲取會話的詳細(xì)信息:
$ curl -X GET http://localhost:8500/v1/session/info/4ca8e74b-6350-7587-addf-a18084928f3c
[
{
"LockDelay": 1.5e+10,
"Checks": [
"serfHealth"
],
"Node": "consul-primary-bjsiobmvdij6-node-lhe5ihreel7y",
"Name": "dbservice",
"ID": "4ca8e74b-6350-7587-addf-a18084928f3c",
"CreateIndex": 28
}
]
使用會話實(shí)現(xiàn)分布式信號量
當(dāng)用戶想要調(diào)度許多服務(wù),同時某些資源有訪問限制時,分布式信號量(distributed semaphore)可能會很有用。在本章節(jié)中,我們將重點(diǎn)介紹如何使用Consul對會話的支持以及利用Consul KV數(shù)據(jù)集來構(gòu)建分布式信號量。有多種方式可以構(gòu)建信號量,本章節(jié)不會全部覆蓋。
在閱讀本章節(jié)前,用戶應(yīng)該熟悉Consul KV數(shù)據(jù)集和Consul會話。
信號量中的競爭節(jié)點(diǎn)
想象一下,我們有一組試圖獲取信號量中插槽(slot)的節(jié)點(diǎn),所有參與競爭的節(jié)點(diǎn)都應(yīng)達(dá)成以下三個共識:
- KV數(shù)據(jù)集中用于調(diào)度的前綴;
- 單獨(dú)的
鍵值(Key)作為鎖; - 持有插槽數(shù)量的限制。
會話
第一步是為每個競爭節(jié)點(diǎn)創(chuàng)建一個會話,會話使我們能夠構(gòu)建可以正常處理故障的系統(tǒng)。
$ curl -X PUT -d '{"Name": "db-semaphore"}' \
http://localhost:8500/v1/session/create
響應(yīng)值是包含會話ID的JSON對象。
{
"ID": "4ca8e74b-6350-7587-addf-a18084928f3c"
}
創(chuàng)建競爭鎖
接下來,我們創(chuàng)建一個競爭鎖(lock contender)。每個競爭者都會創(chuàng)建一個與會話相關(guān)聯(lián)的KV條目(KV entry)。這樣做的目的是,如果競爭者持有某個插槽并發(fā)生故障,則其會話會與鍵值分離 ,然后其他競爭者可以檢測到該會話,用戶可以發(fā)送PUT請求創(chuàng)建競爭鎖:
curl -X PUT -d <body> http://localhost:8500/v1/kv/<prefix>/<session>?acquire=<session>
body的值一般為與該競爭鎖相關(guān)的有意義的內(nèi)容 ,比如節(jié)點(diǎn)的名稱,Consul本身不關(guān)注body的內(nèi)容,這個值對運(yùn)維人員有用,<session>的值是/v1/session/create請求的返回值。
該調(diào)用結(jié)果返回true或者false。如果為true,則說明創(chuàng)建競爭鎖創(chuàng)建成功。如果為false,則表明創(chuàng)建失敗,這通常意味著會話失效。
創(chuàng)建鍵值(Key)
下一步是創(chuàng)建一個鍵值(Key),用來協(xié)調(diào)插槽的持有者。 <prefix>/.lock是該鍵值的一個不錯選擇,我們稱這個特殊的鍵值為<lock>。
$ curl -X PUT -d <body> http://localhost:8500/v1/kv/<lock>?cas=0
由于正在創(chuàng)建鎖,此處cas索引值為0,僅當(dāng)該值不存在時才放入,body的內(nèi)容為信號量的插槽限制和當(dāng)前所有者的會話ID,內(nèi)容如下所示:
{
"Limit": 2,
"Holders": ["<session>"]
}
信號量管理
對<prefix>發(fā)送GET請求可以讀取信號量當(dāng)前狀態(tài):
$ curl http://localhost:8500/v1/kv/<prefix>?recurse
在返回值中,我們應(yīng)該重點(diǎn)關(guān)注兩個值:<lock>和<session>:
[
{
"LockIndex": 0,
"Key": "<lock>",
"Flags": 0,
"Value": "eyJMaW1pdCI6IDIsIkhvbGRlcnMiOlsiPHNlc3Npb24+Il19",
"Session": "",
"CreateIndex": 898,
"ModifyIndex": 901
},
{
"LockIndex": 1,
"Key": "<prefix>/<session>",
"Flags": 0,
"Value": null,
"Session": "<session>",
"CreateIndex": 897,
"ModifyIndex": 897
}
]
返回的響應(yīng)中,<lock>字段使用的是Base64編碼。
讀取<lock>數(shù)據(jù)并將其解碼后,我們可以驗(yàn)證Limit與Holders計(jì)數(shù)是否一致,這用于檢測潛在沖突。下一步是確定當(dāng)前哪些插槽(slot)仍在運(yùn)行。查詢響應(yīng)結(jié)果返回了所有競爭者條目,通過掃描這些條目,我們可以創(chuàng)建了一組會話值,不在該組中的所有Holders都會被修剪。實(shí)際上,我們將根據(jù)列表結(jié)果創(chuàng)建一組實(shí)時競爭者,并與Holder進(jìn)行一定的區(qū)別以檢測和修剪任何可能失敗的Holder。在此示例中,<session>存在于Holders中,并附加在鍵<prefix>/<session>上,因此不需要修剪。
如果修剪后的持有者數(shù)量少于限制數(shù)量,則競爭者會嘗試通過將其自己的會話添加到持有者(Holders)列表中,并對<lock>進(jìn)行Check-And-Set操作,這會執(zhí)行樂觀更新。
我們可以通過以下方式獲?。?/p>
$ curl -X PUT -d <Updated Lock Body> http://localhost:8500/v1/kv/<lock>?cas=<lock-modify-index>
lock-modify-index是<lock>已知的最新ModifyIndex值,在此示例中為901。
如果該請求返回true,競爭者將在信號量中保留一個插槽,如果返回false,則可能是與另一個競爭者發(fā)生了爭奪。
當(dāng)重新嘗試進(jìn)行數(shù)據(jù)采集時,我們會注意到<prefix>的更新。這是因?yàn)榭赡軙尫挪宀?,或者?jié)點(diǎn)發(fā)生故障等等。通過對/kv/<prefix>recurse接口進(jìn)行阻塞查詢可以監(jiān)控更新。
插槽持有者必須不斷關(guān)注<prefix>的更新,因?yàn)檫\(yùn)維人員可以釋放插槽,或者由于故障檢測器誤報(bào)而自動釋放插槽。在更改<prefix>時,必須重新檢查鎖的持有人(Holders)列表,以確保仍保留該插槽。另外,如果監(jiān)聽器無法連接插槽,則應(yīng)視為丟失。
此信號量系統(tǒng)純粹是建議性的。因此,還是應(yīng)該由客戶端來確定在執(zhí)行某些關(guān)鍵操作之前(和期間)是否已保留插槽。
最后,如果插槽持有者希望自愿釋放插槽,則應(yīng)通過對<lock>進(jìn)行檢查并設(shè)置(Check-And-Set)操作以從Holders對象中刪除其會話。完成后,競爭者密鑰<prefix>/<session>和會話都會被刪除。