Kubernetes故障域变更与etcd一致性校验 - 第1节
1.1 Raft 协议核心原理:从状态机复制到强一致性
etcd 基于 Raft 共识算法实现分布式一致性。Raft 将共识问题分解为三个相对独立的子问题:Leader 选举、日志复制和安全性。其核心思想是通过一个强 Leader 节点来协调所有写操作,并将操作以日志条目(Log Entry)的形式复制到集群中的大多数节点(Quorum),从而保证即使部分节点故障,整个集群仍能对外提供一致的服务。
1.1.1 节点角色与任期
Raft 集群中的每个节点在任意时刻都处于以下三种角色之一:
| 角色 | 职责 | 状态特征 |
|---|---|---|
| Leader(领导者) | 处理所有客户端请求;管理日志复制;发送心跳维持权威 | 唯一;定期发送 AppendEntries RPC(含心跳) |
| Follower(跟随者) | 被动响应 Leader 和 Candidate 的 RPC 请求;不主动发起操作 | 默认角色;接收并应用已提交的日志 |
| Candidate(候选者) | 发起 Leader 选举;向其他节点请求投票 | 选举超时后进入;获得多数票后转为 Leader |
Raft 将时间划分为一个个连续的、单调递增的任期(Term)。每个任期最多只有一个 Leader。任期号作为逻辑时钟,用于检测过期信息(如过期的 Leader 或过期的投票请求)。
1.1.2 状态机复制模型
etcd 的底层存储引擎(如 BoltDB / bbolt)与 Raft 日志层协同工作,构成一个典型的复制状态机(Replicated State Machine):
- 客户端请求:写请求(如 put、delete)被发送到 Leader。
- 日志追加:Leader 将请求封装为一个 Log Entry(包含 Term、Index、Command),追加到本地日志。
- 日志复制:Leader 通过 AppendEntries RPC 并行将日志条目发送给所有 Follower。
- 日志提交:当 Leader 确认日志条目已被复制到大多数节点(Quorum)后,将该条目标记为已提交(Committed),并应用到状态机。
- 响应客户端:Leader 将执行结果返回给客户端。
1.2 Leader 选举机制:故障检测与权威确立
Leader 选举是 Raft 保证集群在故障后快速恢复写入能力的关键机制。当现有 Leader 失效或网络分区发生时,Follower 会通过选举超时机制触发新一轮选举。
1.2.1 选举触发条件
- 选举超时(Election Timeout):每个 Follower 维护一个随机化的选举超时计时器(通常 150ms ~ 300ms)。若在超时时间内未收到 Leader 的有效心跳(AppendEntries RPC),Follower 认为 Leader 已失联,自动转换为 Candidate。
- 任期递增:Candidate 启动选举时,将当前任期号(Term)+1,并向集群中所有节点发送 RequestVote RPC。
1.2.2 投票规则与安全性
Raft 通过以下规则保证选举的安全性(最多只有一个 Leader 被选出):
| 规则 | 说明 | 作用 |
|---|---|---|
| 每个任期只能投一票 | 每个节点在每个 Term 内只能投票给一个 Candidate | 防止分裂投票导致多个 Leader |
| 日志新旧比较 | 节点只投票给日志“至少与自己一样新”的 Candidate(比较最后一条日志的 Term 和 Index) | 保证新 Leader 包含所有已提交的日志 |
| 多数派原则 | Candidate 必须获得集群中超过半数(N/2 + 1)的投票才能成为 Leader | 保证只有一个 Candidate 能获得多数票 |
1.3 日志复制与提交:一致性保障的核心路径
日志复制是 Raft 实现状态机一致性的核心机制。Leader 负责将客户端请求转化为日志条目,并确保这些条目被可靠地复制到集群中的大多数节点。
1.3.1 日志条目结构
每个日志条目包含以下关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| Term | uint64 | 创建该条目时的 Leader 任期号 |
| Index | uint64 | 日志条目在日志中的唯一位置(单调递增) |
| Command | []byte | 具体的操作指令(如 put key=value) |
1.3.2 日志复制流程详解
- Leader 接收请求:客户端将写请求发送到 Leader。
- 追加本地日志:Leader 将请求封装为 Log Entry,追加到自己的日志中。
- 并行复制:Leader 通过 AppendEntries RPC 将新日志条目(以及之前未确认的条目)发送给所有 Follower。
- Follower 校验与追加:Follower 收到 AppendEntries 后,进行一致性检查(prevLogIndex 和 prevLogTerm 必须匹配),匹配则追加到本地日志。
- 响应确认:Follower 返回成功响应给 Leader。
- 提交判定:Leader 收到多数节点(包括自己)的成功响应后,将日志条目标记为已提交(Committed),并应用到状态机。
- 通知提交:后续的 AppendEntries RPC(或心跳)中,Leader 会携带 commitIndex,通知 Follower 哪些条目已提交。
1.3.3 日志匹配特性(Log Matching Property)
Raft 保证以下两个关键特性,这是 etcd 数据一致性的数学基础:
- 如果两个日志条目具有相同的 Index 和 Term,则它们存储的 Command 相同。
- 如果两个日志条目具有相同的 Index 和 Term,则它们之前的所有日志条目也相同。
1.3.4 日志不一致处理
当 Leader 与 Follower 的日志出现不一致时(如网络分区后恢复),Raft 通过以下机制强制 Follower 的日志与 Leader 保持一致:
| 场景 | Leader 行为 | Follower 行为 |
|---|---|---|
| Follower 日志缺失 | 从 prevLogIndex 开始逐条发送缺失的条目 | 追加缺失条目 |
| Follower 日志冲突 | 递减 nextIndex,直到找到匹配的日志位置 | 删除冲突位置之后的所有条目 |
| Follower 日志超前 | 不会发生(Leader 的日志总是最新的) | — |
1.4 安全性保证:Raft 协议的基石
Raft 通过一系列安全性约束,确保在 Leader 选举和日志复制过程中不会出现数据不一致或脑裂问题。
1.4.1 选举安全性(Election Safety)
在任意一个 Term 内,最多只有一个 Leader 被选出。这是通过“每个节点每个 Term 只能投一票”和“多数派原则”共同保证的。
1.4.2 Leader 只追加性(Leader Append-Only)
Leader 永远不会覆盖或删除自己日志中的条目,只会追加新条目。这保证了已提交的日志不会被 Leader 修改。
1.4.3 日志匹配性(Log Matching)
如前所述,通过 prevLogIndex 和 prevLogTerm 的校验,保证所有节点的日志在已提交部分完全一致。
1.4.4 Leader 完整性(Leader Completeness)
被选为 Leader 的节点必须包含所有已提交的日志条目。这是通过投票时的“日志新旧比较”规则实现的:Candidate 的日志必须至少与投票者一样新,才能获得投票。
1.4.5 状态机安全性(State Machine Safety)
如果某个节点将特定 Index 的日志条目应用到了状态机,那么其他节点在相同 Index 位置应用的一定是相同的 Command。这是由日志匹配性和提交规则共同保证的。
1.5 本章小结:一致性基础与故障域复核的关联
Raft 协议为 etcd 提供了强一致性保障,其核心机制——Leader 选举、日志复制与提交、安全性约束——共同构成了分布式系统可靠运行的基石。在后续章节中,我们将基于这些基础原理,深入探讨:
- 如何通过故障域划分优化 Raft 的选举和复制性能
- 资源变更(如节点扩缩容、网络配置修改)对 Raft 日志一致性的影响
- 一致性复核的具体方法和工具(如 etcdctl check、数据校验和)