etcd 基础回顾:架构、Raft 协议、键值存储模型与 MVCC
在深入存储配额耗尽故障之前,必须牢固掌握 etcd 的核心基础。etcd 是一个高可用的分布式键值存储系统,其设计围绕三个核心支柱:Raft 共识算法保证数据一致性,键值存储模型提供数据组织方式,多版本并发控制(MVCC)实现高效并发与历史查询。本节将系统梳理这些基础概念。
etcd 整体架构
etcd 集群由多个节点组成,每个节点在逻辑上分为多个层次。理解这些层次有助于定位故障根因。
架构要点:etcd 的存储路径是
Client → gRPC API → Raft 层 → WAL(预写日志)→ MVCC 存储引擎 → BoltDB(底层持久化)。任何一层的瓶颈都可能引发存储配额问题。
Raft 协议核心机制
Raft 是一种易于理解的分布式共识算法,etcd 使用它来确保集群中所有节点的数据一致。Raft 将节点角色分为三种:Leader、Follower 和 Candidate。
| 角色 | 职责 | 关键行为 |
|---|---|---|
| Leader | 处理所有客户端写入请求,管理日志复制 | 定期发送心跳(Heartbeat)维持权威;负责日志条目分发 |
| Follower | 被动接收 Leader 的日志复制,响应投票请求 | 如果选举超时未收到心跳,则转为 Candidate 发起选举 |
| Candidate | 在选举阶段请求其他节点投票 | 获得多数票(quorum)后成为新 Leader |
运维启示:存储配额耗尽时,Leader 节点可能因无法写入 WAL 而触发降级或选举超时,导致集群短暂不可用。这是故障场景中需要优先监控的指标。
键值存储模型
etcd 的键值存储模型具有以下特点:
- 层次化键空间:键以
/分隔,形成类似文件系统的目录结构,例如/registry/services/specs/nginx。 - 键值对:每个键关联一个值,值可以是任意字节序列(通常为 JSON 或 protobuf 序列化数据)。
- 租约(Lease):键可以绑定到租约上,租约过期时自动删除关联的键,用于实现 TTL 自动清理。
- Watch 机制:客户端可以监视某个键或前缀的变化,实现事件驱动。
# 示例:写入一个键值对
etcdctl put /cluster/config/max_nodes "100"
# 读取键值
etcdctl get /cluster/config/max_nodes
# 带租约写入(60秒后自动删除)
etcdctl put /tmp/session "active" --lease=60s
多版本并发控制(MVCC)
MVCC 是 etcd 实现高效并发读和故障恢复的核心机制。每次对键的修改都会生成一个新的版本号,旧版本数据不会被立即覆盖,而是保留一段时间。
| 概念 | 说明 | 与存储配额的关系 |
|---|---|---|
| revision | 全局单调递增的版本号,每次事务(put/delete)递增 | 历史 revision 越多,BoltDB 占用空间越大 |
| keyValue | 存储键、值、创建版本、最后修改版本、租约等信息 | 每个版本都占用存储空间 |
| 历史版本压缩 | 通过 etcdctl compaction 或自动压缩策略清理旧版本 |
未及时压缩是存储配额超限的常见原因 |
| 碎片整理 | 压缩后 BoltDB 文件内部产生碎片,需 defrag 回收空间 |
即使压缩后,磁盘占用可能仍不下降,需手动整理 |
常见误区:很多运维人员认为执行了
etcdctl compaction 后磁盘占用会立即下降。实际上,压缩只是标记旧版本为可回收,BoltDB 文件大小不会变化,必须配合 etcdctl defrag 才能回收物理空间。在存储配额耗尽场景中,如果只压缩不整理,告警不会解除。
关键参数速查表
以下参数直接影响存储配额行为和故障恢复策略:
| 参数 | 默认值 | 说明 |
|---|---|---|
--quota-backend-bytes |
2 GB | BoltDB 存储配额上限,超过后拒绝写入 |
--auto-compaction-mode |
periodic | 自动压缩模式:periodic(周期)或 revision(版本数) |
--auto-compaction-retention |
1h | 保留历史版本的时间或版本数量 |
--max-request-bytes |
1.5 MB | 单个请求的最大字节数,防止大 key 撑爆内存 |