TT Lab
开始
学习 学习路径 课程

当时有两个领导者

失去法定人数不是变慢,而是停止

在 TT Lab 中继续学习

一句话总结

etcd 是原样实现了 Raft 的存储,前面亲手做出来的任期和法定人数,会以同样的名字 显现出来。最常被误解的地方是读取——失去过半数时,不只是写入,连默认的读取也会停止。

为什么需要它

简易实现适合学习,但并不是可以相信的依据。我做的规则是玩具的规则, 还是真正的规则,只有看到同样的数字、同样的话在真实系统中出现,才能确认。 etcd 是最适合做这种确认的对象。Kubernetes 的所有对象都存放在这里, 文档也把自己介绍为“一致且容错地存储元数据”的 “面向大规模分布式系统的通用基础” (etcd——为什么是 etcd)。

工作原理

用相同的 --initial-cluster 字符串启动三个成员,它们就会把彼此认作同一个集群。 文档中静态引导的示例,会同时给出名称、对等节点地址、客户端地址,以及集群令牌。 之所以设置令牌,正如文档所说,是为了“给每个集群一个唯一的令牌”, 避免不同的集群混在一起 (etcd 集群配置)。

启动之后,前面模块里做出来的那些值会原样显现出来。

ENDPOINT          ID                IS LEADER   RAFT TERM   RAFT INDEX
127.0.0.1:22379   99f0eb44090117f8  false       2           14
127.0.0.1:22479   e64076ee26a8ab0d  false       2           14
127.0.0.1:22579   8e05ae0f7f2c67be  true        2           14

RAFT TERM 是任期,RAFT INDEX 是日志的位置编号。用 etcdctl move-leader 转移 领导者,任期就会加一——这与前面新领导者提高任期再当选是同一回事。

法定人数的表,文档里原样就有。1 个节点的过半数是 1,可容忍 0 台故障;3 个节点是 2 和 1 台; 5 个节点是 3 和 2 台;7 个节点是 4 和 3 台。文档建议把集群控制在七台以内, 并写道:“奇数规模的集群在容忍与偶数规模相同数量故障的同时,节点更少” (etcd FAQ)。

最重要的地方是读取。etcd 默认的读取是线性一致读(linearizable read), 文档把线性一致性定义为“并发执行的进程所应用的每个操作,看起来都在调用与响应之间的 某一个时间点上瞬间发生”。紧接着又写道:“线性一致性是有代价的—— 因为线性一致的请求必须经过 Raft 共识过程” (etcd API 保证)。所以失去过半数的 集群不只是变慢,连读取也会停止。同一份文档还解释说,如果把请求的一致性模式 设为 serializable,“就可以按法定人数的标准读到陈旧的数据,作为交换, 消除了线性一致的访问依赖存活的共识所带来的性能负担”——也就是说,如果能接受 陈旧的值,就有这条路。

读到这里,就能看出前面三个实验里写的规则,和这份文档里的句子说的是同一回事。我们写在 has_majority 里的“超过一半”,就是文档中的 (n/2)+1, commit_index 所数的东西,在这里就是 RAFT INDEX,而提高任期、当选的新领导者, 就是 move-leader 之后的 RAFT TERM。只是名字不同而已。

在现场相遇的样子

Kubernetes 集群处于“什么都不行”的状态,其中相当一部分就是这个。控制平面节点 三台中的两台同时宕机,kube-apiserver 即使还活着,连查询也做不了。 这是因为 etcd 失去了过半数,无法确定读取。这时无论怎么盯着剩下的那一台看, 原因也不在那台机器里。

etcd 文档区分了两种情况:临时失去法定人数,在网络恢复后会自动安全地恢复; 而永久失去法定人数则是致命的。这个区分在运维中的含义只有一个 ——在失去法定人数的状态下,不要急着删除或新增成员,先确认有没有 能够回来的成员。

还有一点。这个实验使用的端口不是标准的 2379,原因是同一个 Pod 里,另一个 实验用的控制平面已经在用那个端口了。仅仅挪一下端口,就多出一个 集群,这件事本身也意味着,etcd 只把集群当作“地址和令牌的组合”来区分。

下一项实验要做什么

在同一个 Pod 里用静态引导方式启动三个 etcd 成员。转移领导者,观察任期上升; 停掉一个再写入;停掉两个,同时尝试写入和读取。最后, 把前面三个实验里亲手做的规则,与这里看到的数字对照一下。