2. etcd 核心概念回顾:Raft 共识算法、Leader 选举、日志复制、仲裁机制
在深入多租户仲裁水位隔离策略之前,必须重新校准对 etcd 底层共识引擎的理解。etcd 使用 Raft 共识算法 来保证分布式系统中的强一致性与高可用性。本节将拆解 Raft 的核心组件:Leader 选举、日志复制与仲裁(Quorum)机制,并阐明它们如何共同决定 etcd 集群的容错边界。
2.1 Raft 共识算法概述
Raft 是一种易于理解的分布式共识算法,它将共识问题分解为三个相对独立的子问题:Leader 选举、日志复制 和 安全性。在 etcd 中,Raft 确保所有节点(Member)的状态机以相同的顺序执行相同的命令,从而达成最终一致。
核心原则: 只要集群中超过半数(N/2 + 1)的节点存活且相互通信正常,etcd 集群就能对外提供一致的读写服务。这个半数阈值就是 仲裁(Quorum)。
2.2 Raft 节点角色与状态转换
每个 etcd 节点在任意时刻都处于以下三种角色之一:
| 角色 | 职责 | 关键行为 |
|---|---|---|
| Leader(领导者) | 处理所有客户端请求(读写),管理日志复制,发送心跳。 | 定期向 Follower 发送 AppendEntries(心跳 + 日志);处理写请求时,必须等待多数节点确认。 |
| Follower(跟随者) | 被动响应 Leader 和 Candidate 的请求,不主动发起任何操作。 | 接收并应用来自 Leader 的日志条目;如果超时未收到 Leader 心跳,则转换为 Candidate。 |
| Candidate(候选者) | 发起 Leader 选举,争取成为新的 Leader。 | 增加任期号(Term),向其他节点请求投票;获得多数票后成为 Leader。 |
术语提示: 任期号(Term)是 Raft 中的逻辑时钟,每次选举开始都会递增。一个任期内最多只有一个 Leader。
2.3 Leader 选举机制
当 Follower 在 选举超时时间(通常为 150ms ~ 300ms 随机)内未收到 Leader 的心跳,它会认为 Leader 已宕机,从而触发新一轮选举:
- 发起选举: Follower 增加本地任期号(Term+1),转换为 Candidate,并投票给自己。
- 请求投票: Candidate 并行向集群中所有其他节点发送
RequestVote RPC。 - 投票规则: 每个节点在一个任期内只能投出一票,且优先投票给日志更完整(Term 更大、Log Index 更长)的 Candidate。
- 胜出条件: Candidate 获得超过半数(N/2 + 1)的投票后,成为新的 Leader。
- 平局处理: 如果多个 Candidate 均未获得多数票,选举失败,所有 Candidate 等待随机超时后重新发起选举。
关键影响: 频繁的 Leader 选举会降低集群可用性。在 etcd 运维中,网络抖动或磁盘 I/O 延迟过高都可能导致不必要的选举,进而引发短暂的“无 Leader”窗口期。
2.4 日志复制(Log Replication)
一旦 Leader 当选,它开始负责处理所有客户端写入请求。日志复制流程如下:
| 步骤 | 操作描述 | 关键状态 |
|---|---|---|
| 1. 追加 | Leader 将客户端请求封装为日志条目(Entry),追加到本地日志。 | 日志条目包含:Term、Index、Command。 |
| 2. 并行发送 | Leader 通过 AppendEntries RPC 并行将日志条目发送给所有 Follower。 |
RPC 包含前一条日志的 Index 和 Term,用于一致性检查。 |
| 3. 接收与确认 | Follower 检查日志一致性后追加到本地日志,并回复确认。 | 如果日志不一致,Follower 拒绝并返回冲突信息,Leader 回退重试。 |
| 4. 提交(Commit) | Leader 收到多数节点(Quorum)的确认后,将日志条目标记为已提交。 | Leader 将提交索引(Commit Index)通过后续心跳广播给所有 Follower。 |
| 5. 应用(Apply) | 所有节点将已提交的日志条目应用到状态机(如 etcd 的 KV 存储)。 | 应用顺序与日志顺序完全一致,保证状态机确定性。 |
2.5 仲裁(Quorum)机制与容错模型
仲裁是 Raft 算法的核心安全阀。它定义了集群能够容忍的最大故障节点数。对于一个包含 N 个节点的 etcd 集群:
- 仲裁大小(Quorum Size):
Q = N/2 + 1(向下取整)。 - 最大故障容忍数(Fault Tolerance):
F = N - Q = (N-1)/2(向下取整)。 - 写操作: 必须等待至少
Q个节点(包括 Leader)确认日志写入,才能返回客户端成功。 - 读操作(线性一致性读): 默认也需要经过 Quorum 确认(通过 ReadIndex 或 Lease Read),以确保读取到已提交的最新数据。
| 集群节点数 (N) | 仲裁大小 (Q) | 最大故障容忍数 (F) | 典型场景 |
|---|---|---|---|
| 1 | 1 | 0 | 开发/测试,无高可用 |
| 3 | 2 | 1 | 生产最小推荐配置 |
| 5 | 3 | 2 | 生产标准高可用配置 |
| 7 | 4 | 3 | 超大规模或跨区域部署 |
多租户关联: 在 Serverless 控制面中,每个租户的请求最终都会转化为 etcd 的写入操作。如果某个 etcd 节点因为租户流量过载(如大量 Watch 请求或高并发写入)而变慢,它可能无法及时响应 Leader 的
AppendEntries 心跳,导致 Leader 认为该节点已失效。当故障节点数超过 F 时,集群将失去仲裁能力,所有租户的读写都会中断。这正是我们引入“仲裁水位隔离策略”的原始动机——通过控制每个租户对 etcd 仲裁资源的消耗,防止单一租户的行为导致全局仲裁失效。
2.6 安全性保证:为什么仲裁如此重要
Raft 的安全性建立在以下关键属性上,这些属性都依赖于仲裁机制:
- Election Safety: 在一个任期内,最多只有一个 Leader 被选出(通过多数票保证)。
- Leader Append-Only: Leader 从不覆盖或删除自己的日志,只追加新条目。
- Log Matching: 如果两个节点上的日志条目具有相同的 Index 和 Term,那么这两个条目及其之前的所有条目都完全相同。
- Leader Completeness: 被提交的日志条目在后续所有 Leader 任期中都一定存在(新 Leader 必须包含所有已提交的日志)。
- State Machine Safety: 所有节点以相同的顺序应用相同的日志条目,保证状态机一致。
实践建议: 在生产环境中,始终使用奇数节点数(3、5、7)。偶数节点(如 4 节点)的容错能力与比它小 1 的奇数节点相同(4 节点容忍 1 故障,3 节点也容忍 1 故障),但增加了运维复杂度和网络开销。