本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
使用 Kubernetes 和 Docker 進行簡單的領導者選舉
概述
Kubernetes 簡化了叢集上執行服務的部署和操作管理。然而,它也簡化了這些服務的開發。在這篇文章中,我們將看到如何使用 Kubernetes 輕鬆地在分散式應用程式中執行領導者選舉。分散式應用程式通常為了可靠性和可伸縮性而複製服務任務,但通常需要指定其中一個副本作為領導者,負責所有副本之間的協調。
通常在領導者選舉中,會確定一組候選者。這些候選者都競相宣告自己是領導者。其中一位候選者獲勝併成為領導者。一旦選舉獲勝,領導者會持續“心跳”以更新其領導者位置,而其他候選者則定期嘗試成為新的領導者。這確保瞭如果當前領導者因某種原因失敗,可以迅速識別新的領導者。
實現領導者選舉通常需要部署 ZooKeeper、etcd 或 Consul 等軟體並將其用於共識,或者自行實現共識演算法。我們將在下面看到,Kubernetes 使在應用程式中使用領導者選舉的過程大大簡化。
在 Kubernetes 中實現領導者選舉
領導者選舉的第一個要求是指定成為領導者的候選者集合。Kubernetes 已經使用 _Endpoints_ 來表示由服務組成的副本 Pod 集合,因此我們將重用這個相同的物件。(附註:您可能認為我們會使用 _ReplicationControllers_,但它們與特定的二進位制檔案繫結,通常即使您正在執行滾動更新,您也希望只有一個領導者)
為了執行領導者選舉,我們使用所有 Kubernetes API 物件的兩個屬性:
- ResourceVersions - 每個 API 物件都有一個唯一的 ResourceVersion,您可以使用這些版本對 Kubernetes 物件執行比較並交換操作
- Annotations - 每個 API 物件都可以使用任意鍵/值對進行註釋,供客戶端使用。
有了這些原語,使用主選舉的程式碼相對簡單,您可以在此處找到它。讓我們自己執行一下。
$ kubectl run leader-elector --image=gcr.io/google_containers/leader-elector:0.4 --replicas=3 -- --election=example
這將建立一個包含 3 個副本的領導者選舉集
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
leader-elector-inmr1 1/1 Running 0 13s
leader-elector-qkq00 1/1 Running 0 13s
leader-elector-sgwcq 1/1 Running 0 13s
要檢視哪個 Pod 被選為領導者,您可以訪問其中一個 Pod 的日誌,將您自己的 Pod 名稱替換掉
${pod_name}, (e.g. leader-elector-inmr1 from the above)
$ kubectl logs -f ${name}
leader is (leader-pod-name)
... 另外,您也可以直接檢查端點物件
'example' 是上述 kubectl run … 命令中的候選集名稱。
$ kubectl get endpoints example -o yaml
現在要驗證領導者選舉是否確實有效,請在另一個終端中執行
$ kubectl delete pods (leader-pod-name)
這將刪除現有的領導者。由於 Pod 集由副本控制器管理,因此一個新的 Pod 將替換被刪除的 Pod,從而確保副本集的大小仍然是三個。透過領導者選舉,這三個 Pod 中的一個被選為新的領導者,您應該會看到領導者故障轉移到另一個 Pod。由於 Kubernetes 中的 Pod 在終止前有一個 _寬限期_,這可能需要 30-40 秒。
領導者選舉容器提供了一個簡單的 Web 伺服器,可以在任何地址(例如 https://:4040)上提供服務。您可以透過刪除現有領導者選舉組並建立一個新的組來測試此功能,在新組中,您還需要向 leader-elector 映象傳遞 --http=(host):(port) 引數。這會使集合中的每個成員透過 Webhook 提供有關領導者的資訊。
# delete the old leader elector group
$ kubectl delete rc leader-elector
# create the new group, note the --http=localhost:4040 flag
$ kubectl run leader-elector --image=gcr.io/google_containers/leader-elector:0.4 --replicas=3 -- --election=example --http=0.0.0.0:4040
# create a proxy to your Kubernetes api server
$ kubectl proxy
然後您可以訪問
https://:8001/api/v1/proxy/namespaces/default/pods/(leader-pod-name):4040/
您將看到
{"name":"(name-of-leader-here)"}
使用 Sidecar 的領導者選舉
好的,太棒了,您可以進行領導者選舉並透過 HTTP 找出領導者,但是如何在您自己的應用程式中使用它呢?這就是 sidecar 概念的用武之地。在 Kubernetes 中,Pod 由一個或多個容器組成。通常,這意味著您將 sidecar 容器新增到您的主應用程式中以組成一個 Pod。(有關此主題的更詳細處理,請參閱我之前的部落格文章)。
領導者選舉容器可以作為 sidecar,您可以在自己的應用程式中使用它。Pod 中任何對當前主節點感興趣的容器都可以簡單地訪問 https://:4040,它們將返回一個包含當前主節點名稱的簡單 JSON 物件。由於 Pod 中的所有容器共享相同的網路名稱空間,因此無需服務發現!
例如,這是一個簡單的 Node.js 應用程式,它連線到領導者選舉 sidecar 並打印出它當前是否是主節點。領導者選舉 sidecar 預設將其識別符號設定為 hostname
。
var http = require('http');
// This will hold info about the current master
var master = {};
// The web handler for our nodejs application
var handleRequest = function(request, response) {
response.writeHead(200);
response.end("Master is " + master.name);
};
// A callback that is used for our outgoing client requests to the sidecar
var cb = function(response) {
var data = '';
response.on('data', function(piece) { data = data + piece; });
response.on('end', function() { master = JSON.parse(data); });
};
// Make an async request to the sidecar at https://:4040
var updateMaster = function() {
var req = http.get({host: 'localhost', path: '/', port: 4040}, cb);
req.on('error', function(e) { console.log('problem with request: ' + e.message); });
req.end();
};
/ / Set up regular updates
updateMaster();
setInterval(updateMaster, 5000);
// set up the web server
var www = http.createServer(handleRequest);
www.listen(8080);
當然,您可以從您選擇的任何支援 HTTP 和 JSON 的語言中使用此 sidecar。
結論
希望我已經向您展示了使用 Kubernetes 為分散式應用程式構建領導者選舉是多麼容易。在未來的文章中,我們將向您展示 Kubernetes 如何讓構建分散式系統變得更加簡單。與此同時,請前往Google Container Engine或kubernetes.io開始使用 Kubernetes。