本文发布已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已失效或不准确。
宣布 etcd 3.4
etcd 3.4 版本侧重于稳定性、性能和易操作性,包含了预投票(pre-vote)、非投票成员(non-voting member)等特性,并改进了存储后端和客户端负载均衡器。
请参阅更新日志 (CHANGELOG) 查看完整的变更列表。
更优秀的存储后端
etcd v3.4 包含多项针对大规模 Kubernetes 工作负载的性能改进。
特别是,etcd 在大量并发读事务下曾出现性能问题,即使没有写入操作(例如 “read-only range request ... took too long to execute”
)。此前,存储后端对挂起写入的提交操作会阻塞传入的读事务,即使没有挂起写入。现在,提交操作不再阻塞读取,从而提高了长时间运行的读事务性能。
我们进一步实现了后端读事务的完全并发。此前,正在进行的长时间运行的读事务会阻塞写入和后续的读取。通过这项变更,在长时间运行的读取存在的情况下,写入吞吐量提高了 70%,P99 写入延迟降低了 90%。我们还在 GCE 上运行了 Kubernetes 5000 节点可扩展性测试,并观察到类似的改进。例如,在测试刚开始时,存在大量长时间运行的“LIST pods”操作,此时“POST clusterrolebindings”的 P99 延迟降低了 97.4%。
租约存储也进行了更多改进。通过更有效地存储租约对象,我们增强了租约过期/撤销性能,并使得租约查找操作在当前租约授予/撤销操作期间不再阻塞。etcd v3.4 还引入了实验性的租约检查点(lease checkpoint)功能,通过共识机制持久化剩余的存活时间(TTL)值。这确保了短生命周期的租约对象在领导者选举后不会自动续订。这也防止了当存活时间值相对较大时(例如 在 Kubernetes 用例中 1 小时 TTL 从未过期)租约对象堆积的问题。
改进的 Raft 投票过程
etcd 服务器实现了Raft 共识算法进行数据复制。Raft 是一种基于领导者的协议。数据从领导者复制到追随者;追随者将提议转发给领导者,领导者决定是否提交。一旦获得集群多数派的同意,领导者就会持久化并复制一个条目。集群成员选举出一个领导者,其他所有成员都成为追随者。当选的领导者定期向其追随者发送心跳以维护其领导地位,并期望从每个追随者那里获得响应以跟踪其进度。
最简单的情况下,当 Raft 领导者收到带有更高任期(term)的消息时,如果没有进一步的集群范围健康检查,它就会降级为追随者。这种行为会影响整个集群的可用性。
例如,一个不稳定的(或重新加入的)成员不断进出,并开始发起竞选。这个成员最终拥有更高的任期,会忽略所有带有较低任期的传入消息,并发送带有更高任期的消息。当领导者收到这个更高任期的消息时,它就会变回追随者。
当存在网络分区时,这种情况会变得更具破坏性。每当分区节点恢复连接时,它可能会触发领导者重新选举。为了解决这个问题,etcd Raft 引入了一种新的节点状态——预候选者(pre-candidate),并带有预投票(pre-vote)特性。预候选者首先询问其他服务器它是否足够新以获得选票。只有当它可以从多数派那里获得选票时,它才会增加自己的任期并开始选举。这个额外的阶段总体上提高了领导者选举的健壮性。并且只要领导者能维持与其对等节点的多数派连接,它就能保持稳定。
类似地,当重启节点未能及时收到领导者心跳(例如由于网络缓慢)时,也会触发领导者选举,从而影响 etcd 的可用性。此前,etcd 在服务器启动时会快进选举计时器(election ticks),只剩下一次心跳间隔就进行领导者选举。例如,当选举超时时间为 1 秒时,追随者仅等待 100 毫秒接收领导者联系,然后就开始选举。这通过避免等待选举超时时间来加快初始服务器启动(例如,100 毫秒而非 1 秒内触发选举)。推进选举计时器对于具有较长选举超时时间的跨数据中心部署也很有用。然而,在许多情况下,可用性比初始领导者选举的速度更关键。为了确保重新加入节点时具有更好的可用性,etcd 现在调整了选举计时器,使其剩余多于一次心跳间隔,从而为领导者提供了更多时间来防止破坏性重启。
Raft 非投票成员,Learner
成员变更(membership reconfiguration)的挑战在于它常常导致多数派大小的变化,这很容易引发集群不可用。即使不改变多数派大小,发生成员变更的集群也更容易遇到其他潜在问题。为了提高变更的可靠性和信心,etcd 3.4 版本引入了一个新角色 - 学习者(learner)。
新的 etcd 成员加入集群时没有初始数据,它会向领导者请求所有历史更新,直到赶上领导者的日志进度。这意味着领导者的网络更容易过载,可能阻塞或丢弃发往追随者的心跳。在这种情况下,追随者可能出现选举超时,并开始新的领导者选举。也就是说,包含新成员的集群更容易受到领导者选举的影响。领导者选举以及随后的更新传播给新成员都容易导致集群在一段时间内不可用(参见图 1)。
最糟糕的情况是成员添加配置错误。etcd 中的成员变更是一个两步过程:使用 peer URL 执行 etcdctl member add
命令,然后启动一个新的 etcd 实例加入集群。也就是说,无论 peer URL 的值是否有效,都会应用 member add
命令。如果第一步应用了无效的 URL 并改变了多数派大小,则在新的节点连接之前,集群可能已经失去了多数派。由于带有无效 URL 的节点永远不会上线,并且没有领导者,因此无法回滚成员变更(参见图 2)。
当存在分区节点时,情况会变得更复杂(更多详情请参见设计文档)。
为了解决此类故障模式,etcd 引入了新的节点状态“学习者(Learner)”,它作为非投票成员加入集群,直到赶上领导者的日志进度。这意味着学习者仍然接收来自领导者的所有更新,但它不计入多数派(多数派用于领导者评估对等节点的活跃性)。学习者在被提升(promote)之前仅作为备用节点。这种对多数派要求的放宽在成员变更期间提供了更好的可用性和操作安全性(参见图 3)。
我们将进一步提高学习者的健壮性,并探索自动提升机制,以实现更轻松、更可靠的操作。请阅读我们的学习者设计文档和运行时配置文档以获取用户指南。
新的客户端负载均衡器
etcd 设计用于容忍各种系统和网络故障。根据设计,即使一个节点宕机,集群通过提供多个服务器的一个逻辑集群视图,“看起来”仍然正常工作。但这不能保证客户端的活跃性。因此,etcd 客户端实现了一套不同的复杂协议,以确保在故障条件下的正确性和高可用性。
从历史上看,etcd 客户端负载均衡器严重依赖旧的 gRPC 接口:每次 gRPC 依赖升级都会破坏客户端行为。大部分开发和调试工作都集中在修复这些客户端行为变更上。结果,其实现变得过于复杂,对服务器连接性存在错误的假设。主要目标是简化 etcd v3.4 客户端的负载均衡器故障转移逻辑;不再维护可能过时的不健康端点列表,而是在客户端与当前端点断开连接时,简单地轮询到下一个端点。它不假设端点状态。因此,不再需要复杂的状态跟踪。
此外,新客户端现在创建自己的凭据包,以修复针对安全端点的负载均衡器故障转移问题。这解决了一个长达一年的 Bug,即当第一个 etcd 服务器不可用时,kube-apiserver 会失去与 etcd 集群的连接。
更多详情请参见客户端负载均衡器设计文档。