4. etcd 架构深度解析:Raft共识算法、集群节点角色与数据存储
etcd 是 Kubernetes 集群的“大脑”,所有集群状态、配置信息、服务发现数据都存储在 etcd 中。理解 etcd 的底层架构,是进行故障域隔离设计和灾难恢复的前提。本章将深入剖析 etcd 的核心——Raft 共识算法、节点角色分工以及数据持久化机制。
4.1 Raft 共识算法:分布式一致性的基石
etcd 使用 Raft 共识算法来保证集群中多个节点之间的数据一致性。Raft 将复杂的一致性问题分解为三个关键子问题:领导者选举(Leader Election)、日志复制(Log Replication)和安全性(Safety)。
核心原则: 只要集群中超过半数(N/2+1)的节点存活且正常通信,etcd 集群就能对外提供一致性的读写服务。这是 etcd 高可用设计的数学基础。
4.1.1 领导者选举与任期
- Term(任期): Raft 将时间划分为任意长度的任期,每个任期以一次选举开始。任期号单调递增,作为逻辑时钟。
- 选举触发: Follower 节点如果在“选举超时时间”内未收到 Leader 的心跳,则转换为 Candidate 状态,发起选举。
- 投票机制: Candidate 向其他节点发送 RequestVote RPC。每个节点在一个任期内只能投一票,先到先得。获得超过半数投票的 Candidate 成为新 Leader。
- 脑裂防护: 如果出现平票,则进入新一轮选举。Raft 通过随机化选举超时时间(150-300ms)来降低平票概率。
4.1.2 日志复制与提交
- 客户端请求: 所有写请求必须发送给 Leader。Leader 将请求封装为一条日志条目(Log Entry),包含任期号和索引。
- 并行复制: Leader 将日志条目通过 AppendEntries RPC 并行发送给所有 Follower。
- 日志匹配: Follower 收到日志后,检查前一条日志的任期和索引是否匹配。匹配则追加到本地日志。
- 提交(Commit): 当 Leader 确认日志条目已被超过半数的节点复制(持久化)后,该条目进入“已提交”状态。Leader 将提交索引推进,并通知 Follower。
- 应用到状态机: 已提交的日志条目最终被应用到 etcd 的持久化存储(BoltDB)中,完成数据写入。
关键指标: 日志复制延迟直接影响 etcd 的写入性能。在故障域隔离设计中,应尽量保证 Leader 与多数 Follower 之间的网络延迟在 10ms 以内,以避免频繁的选举超时和性能抖动。
4.2 etcd 集群节点角色:Leader / Follower / Candidate
etcd 集群中的每个节点在任意时刻都处于以下三种角色之一。角色转换由 Raft 算法自动驱动,无需人工干预。
| 角色 | 职责 | 状态特征 | 故障影响 |
|---|---|---|---|
| Leader | 处理所有客户端写请求;管理日志复制;发送心跳维持权威。 | 定期向所有 Follower 发送心跳(默认 100ms)。 | Leader 宕机触发新选举,集群在选举期间不可写入。 |
| Follower | 被动复制 Leader 的日志;响应 Leader 和 Candidate 的 RPC 请求;处理客户端读请求(可配置)。 | 被动接收心跳;如果超时未收到心跳,转换为 Candidate。 | 少数 Follower 宕机不影响集群读写;多数 Follower 宕机导致 Leader 降级,集群不可用。 |
| Candidate | 发起领导者选举;向其他节点拉票。 | 临时状态;选举成功后转为 Leader,失败则退回 Follower。 | Candidate 本身不处理业务请求,选举期间集群短暂不可用。 |
故障域隔离要点: 在跨可用区部署时,应避免将多数 etcd 节点部署在同一个故障域(如一个机架、一个交换机或一个可用区)。如果 Leader 和多数 Follower 同时位于同一故障域,该域故障将直接导致集群失去法定人数(Quorum),造成全局不可用。
4.3 数据存储与快照机制
etcd 的数据存储分为两层:内存中的逻辑视图(Tree Index)和磁盘上的物理存储(BoltDB + WAL)。理解这两层的关系,对于诊断数据损坏和制定恢复策略至关重要。
4.3.1 存储架构分层
| 层级 | 组件 | 作用 | 持久化方式 |
|---|---|---|---|
| 内存层 | Tree Index (B-tree) | 维护 key 到 revision 的映射,支持高效的范围查询和 MVCC。 | 不持久化,重启后从 BoltDB 重建。 |
| 磁盘层 | BoltDB (backend) | 存储实际的 key-value 数据,以 revision 为主键组织。 | 持久化到 member/snap/db 文件。 |
| 预写日志 | WAL (Write-Ahead Log) | 记录所有 Raft 日志条目,用于故障恢复和数据一致性校验。 | 持久化到 member/wal/ 目录。 |
| 快照 | Snapshot | 压缩 WAL 日志,记录某一时刻的完整状态,加速重启和恢复。 | 持久化到 member/snap/ 目录。 |
4.3.2 快照(Snapshot)的工作原理
随着写入量的增加,WAL 日志会无限增长。etcd 通过定期创建快照来控制日志大小。快照记录了某一时刻的完整数据库状态。
- 触发条件: 当 WAL 日志条目数量达到
--snapshot-count(默认 100,000)时,自动触发快照。 - 创建过程: etcd 将当前 BoltDB 状态持久化为一个
.snap文件,并记录该快照包含的最后一条日志的索引和任期。 - 日志压缩: 快照创建完成后,该快照之前的所有 WAL 日志可以被安全删除。
- 恢复加速: 新节点加入或节点重启时,先加载最新的快照文件,然后只回放快照之后的 WAL 日志,大大缩短恢复时间。
灾难恢复关键: 在 etcd 数据损坏或丢失时,
member/snap/ 目录下的快照文件和 member/wal/ 目录下的 WAL 日志是恢复数据的唯一来源。务必对这两个目录实施定期备份(如每小时一次快照备份)。
4.3.3 数据目录结构
# etcd 数据目录典型结构(默认 /var/lib/etcd/)
/var/lib/etcd/
├── member/
│ ├── snap/
│ │ ├── 0000000000000001-0000000000000001.snap # 快照文件
│ │ └── 0000000000000002-0000000000000002.snap
│ ├── wal/
│ │ ├── 0000000000000000-0000000000000000.wal # WAL 日志文件
│ │ └── 0000000000000001-0000000000000001.wal
│ └── backend/
│ └── db # BoltDB 数据库文件
└── etcd.conf # 配置文件
4.4 本章小结
- Raft 共识算法 通过领导者选举和日志复制,保证了 etcd 在多数节点存活时的强一致性。
- 节点角色(Leader/Follower/Candidate)由算法自动管理,故障域隔离设计必须确保法定人数(Quorum)跨故障域分布。
- 数据存储 采用内存索引 + BoltDB 磁盘存储 + WAL 预写日志 + 快照的四层架构,快照机制是控制日志增长和加速恢复的核心。
- 在后续的演练章节中,我们将基于这些底层原理,设计针对 etcd 故障的隔离策略和租户恢复方案。