本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用 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 Enginekubernetes.io開始使用 Kubernetes。