本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

宣布 etcd 3.4

etcd 3.4 版本重点关注稳定性、性能和易操作性,引入了预投票和非投票成员等功能,并改进了存储后端和客户端负载均衡器。

有关完整的更改列表,请参阅更新日志

更好的存储后端

etcd v3.4 包含了许多针对大规模 Kubernetes 工作负载的性能改进。

特别是,etcd 在大量并发读取事务时(即使没有写入操作,例如 “read-only range request ... took too long to execute”)也会出现性能问题。以前,存储后端对挂起写入的提交操作会阻塞传入的读取事务,即使没有挂起写入。现在,提交不再阻塞读取,从而改善了长时间运行的读取事务性能。

我们进一步使后端读取事务完全并发。以前,正在进行的长时间运行的读取事务会阻塞写入和即将进行的读取。通过此更改,在长时间运行的读取存在的情况下,写入吞吐量增加了 70%,P99 写入延迟降低了 90%。我们还在 GCE 上使用此更改运行了Kubernetes 5000 节点可伸缩性测试,并观察到类似的改进。例如,在测试开始时,存在大量长时间运行的“LIST pods”,P99 “POST clusterrolebindings”的延迟降低了 97.4%

租约存储也得到了更多改进。我们通过更有效地存储租约对象,增强了租约过期/撤销性能,并使租约查找操作非阻塞,与当前的租约授予/撤销操作并发执行。etcd v3.4 还引入了租约检查点作为一项实验性功能,通过共识持久化剩余的生存时间值。这确保了短寿命租约对象在领导选举后不会自动续订。这也防止了当生存时间值相对较大时(例如,Kubernetes 用例中 1 小时 TTL 永不过期)租约对象堆积。

改进的 Raft 投票过程

etcd 服务器实现了Raft 共识算法用于数据复制。Raft 是一种基于领导者的协议。数据从领导者复制到追随者;追随者将提议转发给领导者,领导者决定是否提交。领导者持久化并复制一个条目,一旦它得到集群法定人数的同意。集群成员选举一个单一的领导者,所有其他成员都成为追随者。当选的领导者定期向其追随者发送心跳以维持其领导地位,并期望从每个追随者那里得到响应以跟踪其进度。

在最简单的形式中,当 Raft 领导者收到带有更高任期的消息时,它会退位成为追随者,而无需进行任何进一步的集群范围健康检查。这种行为可能会影响整个集群的可用性。

例如,一个不稳定的(或重新加入的)成员反复进出,并开始竞选。这个成员最终会拥有更高的任期,忽略所有带有更低任期的传入消息,并发送带有更高任期的消息。当领导者收到这条更高任期的消息时,它会退回到追随者状态。

当出现网络分区时,这会变得更具破坏性。每当分区节点恢复连接时,它可能会触发领导者重新选举。为了解决这个问题,etcd Raft 引入了一个新的节点状态“预候选者”,并具有预投票功能。预候选者首先询问其他服务器,它是否足够最新以获得投票。只有当它能获得多数投票时,它才会增加其任期并开始选举。这个额外的阶段通常会提高领导者选举的鲁棒性。并且只要领导者与其法定人数的对等体保持连接,它就能保持稳定。

同样,当重启的节点没有及时收到领导者心跳(例如由于网络缓慢)时,etcd 的可用性可能会受到影响,这会触发领导者选举。以前,etcd 在服务器启动时会快进选举计时,只剩下一次计时用于领导者选举。例如,当选举超时为 1 秒时,追随者只等待 100 毫秒的领导者联系,然后才开始选举。这加快了初始服务器启动速度,因为无需等待选举超时(例如,在 100 毫秒而不是 1 秒内触发选举)。推进选举计时对于具有更大选举超时的跨数据中心部署也很有用。然而,在许多情况下,可用性比初始领导者选举的速度更关键。为了确保重新加入节点的更好可用性,etcd 现在调整选举计时,保留多于一次计时,从而为领导者提供更多时间来防止破坏性重启。

Raft 非投票成员,学习者

成员资格重新配置的挑战在于它通常会导致法定人数大小的变化,这容易导致集群不可用。即使它不改变法定人数,成员资格发生变化的集群也更有可能遇到其他潜在问题。为了提高重新配置的可靠性和信心,etcd 3.4 版本引入了一个新角色——学习者。

新的 etcd 成员以无初始数据加入集群,请求领导者所有历史更新,直到它赶上领导者的日志。这意味着领导者的网络更容易过载,阻塞或丢弃发给追随者的领导者心跳。在这种情况下,追随者可能会遇到选举超时并开始新的领导者选举。也就是说,拥有新成员的集群更容易受到领导者选举的影响。领导者选举和随后对新成员的更新传播都容易导致集群不可用期间(参见 图 1)。

learner-figure-1

最糟糕的情况是成员添加配置错误。etcd 中的成员重配置是一个两步过程:使用对等 URL 执行 `etcdctl member add` 命令,然后启动新的 etcd 加入集群。也就是说,无论对等 URL 值是否无效,`member add` 命令都会被应用。如果第一步是应用无效的 URL 并改变法定人数大小,则集群在新的节点连接之前就可能失去法定人数。由于具有无效 URL 的节点永远不会上线且没有领导者,因此无法回滚成员更改(参见 图 2)。

learner-figure-2

当存在分区节点时,情况会变得更加复杂(有关更多信息,请参阅设计文档)。

为了解决这些故障模式,etcd 引入了一个新的节点状态“学习者”,它以非投票成员的身份加入集群,直到它赶上领导者的日志。这意味着学习者仍然接收领导者的所有更新,但它不计入法定人数,法定人数由领导者用于评估对等体的活跃性。学习者在晋升之前只充当备用节点。这种对法定人数的宽松要求在成员重配置和操作安全性期间提供了更好的可用性(参见 图 3)。

learner-figure-3

我们将进一步提高学习者的鲁棒性,并探索自动晋升机制,以实现更简单、更可靠的操作。请阅读我们的学习者设计文档运行时配置文档以获取用户指南。

新的客户端负载均衡器

etcd 旨在容忍各种系统和网络故障。按照设计,即使一个节点宕机,集群也能“看似”正常工作,因为它提供了多台服务器的统一逻辑集群视图。但是,这并不能保证客户端的活性。因此,etcd 客户端实现了一套不同的复杂协议,以保证其在故障条件下的正确性和高可用性。

从历史上看,etcd 客户端负载均衡器严重依赖旧的 gRPC 接口:每次 gRPC 依赖项升级都会破坏客户端行为。大量的开发和调试工作都致力于修复这些客户端行为变更。结果,其实现变得过于复杂,对服务器连接做出了错误的假设。主要目标是简化 etcd v3.4 客户端中的负载均衡器故障转移逻辑;不再维护可能过时的不健康端点列表,而是在客户端与当前端点断开连接时,简单地轮询到下一个端点。它不假定端点状态。因此,不再需要复杂的状跟踪。

此外,新的客户端现在会创建自己的凭据捆绑包,以修复针对安全端点的负载均衡器故障转移。这解决了长达一年的 bug,即当第一个 etcd 服务器不可用时,kube-apiserver 会失去与 etcd 集群的连接。

有关更多信息,请参阅客户端负载均衡器设计文档