2. etcd 基础:架构、Raft 核心概念与 K8s 数据模型

etcd 是 Kubernetes 控制平面的基石,一个高可用的分布式键值存储系统。它使用 Raft 共识算法 保证数据强一致性,并作为 K8s 所有集群状态(Pod、Service、ConfigMap 等)的唯一数据源。理解 etcd 是掌握 K8s 存储与故障恢复的前提。

2.1 etcd 整体架构

etcd 采用 Client-Server 架构,Server 端通过 Raft 协议组成集群。每个 etcd 节点包含以下核心组件:

  • HTTP/ gRPC Server:处理客户端读写请求、集群成员变更、Leader 选举等。
  • Raft 状态机:实现 Raft 共识算法,负责日志复制、选举和持久化。
  • WAL (Write-Ahead Log):顺序写入磁盘,记录所有状态变更,用于故障恢复。
  • Snapshot:当 WAL 过大时,生成快照压缩日志,加速重启恢复。
  • MVCC (Multi-Version Concurrency Control):支持并发读写,保留历史版本,用于 Watch 机制和事务。
  • Backend (BoltDB):底层持久化存储引擎,以 B+ 树组织数据。
架构要点: 所有写请求必须由 Leader 处理,Follower 将请求转发给 Leader。读请求可由任意节点处理(默认线性一致性读需 Leader 确认)。
etcd 节点内部架构 K8s API Server (Client) HTTP / gRPC Server Raft 状态机 MVCC / Watch Backend (BoltDB) WAL (日志) Snapshot 磁盘 (Data) 写路径:Client → Leader → Raft → WAL → Backend → 响应 读路径:Client → 任意节点 → MVCC → Backend → 响应

2.2 Raft 协议核心概念

Raft 是一种易于理解的分布式共识算法,etcd 使用它来保证集群中所有节点数据一致。Raft 将节点分为三种角色:

角色 职责 关键行为
Leader 处理所有写请求,管理日志复制 定期发送心跳(Heartbeat)给 Follower;负责日志提交
Follower 被动接收 Leader 的日志和心跳 如果超时未收到心跳,转换为 Candidate 发起选举
Candidate 选举过程中的临时角色 向其他节点请求投票,获得多数票后成为 Leader

2.3 Raft 选举与日志复制流程

  1. Leader 选举:Follower 在选举超时(150-300ms 随机)后变为 Candidate,发起投票。获得超过半数(N/2+1)节点投票的 Candidate 成为新 Leader。
  2. 日志复制:Leader 接收客户端写请求,将日志条目追加到本地 WAL,然后并行发送 AppendEntries RPC 给所有 Follower。
  3. 日志提交:当 Leader 收到多数 Follower 的确认后,将日志条目标记为已提交(Committed),并应用到状态机,最后响应客户端。
  4. 安全性:Raft 保证只有拥有最新日志的节点才能成为 Leader,防止已提交数据被覆盖。
SRE 视角: etcd 集群通常部署 3 或 5 节点。3 节点集群容忍 1 节点故障,5 节点集群容忍 2 节点故障。选举超时和心跳间隔直接影响故障恢复速度。

2.4 etcd 在 Kubernetes 中的角色

Kubernetes 将 etcd 作为 控制平面的核心数据存储,所有 API 对象都持久化在 etcd 中。具体角色如下:

  • 状态持久化:集群的期望状态(Spec)和当前状态(Status)都存储在 etcd 中。
  • Watch 机制:API Server 通过 etcd 的 Watch 功能监听资源变化,驱动控制器(Controller)工作。
  • 集群一致性:多个 API Server 实例通过 etcd 共享数据,保证集群状态一致。
  • 故障恢复:etcd 的快照和 WAL 是灾难恢复的关键,定期备份 etcd 是 SRE 的必修课。
etcd 在 Kubernetes 控制平面中的角色 kube-apiserver etcd (Leader) etcd (Follower) etcd (Follower) 数据流:kubectl/Controller → API Server → etcd (Leader) → 复制到 Follower Watch 流:etcd → API Server → Controller Manager / Scheduler kube-controller-manager kube-scheduler kubelet (Worker)

2.5 etcd 数据模型与 K8s 存储结构

etcd 内部使用 扁平的键值对 存储数据,但 Kubernetes 通过 前缀机制 构建了层次化的命名空间。K8s 所有资源都存储在 /registry 前缀下:

etcd 键路径 对应 K8s 资源 示例
/registry/pods/<namespace>/<name> Pod /registry/pods/default/nginx-pod
/registry/services/<namespace>/<name> Service /registry/services/kube-system/kube-dns
/registry/configmaps/<namespace>/<name> ConfigMap /registry/configmaps/default/app-config
/registry/secrets/<namespace>/<name> Secret /registry/secrets/default/tls-secret
/registry/nodes/<name> Node /registry/nodes/worker-1
/registry/clusterroles/<name> ClusterRole /registry/clusterroles/admin
SRE 警告: 不要直接使用 etcdctl 修改 K8s 数据,除非在灾难恢复场景。直接操作 etcd 可能导致集群状态不一致,引发不可预知的问题。始终通过 API Server 操作资源。

2.6 关键数据模型特性

  • MVCC 版本控制:每个键值对都有 revision(全局版本号)和 create_revision / mod_revision。Watch 基于 revision 实现增量变更推送。
  • Lease 机制:etcd 支持租约(Lease),用于实现键的 TTL 自动过期。K8s 使用 Lease 实现 Node 心跳和 Leader 选举。
  • 事务支持:etcd 提供 比较-交换(CAS) 事务,K8s 利用它实现乐观锁(resourceVersion 冲突检测)。
  • Watch 流:客户端可以监听某个前缀或键的变化,K8s 的 Informer 机制正是基于此实现。
压测关注点: etcd 性能瓶颈通常出现在 写请求量 和 Watch 连接数。K8s 集群中大量 Pod 频繁更新 Status 或频繁创建/删除资源,会直接冲击 etcd 的写入吞吐和 WAL 写入延迟。