文档
前言
如果你用过 Redis,你可能会问:"有了 Redis 为什么还要 etcd?"答案在于一个词:一致性。Redis 是 AP 系统(最终一致),而 etcd 是 CP 系统(强一致)。当你的分布式锁、选主、配置变更必须是"绝对正确"的时候,etcd 是答案。
第一章:理解 Raft
1.1 为什么需要共识算法
问题:3 个节点存储同一数据,如何确保所有节点返回相同值?
方案1:主从复制(MySQL)
→ 主写入,从复制。主挂 → 可能丢数据
方案2:Raft
→ 多数派(quorum)写入:3 节点中 2 个确认即成功
→ 保证:任何时刻,多数派中至少有一个节点有最新数据
1.2 Raft 三阶段
阶段1:Leader 选举
所有节点 → Candidate → 获得多数票 → Leader
心跳间隔:T(通常 100-500ms)
阶段2:日志复制
Client → Leader → 写入日志 → 复制到 Follower → 多数确认 → 提交
阶段3:安全
新 Leader 一定包含所有已提交的日志
利用 Term(任期号)和 Index(日志索引)保证
1.3 Quorum 数学
N 节点集群需要 (N/2)+1 节点确认:
N=3 → 需 2 确认 → 容忍 1 节点故障
N=5 → 需 3 确认 → 容忍 2 节点故障
N=7 → 需 4 确认 → 容忍 3 节点故障
第二章:etcd 核心 API
2.1 KV 操作
etcdctl put /config/app/timeout "30s"
etcdctl get /config/app/timeout
etcdctl del /config/app/timeout
etcdctl get /config/ --prefix # 前缀查询
2.2 Watch(长连接推送)
# 终端1:监听变化
etcdctl watch /config/ --prefix
# 终端2:修改值
etcdctl put /config/app/timeout "60s"
# 终端1 实时输出:
# PUT /config/app/timeout
# 60s
2.3 Lease(租约)
# 创建 30 秒租约
etcdctl lease grant 30
# lease 694d8f7a3b2e1c5d granted with TTL(30s)
# 用租约写 key(到期自动删除)
etcdctl put /services/app/instance1 "192.168.1.1:8080" --lease=694d8f7a3b2e1c5d
# 续约
etcdctl lease keep-alive 694d8f7a3b2e1c5d
2.4 事务
# 原子 CAS:如果 /lock 不存在则写入
etcdctl txn <<EOF
compares:
create("/lock/my-resource") = "0"
success requests (get, put, del):
put /lock/my-resource "holder-abc"
failure requests (get, put, del):
get /lock/my-resource
EOF
第三章:etcd vs Redis vs ZooKeeper vs Consul
| 特性 | etcd | Redis | ZooKeeper | Consul |
|---|---|---|---|---|
| CAP | CP | AP | CP | CP |
| 共识算法 | Raft | 无(主从) | ZAB | Raft |
| 配置存储 | ✅ KV | ✅ KV | ✅ 树形 | ✅ KV |
| Watch | ✅ | ✅ Pub/Sub | ✅ | ✅ |
| Lease/TTL | ✅ | ✅ EXPIRE | ✅ Ephemeral | ✅ |
| 事务 | ✅ | ✅ MULTI/EXEC | ✅ Multi | ✅ |
| K8s 原生 | ✅(核心依赖) | ❌ | ❌ | ❌ |
| 多数据中心 | ❌ | ❌ | ❌ | ✅ |
| 侧重点 | 一致性 + 简洁 | 性能 + 丰富数据结构 | 配置 + 选举 | 服务发现 + Mesh |
第四章:生产运维要点
4.1 数据压缩与碎片整理
# 查看当前 revision
etcdctl endpoint status --write-out=table
# 压缩旧版本(保留最近 1000 个)
rev=$(etcdctl endpoint status --write-out=json | jq '.[0].Status.header.revision')
etcdctl compact $(($rev - 1000))
# 碎片整理
etcdctl defrag --cluster
4.2 备份与恢复
# 备份
etcdctl snapshot save backup.db
# 恢复
etcdctl snapshot restore backup.db \
--data-dir=/var/lib/etcd-new \
--name=infra0 \
--initial-cluster=infra0=http://10.0.0.1:2380 \
--initial-advertise-peer-urls=http://10.0.0.1:2380
4.3 安全加固
# 启用认证
etcdctl user add root
etcdctl auth enable
# 创建角色和用户
etcdctl role add app-role
etcdctl role grant-permission app-role readwrite /config/app/ --prefix
etcdctl user add app-user
etcdctl user grant-role app-user app-role
思考题
- 为什么 etcd 推荐奇数节点(3/5/7)?4 节点和 3 节点的容错能力一样吗?
- Raft 的 Leader 选举中如果出现"脑裂"(两个 Leader),etcd 怎么处理?客户端怎么知道谁是真正的 Leader?
- etcd 的 MVCC 机制和数据库的 MVCC 有什么异同?为什么要保存历史版本?
- Kubernetes 中所有资源都存储在 etcd 中,etcd 挂了对 K8s 集群有什么影响?已有 Pod 还能继续运行吗?
下一步
- 学习 etcd + gRPC 实现命名解析(Name Resolver)
- 学习 Kubernetes CRD + etcd 原理
- 学习 etcd 性能调优:磁盘/网络/配置参数