etcd 深度解析:Kubernetes 的分布式大脑
1. etcd 是什么
etcd 是一个强一致性、高可用的分布式键值存储,是 Kubernetes 集群的唯一持久化存储后端。集群的所有状态数据——Pod、Service、ConfigMap、Secret、节点信息等——全部存储在 etcd 中。
etcd 的名字来源于 Unix /etc 目录(存储配置)+ d(distributed,分布式)。
2. 核心特性
| 特性 |
说明 |
| 强一致性 |
基于 Raft 算法,线性一致读写 |
| 高可用 |
奇数节点集群,容忍 (n-1)/2 节点故障 |
| Watch 机制 |
实时监听 key 变化,驱动控制器 |
| MVCC |
多版本并发控制,支持历史版本查询 |
| Lease |
TTL 租约机制,用于节点心跳 |
| 事务 |
原子性 CAS 操作 |
3. Raft 共识算法
etcd 使用 Raft 算法保证分布式一致性。
3.1 节点角色
1
| Leader(领导者) ←→ Follower(跟随者) ←→ Candidate(候选者)
|
- Leader:处理所有写请求,定期发送心跳
- Follower:被动接收日志,响应 Leader 心跳
- Candidate:选举期间的临时状态
3.2 Leader 选举流程
1 2 3 4
| 1. Follower 超时未收到心跳 → 转为 Candidate 2. Candidate 递增 Term,向所有节点发送 RequestVote RPC 3. 获得多数票(>n/2)→ 成为 Leader 4. Leader 开始发送心跳,其他节点回退为 Follower
|
3.3 日志复制流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Client Write │ ▼ Leader 追加日志条目(uncommitted) │ ▼ 并行发送 AppendEntries RPC 给所有 Follower │ ▼ 多数节点确认 → Leader commit 日志 │ ▼ 通知 Follower commit → 应用到状态机 │ ▼ 返回客户端成功
|
3.4 脑裂保护
Raft 通过 Term(任期) 机制防止脑裂:
- 每次选举 Term 递增
- 节点只接受 Term ≥ 自身 Term 的消息
- 旧 Leader 收到更高 Term 消息后自动降级
4. MVCC 数据模型
etcd 使用 MVCC(Multi-Version Concurrency Control) 存储数据。
4.1 存储结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ┌─────────────────────────────────────────────┐ │ BoltDB(底层存储引擎) │ │ │ │ keyIndex(内存索引) │ │ ┌─────────────────────────────────────┐ │ │ │ key → [{revision: 1, ...}, │ │ │ │ {revision: 5, ...}] │ │ │ └─────────────────────────────────────┘ │ │ │ │ backend(磁盘存储) │ │ ┌─────────────────────────────────────┐ │ │ │ (revision) → (key, value, lease) │ │ │ └─────────────────────────────────────┘ │ └─────────────────────────────────────────────┘
|
4.2 Revision(版本号)
每次写操作都会生成一个全局递增的 revision:
1 2 3 4 5
| etcdctl endpoint status --write-out=json | jq '.[0].Status.header.revision'
etcdctl get /registry/pods/default/nginx --rev=100
|
4.3 Compaction(压缩)
随着时间推移,历史版本会占用大量空间,需要定期压缩:
1 2 3 4 5
| etcdctl compact 1000
etcdctl defrag
|
5. Watch 机制
Watch 是 Kubernetes 控制器模式的基础。
5.1 工作原理
1 2 3 4 5 6 7 8 9
| Client etcd │ │ │── Watch(key, rev=100) ──▶│ │ │ 监听 rev > 100 的变化 │ │ │ key 发生变化(rev=105) │ │◀── WatchEvent ──────────│ │ │ │ 继续监听... │
|
5.2 Watch 的高效实现
etcd 维护一个 watcherGroup,按 key 和 key 前缀分组:
1 2 3 4 5 6 7 8
| func (s *store) Watch(key string, startRev int64) Watcher { w := newWatcher(key, startRev) s.watcherGroup.add(w) s.syncWatchersLoop(w) return w }
|
5.3 Kubernetes 中的 Watch
1 2 3 4 5 6
| kube-apiserver ──Watch──▶ etcd │ │ 缓存到 Watch Cache(内存) │ ▼ 各控制器 ──ListWatch──▶ apiserver(不直接访问 etcd)
|
6. Lease(租约)机制
Lease 用于实现 TTL 和节点心跳:
1 2 3 4 5 6 7 8
| LEASE_ID=$(etcdctl lease grant 5 | awk '{print $2}')
etcdctl put /nodes/node1 "alive" --lease=$LEASE_ID
etcdctl lease keep-alive $LEASE_ID
|
Kubernetes 中 kubelet 通过 Lease 对象向 apiserver 汇报心跳,替代了早期的 Node Status 更新方式,大幅降低了 etcd 写压力。
7. Kubernetes 中的 etcd 数据结构
1 2 3 4 5 6 7 8 9 10 11 12
| etcdctl get / --prefix --keys-only
/registry/pods/{namespace}/{name} /registry/services/{namespace}/{name} /registry/deployments/{namespace}/{name} /registry/nodes/{name} /registry/secrets/{namespace}/{name} /registry/configmaps/{namespace}/{name} /registry/events/{namespace}/{name} /registry/leases/{namespace}/{name}
|
8. 生产运维最佳实践
8.1 集群规模
| 集群规模 |
etcd 节点数 |
说明 |
| 开发/测试 |
1 |
无高可用 |
| 生产小规模 |
3 |
容忍 1 节点故障 |
| 生产大规模 |
5 |
容忍 2 节点故障 |
不推荐 7 节点以上,写性能会显著下降。
8.2 硬件要求
1 2 3 4 5
| CPU: 4 核以上(etcd 是 CPU 密集型) 内存: 8GB 以上 磁盘: SSD,IOPS > 3000(etcd 对磁盘延迟极敏感) 网络: 低延迟,节点间 RTT < 10ms
|
8.3 关键参数调优
1 2 3 4 5 6 7
| --heartbeat-interval=100 --election-timeout=1000 --quota-backend-bytes=8589934592 --auto-compaction-mode=periodic --auto-compaction-retention=1h --max-request-bytes=10485760
|
8.4 备份与恢复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key
etcdctl snapshot status /backup/etcd-20260101.db --write-out=table
etcdctl snapshot restore /backup/etcd-20260101.db \ --data-dir=/var/lib/etcd-restore \ --name=etcd-1 \ --initial-cluster=etcd-1=https://10.0.0.1:2380 \ --initial-advertise-peer-urls=https://10.0.0.1:2380
|
8.5 监控指标
1 2 3 4 5 6 7
| # etcd 关键监控指标 etcd_server_leader_changes_seen_total # Leader 切换次数(应接近 0) etcd_disk_wal_fsync_duration_seconds # WAL fsync 延迟(应 < 10ms) etcd_disk_backend_commit_duration_seconds # 后端提交延迟(应 < 25ms) etcd_network_peer_round_trip_time_seconds # 节点间 RTT etcd_mvcc_db_total_size_in_bytes # 数据库大小 etcd_server_proposals_failed_total # 提案失败次数
|
9. 常见问题排查
9.1 etcd 空间不足
1 2 3 4
|
etcdctl compact $(etcdctl endpoint status --write-out="json" | jq '.[0].Status.header.revision') etcdctl defrag --endpoints=https://127.0.0.1:2379
|
9.2 Leader 频繁切换
1 2 3 4 5 6 7 8
| iostat -x 1 10
ping <etcd-peer-ip>
--election-timeout=5000
|
9.3 集群无法选出 Leader
1 2 3 4 5 6
|
etcd --force-new-cluster
|
10. 总结
etcd 是 Kubernetes 的”大脑存储”,其稳定性直接决定集群的可用性。核心要点:
- Raft 保证一致性:所有写操作经过 Leader,多数派确认后提交
- MVCC 支持 Watch:版本号机制让控制器能高效感知变化
- Lease 实现心跳:轻量级的节点存活检测
- 生产必须 3/5 节点:保证高可用和容错
- SSD 是必须的:磁盘延迟直接影响 Raft 性能