深入理解 etcd
深入理解 etcd:一个分布式键值存储系统

好的,根据您提供的内容,我整理了一篇关于 Kubernetes 控制平面组件 etcd 的技术博客,内容如下:
深入理解 Kubernetes 基石:etcd 详解
作者: 孟凡杰(前 eBay 资深架构师)
引言
在 Kubernetes(K8s)集群中,etcd 扮演着至关重要的角色。它是整个集群的“大脑”,负责存储集群状态、配置信息以及进行服务发现。本文将深入探讨 etcd 的核心概念、功能、使用场景、Raft 协议、存储机制、高可用方案以及在 Kubernetes 中的应用,帮助您全面理解这一关键组件。
什么是 etcd?
etcd 是由 CoreOS(现已被 Red Hat 收购)开发的一个开源、分布式、高可用的键值存储系统。它基于 Raft 一致性算法,确保数据在多个节点之间保持一致性,即使在部分节点故障的情况下也能正常运行。
etcd 的主要特点:
- 键值对存储: 数据以键值对的形式存储在分层目录结构中,类似于文件系统。
- 变更监测: 可以监测特定键或目录的变化,并在发生变化时做出反应。
- 简单易用: 提供 HTTP+JSON 的 API,易于使用 curl 等工具进行访问。
- 安全可靠: 支持 SSL 客户端证书认证,使用 Raft 算法保证数据一致性。
- 高性能: 单实例每秒可处理数千次读写操作。
etcd 的核心功能
etcd 主要提供以下功能:
- 基本的键值存储: 存储和检索数据。
- 监听机制: 监听键或目录的变化,实现实时通知。
- 键的过期及续约: 用于监控和服务发现,确保服务的可用性。
- 原子 Compare And Swap (CAS) 和 Compare And Delete (CAD): 用于实现分布式锁和领导者选举。
etcd 的典型应用场景
etcd 在分布式系统中有着广泛的应用,常见的场景包括:
- 服务注册与发现: 服务提供者将自己的信息注册到 etcd,服务消费者通过 etcd 发现可用的服务实例。
- 消息发布与订阅: 构建配置共享中心,实现配置的集中管理和动态更新。
- 分布式锁: 利用 CAS 或 CAD 操作实现分布式锁,协调多个进程对共享资源的访问。
- 领导者选举: 在分布式系统中选举出一个领导者,负责协调和管理其他节点。
- 键值对存储: 作为数据库存储集群数据。
服务注册与发现示例(Mermaid 流程图):

graph LR subgraph 服务注册 A[服务提供者] --> B(向 etcd 注册服务) end subgraph 服务发现 C[服务消费者] --> D(从 etcd 查询服务) end B --> E[etcd] D --> E subgraph 服务绑定 F[服务消费者] --> G[服务提供者] end
消息发布与订阅示例(Mermaid 流程图):

graph LR subgraph 消息发布 A[生产者] --> B(向 etcd 发布消息) end subgraph 消息订阅 C[服务消费者] --> D(从 etcd 订阅消息) end B --> E[etcd] D --> E
etcd 的安装与使用
etcd 的安装非常简单,可以从 GitHub Releases 页面下载预编译的二进制文件,解压后即可运行。
安装步骤(示例):
1 |
|
常用客户端工具:
- etcdctl: 命令行客户端工具,用于与 etcd 集群交互。
- go-etcd: Go 语言客户端库。
- jetcd: Java 客户端库。
- python-etcd: Python 客户端库。
基本数据读写操作(etcdctl 示例):
1 |
|
深入理解 Raft 一致性协议
etcd 的数据一致性是基于 Raft 协议实现的。Raft 是一种易于理解的分布式一致性算法,它将一致性问题分解为几个子问题:
- 领导者选举(Leader Election): 初始启动或领导者失效时,集群中的节点会通过投票选举出一个新的领导者。
- 日志复制(Log Replication): 领导者接收客户端的请求,并将请求作为日志条目复制到其他节点。
- 安全性(Safety): 确保每个节点都执行相同序列的命令,保证数据的一致性。
Raft 协议角色:
- Leader(领导者): 负责接收客户端请求、日志复制和向 Follower 发送心跳。
- Follower(跟随者): 接收 Leader 的日志并复制到本地,参与投票选举 Leader。
- Candidate(候选者): 在 Leader 选举过程中,Follower 会转变为 Candidate,发起投票。
- Learner: 新加入的节点,只接收数据而不参与投票,因此增加
learner节点时,集群的quorum不变。
Raft 协议流程(Mermaid 流程图):

graph LR subgraph 客户端请求 A[客户端] --> B[Leader] end subgraph Leader 处理 B --> C[将请求追加到本地日志] B --> D[通过心跳将日志同步给 Follower] end subgraph Follower 处理 D --> E[Follower 接收日志并记录] E --> F[向 Leader 发送 ACK] end subgraph Leader 提交 B -- 收到多数 Follower ACK --> G[将日志设置为已提交] G --> H[通知客户端] B -- 下次心跳 --> I[通知 Follower 提交日志] end
Raft 协议失效处理:
- Leader 失效: Follower 在超时时间内未收到 Leader 心跳,会发起选举。
- Follower 失效: 重新加入集群后,从 Leader 复制日志。
- 多个 Candidate: 随机等待一段时间后再次发起投票。
WAL 日志:
etcd 使用预写式日志(WAL)来记录所有的数据变更。WAL 日志是二进制格式的,包含以下字段:
- type: 0 表示 Normal,1 表示 ConfChange(配置变更)。
- term: 主节点任期,每次主节点变更时递增。
- index: 变更序号,严格有序递增。
- data: Raft 请求对象的 pb 结构。
etcd 的存储机制
etcd v3 的存储分为两部分:
- 内存索引(kvindex): 基于 Google 开源的 btree 实现,用于加速查询。
- 后端存储(backend): 目前使用 boltdb,一个单机的支持事务的 KV 存储。
etcd 在 boltdb 中存储的 key 是 reversion,value 是 etcd 自己的 key-value 组合,实现了多版本机制。
reversion: 由两部分组成:
- main rev: 每次事务进行加一。
- sub rev: 同一个事务中的每次操作加一。
存储机制示意图(Mermaid 流程图):

graph LR subgraph etcd v3 存储 A[客户端请求] --> B[Leader] B --> C["一致性模块 (Raft)"] C --> D["预检查 (鉴权, 包大小等)"] C --> E["配额, 限速, 选主, 日志复制"] E --> F["写 WAL 日志"] F --> G["写 raftLog (内存)"] G --> H["收到半数确认, 更新 MatchIndex, Apply"] H --> I["MVCC 模块"] I --> J["treeIndex (内存索引)"] I --> K["BoltDB (后端存储)"] end
etcd 的 Watch 机制
etcd v3 的 Watch 机制支持监听特定 key 或范围(模拟目录结构)。每个 WatchableStore 包含两种 watcherGroup:
- synced: watcher 数据已同步完毕,等待新的变更。
- unsynced: watcher 数据同步落后于当前最新变更,还在追赶。
etcd 会启动一个后台 goroutine 持续同步 unsynced 的 watcher,然后将其迁移到 synced 组。

etcd 的高可用方案
为了确保 etcd 集群的高可用性,通常采用以下方案:
- 多节点部署: 部署 3 个或 5 个 etcd 节点,形成集群。
- 故障转移: 当 Leader 节点故障时,其他节点会通过 Raft 协议选举出新的 Leader。
- 数据备份: 定期创建快照,并将快照上传到网络存储设备,以防数据丢失。
高可用 etcd 解决方案:
- etcd-operator: CoreOS 开源的基于 Kubernetes CRD 的 etcd 集群配置工具(已归档)。

- Etcd statefulset Helm chart: Bitnami 提供的 Helm chart,用于在 Kubernetes 上部署高可用 etcd 集群。
使用 Bitnami Helm chart 安装 etcd 高可用集群(示例):
1 |
|
etcd 在 Kubernetes 中的应用
etcd 是 Kubernetes 的后端存储,用于存储集群的所有状态信息。
- 对于每一个 Kubernetes Object,都有对应的
storage.go
文件负责对象的存储操作(例如pkg/registry/core/pod/storage/storage.go
)。 - API Server 启动脚本中会指定 etcd servers 集群的地址。
- 早期 API server 对 etcd 做简单的 Ping check,现在已经改为真实的 etcd api call。
Kubernetes 对象在 etcd 中的存储路径示例:
1 |
|
Kubernetes 集群中 etcd 的部署拓扑:
堆叠式(Stacked): 控制平面和 etcd 成员位于同一节点。
- 优点:易于建立和管理。
- 缺点:存在耦合失败的风险。
外部(External): 控制平面和 etcd 成员解耦。
- 优点:降低耦合失败的风险。
- 缺点:需要更多的主机。


最佳实践和优化
- 集群规模: 建议使用 3 或 5 个 etcd 节点,根据集群规模进行调整。
- 网络延迟: 尽量将 etcd 集群部署在同一地域,减少网络延迟。
- 磁盘 I/O: 使用 SSD,并将 etcd 数据存放在单独的磁盘。
- 日志文件大小: 定期创建快照,并设置合理的存储配额。
- 历史版本压缩: 自动压缩历史版本,释放存储空间。
- 碎片整理: 定期消除存储碎片。
- 数据备份: 定期创建快照,并上传到网络存储设备。

- 参数优化: 根据网络延迟调整心跳周期和选举超时时间。
- 安全性: 启用 peer 和 client 之间的 TLS 加密,并考虑数据加密。
- 事件分离: 对于大规模集群,可以将事件存储在单独的 etcd 集群中。
etcd 常见问题与解决方案
- 频繁的leader election: 检查网络,查看是否有丢包现象
- etcd 分裂:检查集群配置。
- etcd 不响应: 检查磁盘,网络。
- 与apiserver之间的链路阻塞: 检查客户端并发。
- 磁盘暴涨: 检查是否有大量数据写入。
- 少数etcd 成员Down: 检查 down 掉的 etcd 日志。