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

使用 Kubernetes 和 Docker 进行简单的领导者选举

概述

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

通常在领导者选举中,会确定一组候选领导者。这些候选人都竞相宣布自己为领导者。其中一位候选人获胜并成为领导者。选举获胜后,领导者会持续“心跳”以更新其领导者地位,其他候选人会定期尝试成为领导者。这确保了如果当前领导者由于某种原因失败,可以快速确定新的领导者。

实施领导者选举通常需要部署 ZooKeeper、etcd 或 Consul 等软件并将其用于共识,或者自己实现共识算法。我们将在下面看到 Kubernetes 使得在应用程序中使用领导者选举的过程变得更加容易。

在 Kubernetes 中实现领导者选举

领导者选举的首要要求是指定候选领导者集合。Kubernetes 已经使用 *Endpoints* 来表示构成服务的复制 pod 集,因此我们将重复使用同一个对象。(旁白:你可能认为我们会使用 *ReplicationControllers*,但它们与特定的二进制文件绑定,通常即使你正在执行滚动更新,你也希望只有一个领导者)

为了执行领导者选举,我们使用所有 Kubernetes API 对象的两个属性

  • 资源版本 - 每个 API 对象都有一个唯一的资源版本,您可以使用这些版本对 Kubernetes 对象执行比较和交换
  • 注释 - 每个 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 秒。

领导者选举容器提供了一个简单的网络服务器,可以在任何地址上提供服务(例如 http://localhost:4040)。您可以通过删除现有的领导者选举组并创建一个新的组来测试这一点,在新的组中,您还可以将 --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。(有关此主题的更详细处理,请参阅我之前的博客文章)。

领导者选举容器可以作为 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 容器引擎kubernetes.io 开始使用 Kubernetes。