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

使用 Kubernetes 和 Docker 的簡單領導者選舉

概述

Kubernetes 簡化了在叢集上運行的服務的部署和操作管理。但是,它也簡化了這些服務的開發。在這篇文章中,我們將看到如何使用 Kubernetes 輕鬆地在分散式應用程式中執行領導者選舉。分散式應用程式通常會複製服務的任務以實現可靠性和可擴展性,但通常有必要指定其中一個複本作為領導者,負責協調所有複本。

通常在領導者選舉中,會識別一組成為領導者的候選者。這些候選者都競相宣布自己為領導者。其中一位候選人獲勝並成為領導者。一旦選舉獲勝,領導者會不斷「發送心跳訊號」以更新他們作為領導者的地位,而其他候選者會定期嘗試再次成為領導者。這確保了如果現任領導者因某種原因失敗,可以快速識別新的領導者。

實作領導者選舉通常需要部署諸如 ZooKeeper、etcd 或 Consul 之類的軟體並將其用於共識,或者,自行實作共識演算法。我們將在下面看到,Kubernetes 使在您的應用程式中使用領導者選舉的過程變得更加容易。

在 Kubernetes 中實作領導者選舉

領導者選舉的第一個要求是指定一組成為領導者的候選者。Kubernetes 已經使用端點來表示構成服務的一組複寫 Pod,因此我們將重複使用同一個物件。(題外話:您可能認為我們會使用ReplicationController,但它們與特定的二進位檔綁定,而且即使您正在執行滾動更新,通常也希望擁有單一領導者)

為了執行領導者選舉,我們使用了所有 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 秒。

leader-election 容器提供了一個簡單的 Web 伺服器,可以在任何位址上提供服務(例如 http://localhost:4040)。您可以透過刪除現有的領導者選舉群組並建立一個新的群組來測試它,您還可以在其中額外傳遞 --http=(host):(port) 規格給 leader-elector 映像。這會導致集合中的每個成員透過 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

然後您可以存取

http://localhost:8001/api/v1/proxy/namespaces/default/pods/(leader-pod-name):4040/

您將看到

{"name":"(name-of-leader-here)"}

使用 Sidecar 進行領導者選舉

好的,太棒了,您可以執行領導者選舉並透過 HTTP 找出領導者,但是您如何在自己的應用程式中使用它呢?這就是 Sidecar 概念的用武之地。在 Kubernetes 中,Pod 由一個或多個容器組成。通常,這意味著您將 Sidecar 容器添加到您的主要應用程式中以組成一個 Pod。(有關此主題的更詳細介紹,請參閱我之前的網誌文章)。

leader-election 容器可以用作您可以從自己的應用程式中使用的 Sidecar。Pod 中任何對目前主節點是誰感興趣的容器都可以簡單地存取 http://localhost: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 http://localhost: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。