本文发布已超过一年。较旧的文章可能包含过时内容。请检查页面信息自发布以来是否已失效。

使用 Kubernetes 和 Docker 实现简单领导者选举

概述

Kubernetes 简化了在集群上运行的服务的部署和运维管理。然而,它也简化了这些服务的开发。在这篇文章中,我们将看到如何使用 Kubernetes 在分布式应用中轻松实现领导者选举。分布式应用通常会复制服务的任务以提高可靠性和可伸缩性,但通常需要指定其中一个副本作为领导者,负责协调所有副本。

典型的领导者选举过程是,首先确定一组潜在的领导者候选人。这些候选人竞相声明自己是领导者。其中一个候选人获胜并成为领导者。选举获胜后,领导者会持续“心跳”以更新其领导者地位,而其他候选人则会定期尝试成为新的领导者。这确保了如果当前领导者因某种原因失败,可以快速识别出新的领导者。

实现领导者选举通常需要部署 ZooKeeper、etcd 或 Consul 等软件并利用它们实现共识,或者自行实现共识算法。我们将在下文看到,Kubernetes 使在应用中使用领导者选举的过程变得显著更容易。

在 Kubernetes 中实现领导者选举

领导者选举的第一个要求是指定领导者候选人集合。Kubernetes 已经使用 Endpoints 来表示组成服务的复制 Pod 集合,因此我们将重用这个对象。(旁注:您可能认为我们会使用 ReplicationController,但它们与特定的二进制文件绑定,通常即使在执行滚动更新过程中,您也希望只有一个领导者)

进行领导者选举时,我们使用所有 Kubernetes API 对象的两个属性:

  • ResourceVersions - 每个 API 对象都有一个唯一的 ResourceVersion,您可以使用这些版本在 Kubernetes 对象上执行比较并交换 (compare-and-swap) 操作。
  • Annotations - 每个 API 对象都可以使用任意键值对进行标注 (annotate),供客户端使用。

基于这些原语,使用主节点选举 (master election) 的代码相对直观,您可以在此处找到它。让我们自己运行一下。

$ 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)

... 或者,您可以直接检查 Endpoints 对象

'example' 是上面 kubectl run ... 命令中的候选人集合名称

$ kubectl get endpoints example -o yaml

现在,为了验证领导者选举确实有效,在另一个终端中运行:

$ kubectl delete pods (leader-pod-name)

这将删除当前的领导者。由于 Pod 集合由 Replication Controller 管理,删除的 Pod 会被一个新的 Pod 替换,确保复制集合的大小仍然是三个。通过领导者选举,这三个 Pod 中的一个会被选为新的领导者,您应该会看到领导者故障转移到另一个 Pod。由于 Kubernetes 中的 Pod 在终止前有一个宽限期 (grace period),这可能需要 30-40 秒。

leader-election 容器提供了一个简单的 Web 服务器,可以在任何地址(例如 http://localhost: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

然后您可以访问:

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 中的所有容器共享同一个网络命名空间 (network namespace),因此无需服务发现!

例如,这是一个简单的 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。