你有没有想过,当 Kubernetes 集群管理着成千上万个 Pod 时,这些配置数据是怎么在多个 Master 节点间保持一致的?答案就是 etcd——一个用 Go 写的分布式键值存储,它是云原生基础设施的隐形支柱。
它到底解决什么问题
分布式系统里,最头疼的不是存数据,而是怎么让多个节点对同一份数据达成共识。单机数据库跑得好好的,一到分布式环境就麻烦了:
- 配置怎么同步? 微服务配置改了,怎么通知到所有节点
- 谁当老大? 多个实例怎么选出唯一的 Leader
- 数据会不会丢? 机器挂了数据还在不在
etcd 用 Raft 算法把这些问题都解决了,所以 Kubernetes、CoreDNS 这些项目都在用它。
核心技术是怎么实现的
Raft 算法:比 Paxos 好懂多了
Raft 把共识问题拆成了三步:
选 Leader → 同步日志 → 保证安全
大概流程是这样:
- 客户端把请求发给 Leader
- Leader 先写自己的日志
- 然后同时发给其他 Follower 节点
- 超过半数回复 OK 就算成功
这个设计挺实在的,3 个节点能挂 1 个,5 个节点能挂 2 个,集群照样能用。
MVCC 多版本控制
etcd 不会直接覆盖旧数据,每次改动都生成新版本。这样就有两个好处:
- 能查历史:可以看任意时间点的数据
- 能监听变化:订阅某个 Key,一改就通知你
存储架构长这样
gRPC API 层
↓
MVCC 层(内存索引)
↓
BoltDB(B+ 树存储)
↓
WAL 预写日志
WAL(Write-Ahead Log)是个保险机制,就算进程崩了,也能从日志里把数据捞回来。
实际能用来干啥
场景一:服务注册发现
// 服务启动时注册
lease := cli.Grant(ctx, 10) // 10 秒租约
cli.Put(ctx, "/services/user-api/node1",
"192.168.1.100:8080",
clientv3.WithLease(lease.ID))
// 定期续约保持心跳
cli.KeepAlive(ctx, lease.ID)
服务挂了租约就过期,注册信息自动删掉,不用手动清理。
场景二:配置中心
etcd 的 Watch 机制能做到配置秒级更新:
// 监听配置变化
watchChan := cli.Watch(ctx, "/config/", clientv3.WithPrefix())
for resp := range watchChan {
// 收到通知就重新加载
reloadConfig()
}
比定时轮询省事多了,延迟低还不浪费资源。云栈社区( https://yunpan.plus )的几个项目就是这么用的,线上跑得挺稳。
场景三:分布式锁
session, _ := concurrency.NewSession(cli)
mutex := concurrency.NewMutex(session, "/lock/order")
mutex.Lock(ctx)
// 处理订单
processOrder()
mutex.Unlock(ctx)
最妙的是,拿锁的进程挂了,Session 过期锁会自动释放,不会死锁。
生产环境怎么用
集群怎么搭
- 节点数:3/5/7 个奇数节点(偶数没意义,容错能力一样)
- 硬件要求:必须 SSD,WAL 写入对延迟敏感;网络延迟越低越好
- 数据量:单个 Key 别超 1.5MB,总数据控制在 8GB 以内
监控看这几个指标
etcd_server_has_leader # 有没有 Leader(0 就是出事了)
etcd_disk_wal_fsync_duration # WAL 写入延迟(超过 10ms 要注意)
etcd_network_peer_sent_failures # 节点间通信失败次数
性能优化经验
- 批量写:用 Txn 事务把多个操作合并
- 定期压缩:跑
etcdctl compact 清理历史版本
- 备份:每天自动快照存到对象存储
为啥选 etcd
| 对比项 |
etcd |
ZooKeeper |
Consul |
| 算法 |
Raft |
ZAB |
Raft |
| 语言 |
Go |
Java |
Go |
| HTTP API |
✅ |
❌ |
✅ |
| 上手难度 |
简单 |
一般 |
一般 |
| K8s 集成 |
原生 |
要适配 |
要适配 |
etcd 最大的优点是简单:一个二进制文件就能跑,API 也清晰,社区还活跃。如果你在学 Go 语言或者云原生,看 etcd 源码能学到不少东西。
快速试一下
单节点启动:
etcd --listen-client-urls http://0.0.0.0:2379 \
--advertise-client-urls http://localhost:2379
基本操作:
# 写数据
etcdctl put /app/config '{"db":"mysql"}'
# 读数据
etcdctl get /app/config
# 监听变化
etcdctl watch /app/config
用 Docker 跑:
docker run -d -p 2379:2379 \
quay.io/coreos/etcd:latest \
etcd --listen-client-urls http://0.0.0.0:2379
最后说两句
etcd 不是万能数据库,它就干一件事:存关键的少量数据(配置、元数据、协调信息)。如果你在搞微服务或者容器编排,etcd 是个绕不过去的后端架构组件。
云栈社区后面会继续分享 etcd 在生产环境的实战案例,有兴趣可以持续关注。
🔔 关注《云栈后端架构》,获取更多分布式系统干货
📌 项目地址:https://github.com/etcd-io/etcd
📖 官方文档:https://etcd.io/docs
📖 Golang 教程:https://yunpan.plus/f/27
标签:#etcd #Github #分布式系统 #Raft算法 #Kubernetes #云原生 #Go语言 #配置中心